ft_transcendence/pong/game/tournament.py

159 lines
6.0 KiB
Python

# /pong/game/tournament.py
import json
import asyncio
from django.template.loader import render_to_string
import random
from .matchmaking import match_maker
from .game import Game
class TournamentMatch(Game):
def __init__(self, game_id, player1, player2, tournament):
# Initialize the parent Game class with the provided parameters
super().__init__(game_id, player1, player2, False)
# Store the current game instance in active games
match_maker.active_games[game_id] = self
# Set the game for the players
player1.set_game(self)
if player2:
player2.set_game(self)
# Store the tournament instance
self.tournament = tournament
async def end_game(self, disconnected_player=None):
# Call the parent class's end_game method
await super().end_game(disconnected_player)
# Handle the end of the match in the tournament context
await self.tournament.handle_match_end(self)
class TournamentMatchMaker:
def __init__(self):
self.waiting_players = []
self.matches = []
self.rounds = []
self.current_round = 0
self.tournament_state = "waiting" # Can be "waiting", "in_progress", or "ended"
async def add_player(self, player):
if self.tournament_state == "waiting" and player not in self.waiting_players:
self.waiting_players.append(player)
print(f"User {player.user.username} joins the TOURNAMENT WAITING ROOM")
await self.update_waiting_room()
async def remove_player(self, player):
if player in self.waiting_players:
self.waiting_players.remove(player)
await self.update_waiting_room()
async def update_waiting_room(self):
html = self.generate_waiting_room_html()
for player in self.waiting_players:
await self.send_to_player(player, {
'type': 'update_tournament_waiting_room',
'html': html
})
def generate_waiting_room_html(self):
context = {
'players': [player.user.username for player in self.waiting_players],
'tournament_state': self.tournament_state,
'players_count': len(self.waiting_players),
'min_players_to_start': 2 # You can adjust this number as needed
}
return render_to_string('pong/tournament_waiting_room.html', context)
async def start_tournament(self):
if len(self.waiting_players) < 2:
return False
self.tournament_state = "in_progress"
random.shuffle(self.waiting_players)
self.current_round = 1
self.create_matches(self.waiting_players)
await self.update_brackets()
await self.start_round_matches()
return True
def create_matches(self, players):
matches = []
for i in range(0, len(players), 2):
if i + 1 < len(players):
matches.append(TournamentMatch(len(self.matches) + 1, players[i], players[i + 1], self))
else:
matches.append(TournamentMatch(len(self.matches) + 1, players[i], None, self)) # Bye
self.rounds.append(matches)
self.matches.extend(matches)
async def update_brackets(self):
html = self.generate_bracket_html()
for player in self.waiting_players:
await self.send_to_player(player, {
'type': 'update_brackets',
'html': html
})
def generate_bracket_html(self):
context = {
'tournament_rounds': self.get_tournament_data()
}
return render_to_string('pong/tournament_brackets.html', context)
def get_tournament_data(self):
return [
[
{
'player1': match.player1.user.username if match.player1 else 'BYE',
'player2': match.player2.user.username if match.player2 else 'BYE',
'winner': match.game_state['player1_name'] if match.game_state['player1_score'] > match.game_state['player2_score'] else match.game_state['player2_name'] if match.ended else None,
'score1': match.game_state['player1_score'],
'score2': match.game_state['player2_score']
}
for match in round_matches
]
for round_matches in self.rounds
]
async def start_round_matches(self):
for match in self.rounds[-1]:
if match.player1 and match.player2:
await match_maker.notify_players(match.player1, match.player2, match.game_id, False)
asyncio.create_task(match.start_game())
elif match.player1:
# Handle bye
match.game_state['player1_score'] = 3
match.game_state['player2_score'] = 0
await match.end_game()
async def handle_match_end(self, match):
await self.update_brackets()
if all(m.ended for m in self.rounds[-1]):
await self.advance_tournament()
async def advance_tournament(self):
winners = [match.player1 if match.game_state['player1_score'] > match.game_state['player2_score'] else match.player2 for match in self.rounds[-1] if match.player1 and match.player2]
if len(winners) > 1:
self.current_round += 1
self.create_matches(winners)
await self.update_brackets()
await self.start_round_matches()
else:
await self.end_tournament(winners[0])
async def end_tournament(self, winner):
self.tournament_state = "ended"
for player in self.waiting_players:
await self.send_to_player(player, {
'type': 'tournament_end',
'winner': winner.user.username
})
self.waiting_players = []
self.matches = []
self.rounds = []
self.current_round = 0
async def send_to_player(self, player, data):
await player.send(json.dumps(data))
# Instance of the class
tournament_match_maker = TournamentMatchMaker()