add Tournament creation and tournament game start

This commit is contained in:
2024-10-13 22:03:06 +02:00
parent d6fdd6fbc7
commit 8f993caefb
10 changed files with 205 additions and 71 deletions

View File

@ -6,7 +6,7 @@
# By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ # # By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/10/05 03:54:20 by tomoron #+# #+# # # Created: 2024/10/05 03:54:20 by tomoron #+# #+# #
# Updated: 2024/10/10 19:25:56 by tomoron ### ########.fr # # Updated: 2024/10/13 21:24:19 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
@ -19,9 +19,10 @@ import asyncio
import time import time
class Bot(Player): class Bot(Player):
def __init__(self, game): def __init__(self, game=None, tournament=None):
self.socket = DummySocket(game) self.socket = DummySocket(game)
self.game = game self.game = game
self.tournament = tournament
self.ready = True self.ready = True
self.pos = {"pos":0, "up": False} self.pos = {"pos":0, "up": False}
self.lastCalculated = {"pos":0, "up":False} self.lastCalculated = {"pos":0, "up":False}
@ -46,7 +47,7 @@ class Bot(Player):
return(self.objective) return(self.objective)
self.lastCalculated = {"pos":pos, "up" : up} self.lastCalculated = {"pos":pos, "up" : up}
if(not center): if(not center):
offset = random.randint(-100, 100) / 100 offset = random.randint(-99, 99) / 100
if(offset == 0): if(offset == 0):
offset = 0.1 offset = 0.1
pos += offset * (GameSettings.playerLength / 2) pos += offset * (GameSettings.playerLength / 2)
@ -65,24 +66,37 @@ class Bot(Player):
self.objective = self.genRandomBallDirection(0, 0, True) self.objective = self.genRandomBallDirection(0, 0, True)
return return
self.objective = self.genRandomBallDirection(tempBall.pos[0], tempBall.up, False) self.objective = self.genRandomBallDirection(tempBall.pos[0], tempBall.up, False)
def isEnd(self):
if(self.tournament != None):
return(self.tournament.end)
else:
return(self.game.end)
async def updateLoop(self): async def updateLoop(self):
while not self.game.end: while not self.isEnd():
await self.getExpectedPos() if(self.game):
await self.getExpectedPos()
await asyncio.sleep(1) await asyncio.sleep(1)
async def goToObjectiveLoop(self): async def goToObjectiveLoop(self):
lastUpdate = time.time() lastUpdate = time.time()
while not self.game.end: while not self.isEnd():
if(self.pos["pos"] != self.objective["pos"] or self.pos["up"] != self.objective["up"]): if(self.pos["pos"] != self.objective["pos"] or self.pos["up"] != self.objective["up"]):
self.pos["up"] = self.objective["up"] self.pos["up"] = self.objective["up"]
maxDistance = GameSettings.maxPlayerSpeed * (time.time() - lastUpdate) maxDistance = GameSettings.maxPlayerSpeed * (time.time() - lastUpdate)
lastUpdate = time.time() print("maxDistance :", maxDistance)
travel = self.objective["pos"] - self.pos["pos"] travel = self.objective["pos"] - self.pos["pos"]
if(travel >= 0): if(travel >= 0):
travel = min(self.objective["pos"] - self.pos["pos"], GameSettings.maxPlayerSpeed) travel = min(self.objective["pos"] - self.pos["pos"], maxDistance)
else: else:
travel = max(self.objective["pos"] - self.pos["pos"], -GameSettings.maxPlayerSpeed) travel = max(self.objective["pos"] - self.pos["pos"], -maxDistance)
print("travel :", travel)
self.game.move(self.socket, self.pos["pos"] + travel, self.pos["up"]) self.game.move(self.socket, self.pos["pos"] + travel, self.pos["up"])
lastUpdate = time.time()
await asyncio.sleep(1 / 20) await asyncio.sleep(1 / 20)
def setGame(self, game):
self.game = game
self.socket.game = game

View File

@ -6,7 +6,7 @@
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/09/13 16:20:58 by tomoron #+# #+# # # Created: 2024/09/13 16:20:58 by tomoron #+# #+# #
# Updated: 2024/10/10 19:24:02 by tomoron ### ########.fr # # Updated: 2024/10/13 21:50:58 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
@ -16,31 +16,36 @@ from .models import GameResults, User
from .GameSettings import GameSettings from .GameSettings import GameSettings
from .Bot import Bot from .Bot import Bot
from .Ball import Ball from .Ball import Ball
from typing import Union
import time import time
import json import json
import asyncio import asyncio
import random import random
import math import math
from multimethod import multimethod
class Game: class Game:
waitingForPlayer = None waitingForPlayer = None
def __init__(self, socket, withBot, skinId = 0, goalId = 0, opponent = None): @multimethod
self.p1 = None def __init__(self, p1 , p2 , isTournament = False):
self.p2 = None self.initAttributes()
self.started = False if(isinstance(p1, Bot) and isinstance(p2, Bot)):
self.end = False self.winner=1
self.left = None self.pWinner=p1
self.winner = None return
self.gameStart = 0 elif(isinstance(p2,Bot)):
self.gameTime = 0 p2,p1 = p1, p2
self.withBot = withBot self.withBot = isinstance(p1,Bot)
self.isTournament = isTournament
p1.setGame(self)
p2.setGame(self)
print("game created with ", p1.socket.username, "vs", p2.socket.username)
self.ball = Ball() @multimethod
self.speed = GameSettings.startSpeed def __init__(self, socket, withBot : bool, skinId = 0, goalId = 0 , opponent = None):
self.score = [0, 0] self.initAttributes()
self.obstacles = [] self.withBot = withBot
self.lastWin = 2
self.opponentLock = opponent self.opponentLock = opponent
if(1 or withBot): if(1 or withBot):
@ -67,6 +72,24 @@ class Game:
def __del__(self): def __del__(self):
print("game destroy") print("game destroy")
def initAttributes(self):
self.p1 = None
self.p2 = None
self.isTournament=False
self.started = False
self.end = False
self.left = None
self.winner = None
self.pWinner = None
self.gameStart = 0
self.gameTime = 0
self.ball = Ball()
self.speed = GameSettings.startSpeed
self.score = [0, 0]
self.obstacles = []
self.lastWin = 2
def obstaclesInvLength(self): def obstaclesInvLength(self):
for x in self.obstacles: for x in self.obstacles:
x["pos"]["z"] = -x["pos"]["z"] x["pos"]["z"] = -x["pos"]["z"]
@ -100,7 +123,8 @@ class Game:
try: try:
if(self.p1 == None): if(self.p1 == None):
print("game created, set as player 1") print("game created, set as player 1")
self.p1 = Player(socket, self) self.p1 = Player(socket)
self.p1.setGame(self)
self.p1.skin = skin self.p1.skin = skin
self.p1.goal = goal self.p1.goal = goal
else: else:
@ -108,7 +132,8 @@ class Game:
socket.sendError("You are not invited to this game", 9103) socket.sendError("You are not invited to this game", 9103)
return return
print("joined game, set as player 2") print("joined game, set as player 2")
self.p2 = Player(socket, self) self.p2 = Player(socket)
self.p2.setGame(self)
self.p2.skin = skin self.p2.skin = skin
self.p2.goal = goal self.p2.goal = goal
if(self.p2 != None and self.p1 != None): if(self.p2 != None and self.p1 != None):
@ -138,14 +163,16 @@ class Game:
self.p1.socket.sync_send({"type":"game","content":{"action":10,"won":winner==1, "opponentLeft":self.left == 2}}) self.p1.socket.sync_send({"type":"game","content":{"action":10,"won":winner==1, "opponentLeft":self.left == 2}})
self.p2.socket.sync_send({"type":"game","content":{"action":10,"won":winner==2, "opponentLeft":self.left == 1}}) self.p2.socket.sync_send({"type":"game","content":{"action":10,"won":winner==2, "opponentLeft":self.left == 1}})
self.winner = winner self.winner = winner
self.pWinner = self.p1 if winner == 1 else self.p2
self.end = True self.end = True
def leave(self, socket): def leave(self, socket):
socket.game = None
if (socket == self.p1.socket): if (socket == self.p1.socket):
self.left = 1 self.left = 1
self.p1.setGame(None)
else: else:
self.left = 2 self.left = 2
self.p2.setGame(None)
if(Game.waitingForPlayer == self): if(Game.waitingForPlayer == self):
Game.waitingForPlayer = None Game.waitingForPlayer = None
if(self.p2 != None): if(self.p2 != None):
@ -238,9 +265,9 @@ class Game:
self.sendNewBallInfo() self.sendNewBallInfo()
print("game end") print("game end")
if(self.p1.socket.game == self): if(self.p1.socket.game == self):
self.p1.socket.game = None self.p1.setGame(None)
if(self.p2.socket.game == self): if(self.p2.socket.game == self):
self.p2.socket.game = None self.p2.setGame(None)
if(not self.withBot): if(not self.withBot):
await self.saveResults() await self.saveResults()
del self del self

View File

@ -6,7 +6,7 @@
# By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ # # By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/10/06 16:33:56 by tomoron #+# #+# # # Created: 2024/10/06 16:33:56 by tomoron #+# #+# #
# Updated: 2024/10/10 03:48:00 by tomoron ### ########.fr # # Updated: 2024/10/12 23:31:00 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
@ -38,20 +38,11 @@ class GameSettings:
{ "type":1, "name":"J6", "pos":{"x": 1.5, "y": 0.2, "z": -mapLength / 4}, "isUp": False }, { "type":1, "name":"J6", "pos":{"x": 1.5, "y": 0.2, "z": -mapLength / 4}, "isUp": False },
{ "type":1, "name":"J7", "pos":{"x": 1.5, "y": 3.2, "z": -mapLength / 4}, "isUp": True } { "type":1, "name":"J7", "pos":{"x": 1.5, "y": 3.2, "z": -mapLength / 4}, "isUp": True }
] ]
skins = [ nbSkins = 8
{id: 0, 'color': 0xff53aa, 'texture': None},
{id: 1, 'color': 0xaa24ea, 'texture': None},
{id: 2, 'color': 0x2c9c49, 'texture': None},
{id: 3, 'color': 0x101099, 'texture': None},
{id: 4, 'color': None, 'texture': '/static/img/skin/1.jpg'},
{id: 5, 'color': None, 'texture': '/static/img/skin/2.jpg'},
{id: 6, 'color': None, 'texture': '/static/img/skin/3.jpg'},
{id: 7, 'color': None, 'texture': '/static/img/skin/4.jpg'},
]
nbGoals = 4 nbGoals = 4
wallLength = 1 wallLength = 1
wallWidth = 0.05 wallWidth = 0.05
bounceSpeedIncrease = 0.2 bounceSpeedIncrease = 0.2
maxScore = 5 maxScore = 5
maxPlayerSpeed = 0.4 maxPlayerSpeed = 6

View File

@ -6,14 +6,13 @@
# By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ # # By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/10/05 03:22:32 by tomoron #+# #+# # # Created: 2024/10/05 03:22:32 by tomoron #+# #+# #
# Updated: 2024/10/10 03:52:40 by tomoron ### ########.fr # # Updated: 2024/10/13 21:24:16 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
class Player(): class Player():
def __init__(self, socket, game): def __init__(self, socket):
self.socket = socket self.socket = socket
socket.game = game
self.ready = False self.ready = False
self.pos = {"pos":0, "up": False} self.pos = {"pos":0, "up": False}
self.skin = 0 self.skin = 0
@ -21,3 +20,7 @@ class Player():
def __del__(self): def __del__(self):
print("player destroy") print("player destroy")
def setGame(self, game):
self.game = game
self.socket.game = game

View File

@ -6,28 +6,29 @@
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/10/04 17:17:07 by tomoron #+# #+# # # Created: 2024/10/04 17:17:07 by tomoron #+# #+# #
# Updated: 2024/10/10 06:12:28 by tomoron ### ########.fr # # Updated: 2024/10/13 21:11:56 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
import string import string
import asyncio import asyncio
from .Bot import Bot
from .Player import Player
from .utils import genString from .utils import genString
from .TournamentGame import TournamentGame
class Tournament: class Tournament:
currentTournamentsLock = False
currentTournaments = {} currentTournaments = {}
playerLimit = 8 playerLimit = 8
def __init__(self, socket): levels = 3
def __init__(self, socket, nbBot):
self.messages = [] self.messages = []
self.players = [] self.players = []
while(Tournament.currentTournamentsLock): self.nbBot = nbBot
continue; self.end = False
Tournament.currentTournamentsLock = True
self.genCode() self.genCode()
Tournament.currentTournaments[self.code] = self Tournament.currentTournaments[self.code] = self
Tournament.currentTournamentsLock = False
self.join(socket) self.join(socket)
def genCode(self): def genCode(self):
@ -43,12 +44,12 @@ class Tournament:
def broadcast(self, content): def broadcast(self, content):
for x in self.players: for x in self.players:
x.sync_send("tournament",content) x.socket.sync_send("tournament",content)
def sendAllInfo(self, socket): def sendAllInfo(self, socket):
players = [] players = []
for x in self.players: for x in self.players:
players.append({"id":x.id,"username":x.username, "pfp":x.pfp}) players.append({"id":x.socket.id,"username":x.socket.username, "pfp":x.socket.pfp})
socket.sync_send("tournament",{"action":5, "players":players, "messages" : self.messages}) socket.sync_send("tournament",{"action":5, "players":players, "messages" : self.messages})
def sendMessage(self, socket, message): def sendMessage(self, socket, message):
@ -57,22 +58,56 @@ class Tournament:
self.messages.pop(0) self.messages.pop(0)
self.broadcast({"action":3, "username":socket.username, "message":message}) self.broadcast({"action":3, "username":socket.username, "message":message})
def playerFromSocket(self, socket):
for x in range(len(self.players)):
if(self.players[x].socket == socket):
return(x)
return(-1)
def leave(self, socket): def leave(self, socket):
if(socket not in self.players): index = self.playerFromSocket(socket)
if(index == -1):
return; return;
index = self.players.index(socket)
self.players.pop(index) self.players.pop(index)
socket.tournament = None socket.tournament = None
self.broadcast({"action":2,"id":socket.id}) self.broadcast({"action":2,"id":socket.id})
def join(self, socket): def join(self, socket, isBot=False):
if(socket.tournament != None): if(not isBot and socket.tournament != None):
socket.sendError("already in a tournament", 9036) socket.sendError("already in a tournament", 9036)
return return
if(len(self.players) == Tournament.playerLimit): if(not isBot and len(self.players) == Tournament.playerLimit):
socket.sync_send("tournament",{"action":0, "isFull":True}) socket.sync_send("tournament",{"action":0, "isFull":True})
return return
if(isBot):
player = Bot(None, self)
socket = player.socket
else:
player = Player(socket)
socket.tournament = self socket.tournament = self
self.players.append(socket) self.players.append(player)
socket.sync_send("tournament",{"action":0,"isFull":False, "isStarted":False,"exist":True, "code":self.code}) socket.sync_send("tournament",{"action":0, "code":self.code})
self.broadcast({"action":1, "id":socket.id, "username": socket.username,"pfp":socket.pfp}) self.broadcast({"action":1, "id":socket.id, "username": socket.username,"pfp":socket.pfp})
if(len(self.players) == Tournament.playerLimit):
self.start()
if(len(self.players) == Tournament.playerLimit - self.nbBot):
for x in range(self.nbBot):
self.join(None, True)
def createGames(self, players, level=1):
left = None
right = None
if(level == Tournament.levels):
try:
right = players.pop(0)
left = players.pop(0)
except IndexError:
pass
else:
right = self.createGames(players, level + 1)
left = self.createGames(players, level + 1)
return(TournamentGame(left, right))
def start(self):
self.started = True
self.createGames(self.players.copy())

View File

@ -0,0 +1,58 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# TournamentGame.py :+: :+: :+: #
# +:+ +:+ +:+ #
# By: tomoron <marvin@42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/10/12 22:49:00 by tomoron #+# #+# #
# Updated: 2024/10/13 22:01:59 by tomoron ### ########.fr #
# #
# **************************************************************************** #
import asyncio
from .Game import Game
class TournamentGame:
def __init__(self, left, right):
self.game = None
self.winner = None
self.right = right
self.left = left
asyncio.create_task(self.loop())
def startGame(self):
l = None
r = None
if(isinstance(self.left,TournamentGame)):
self.game = Game(self.left.winner, self.right.winner, True)
l = self.left.winner.socket
r = self.right.winner.socket
else:
self.game = Game(self.left, self.right, True)
l = self.left.socket
r = self.right.socket
l.sync_send("tournament", {
"action":4,
"id": r.id,
"username":r.username
})
r.sync_send("tournament", {
"action":4,
"id": l.id,
"username": l.username
})
async def loop(self):
while self.winner == None:
if(self.game == None):
if(isinstance(self.left, TournamentGame)):
if(self.left.winner != None and self.right.winner != None):
await asyncio.sleep(3)
self.startGame()
else:
await asyncio.sleep(3)
self.startGame()
else:
if(self.game.winner != None):
self.winner = self.game.pWinner
await asyncio.sleep(1)

View File

@ -6,7 +6,7 @@
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/09/11 17:07:08 by tomoron #+# #+# # # Created: 2024/09/11 17:07:08 by tomoron #+# #+# #
# Updated: 2024/10/10 03:52:54 by tomoron ### ########.fr # # Updated: 2024/10/12 23:31:22 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
@ -22,7 +22,7 @@ async def start(socket, content):
socket.sendError("Your opponent isn't online",9032) socket.sendError("Your opponent isn't online",9032)
return; return;
skinId = content.get("skinId", 0) skinId = content.get("skinId", 0)
if(skinId < 0 or skinId >= len(GameSettings.skins)): if(skinId < 0 or skinId >= GameSettings.nbSkins):
socket.sendError("Skin id out of range", 9033) socket.sendError("Skin id out of range", 9033)
return; return;
goalId = content.get("goalId",0) goalId = content.get("goalId",0)

View File

@ -6,7 +6,7 @@
# By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ # # By: tomoron <tomoron@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/10/04 17:16:02 by tomoron #+# #+# # # Created: 2024/10/04 17:16:02 by tomoron #+# #+# #
# Updated: 2024/10/05 02:28:50 by tomoron ### ########.fr # # Updated: 2024/10/11 21:14:54 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
@ -19,4 +19,8 @@ async def tournamentStart(socket, content):
else: else:
socket.sync_send("tournament",{"action":0, "exist":False}) socket.sync_send("tournament",{"action":0, "exist":False})
else: else:
Tournament(socket) nbBot = content.get("nbBot", 0)
if(nbBot < 0 or nbBot > 7):
socket.sendError("invalid number of bots", 9040)
return;
Tournament(socket, nbBot)

View File

@ -6,7 +6,7 @@
# By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ # # By: edbernar <edbernar@student.42angouleme. +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2024/09/09 14:31:30 by tomoron #+# #+# # # Created: 2024/09/09 14:31:30 by tomoron #+# #+# #
# Updated: 2024/10/06 16:28:47 by tomoron ### ########.fr # # Updated: 2024/10/12 02:43:29 by tomoron ### ########.fr #
# # # #
# **************************************************************************** # # **************************************************************************** #
@ -137,9 +137,9 @@ class WebsocketHandler(AsyncWebsocketConsumer):
uid = await self.session_get("id", 0) uid = await self.session_get("id", 0)
if(uid in self.onlinePlayers): if(uid in self.onlinePlayers):
del self.onlinePlayers[uid] del self.onlinePlayers[uid]
if(self.game !=None): if(self.game != None):
self.game.leave(self) self.game.leave(self)
if(self.tournament !=None): if(self.tournament != None):
self.tournament.leave(self) self.tournament.leave(self)
async def receive(self, text_data): async def receive(self, text_data):
@ -148,7 +148,8 @@ class WebsocketHandler(AsyncWebsocketConsumer):
except json.JSONDecodeError: except json.JSONDecodeError:
self.sendError("Invalid JSON", 9002) self.sendError("Invalid JSON", 9002)
return return
try: #try:
if(1):
self.printDebug(jsonRequest, 0) self.printDebug(jsonRequest, 0)
if (jsonRequest["type"] in typeRequest): if (jsonRequest["type"] in typeRequest):
if (jsonRequest["type"] == "login" or jsonRequest["type"] == "create_account"): if (jsonRequest["type"] == "login" or jsonRequest["type"] == "create_account"):
@ -159,8 +160,8 @@ class WebsocketHandler(AsyncWebsocketConsumer):
await functionRequest[typeRequest.index(jsonRequest["type"])](self, jsonRequest["content"]) await functionRequest[typeRequest.index(jsonRequest["type"])](self, jsonRequest["content"])
else: else:
self.sendError("Invalid type", 9004) self.sendError("Invalid type", 9004)
except Exception as e: # except Exception as e:
self.sendError("Invalid request", 9005, e) # self.sendError("Invalid request", 9005, e)
@multimethod @multimethod
def sync_send(self, reqType : str, content:dict): def sync_send(self, reqType : str, content:dict):

View File

@ -41,6 +41,7 @@
- 9037 : you're not in a tournament - 9037 : you're not in a tournament
- 9038 : missing message field - 9038 : missing message field
- 9039 : Goal id out of range - 9039 : Goal id out of range
- 9040 : invalid number of bots
- 9100 : Action out of range - 9100 : Action out of range
- 9101 : No game started - 9101 : No game started