diff --git a/makefile b/makefile
index e795e69..f1b37a8 100644
--- a/makefile
+++ b/makefile
@@ -4,7 +4,7 @@ CONTAINER=$(c)
up: down
$(COMPOSE) build
- $(COMPOSE) up -d $(CONTAINER) || true
+ $(COMPOSE) up $(CONTAINER)
build:
$(COMPOSE) build $(CONTAINER)
diff --git a/pong/game/consumers.py b/pong/game/consumers.py
index a5e3853..a71eff0 100644
--- a/pong/game/consumers.py
+++ b/pong/game/consumers.py
@@ -26,6 +26,9 @@ class GameConsumer(AsyncWebsocketConsumer):
await self.game.handle_key_press(self, data['key'])
else:
await match_maker.handle_key_press(self, data['key'])
+ elif data['type'] == 'start_tournament':
+ print("Start TOURNAMENT received..")
+ await tournament_match_maker.start_tournament()
async def authenticate(self, token):
user = await self.get_user_from_token(token)
@@ -82,11 +85,11 @@ class GameConsumer(AsyncWebsocketConsumer):
if user:
self.user = user
await self.send(text_data=json.dumps({'type': 'authenticated'}))
- print(f"User {self.user} authenticated")
+ print(f"User {self.user.username} authenticated for tournament")
await self.join_tournament_waiting_room()
else:
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'}))
- print("Authentication failed")
+ print("Tournament authentication failed")
async def join_tournament_waiting_room(self):
await tournament_match_maker.add_player(self)
diff --git a/pong/game/game.py b/pong/game/game.py
index 515dccd..e619da5 100644
--- a/pong/game/game.py
+++ b/pong/game/game.py
@@ -7,7 +7,6 @@ from datetime import datetime
from .utils import handle_game_data
from asgiref.sync import sync_to_async
-
class Game:
def __init__(self, game_id, player1, player2, localgame):
self.game_id = game_id
@@ -47,12 +46,11 @@ class Game:
self.p2_mov = 0
self.bt1 = 0
self.bt2 = 0
- self.start_time = None
+ self.start_time = datetime.now()
async def start_game(self):
print(f"- Game #{self.game_id} STARTED")
self.game_loop_task = asyncio.create_task(self.game_loop())
- self.start_time = datetime.now()
print(f" Begin MATCH at: {self.start_time}")
async def game_loop(self):
@@ -213,7 +211,7 @@ class Game:
print(f"- Game #{self.game_id} ENDED")
end_time = datetime.now()
- duration = (end_time - self.start_time).total_seconds() / 60
+ duration = (end_time - self.start_time).total_seconds() / 60
# Notify that one player left the game
if disconnected_player:
diff --git a/pong/game/templates/pong/tournament_brackets.html b/pong/game/templates/pong/tournament_brackets.html
new file mode 100644
index 0000000..85719a3
--- /dev/null
+++ b/pong/game/templates/pong/tournament_brackets.html
@@ -0,0 +1,85 @@
+
+
+
+
+
+ Tournament Brackets
+
+
+
+
+ {% for round in tournament_rounds %}
+
+
Round {{ forloop.counter }}
+ {% for match in round %}
+
+
+ {{ match.player1 }}
+ {% if match.score1 is not None %}{{ match.score1 }}{% endif %}
+
+
+ {{ match.player2|default:"BYE" }}
+ {% if match.score2 is not None %}{{ match.score2 }}{% endif %}
+
+
+ {% if not forloop.last %}
{% endif %}
+ {% endfor %}
+
+ {% endfor %}
+
+
+
\ No newline at end of file
diff --git a/pong/game/templates/pong/tournament_waiting_room.html b/pong/game/templates/pong/tournament_waiting_room.html
index dc09859..cb77338 100644
--- a/pong/game/templates/pong/tournament_waiting_room.html
+++ b/pong/game/templates/pong/tournament_waiting_room.html
@@ -1,10 +1,20 @@
-
+
Tournament Waiting Room
-
Players currently waiting: {{ players|length }}
+
+
Players currently waiting: {{ players_count }}
+
Minimum players needed to start: {{ min_players_to_start }}
+
+
Players:
- {% for player in players %}
- - {{ player }}
- {% endfor %}
+ {% for player in players %}
+ - {{ player }}
+ {% endfor %}
+
+ {% if players_count >= min_players_to_start %}
+
+ {% else %}
+
Waiting for more players to join...
+ {% endif %}
\ No newline at end of file
diff --git a/pong/game/tournament.py b/pong/game/tournament.py
index 60dd111..1f039fb 100644
--- a/pong/game/tournament.py
+++ b/pong/game/tournament.py
@@ -1,14 +1,42 @@
# /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 player not in self.waiting_players:
+ 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()
@@ -21,16 +49,111 @@ class TournamentMatchMaker:
async def update_waiting_room(self):
html = self.generate_waiting_room_html()
for player in self.waiting_players:
- await player.send(json.dumps({
- 'type': 'update_waiting_room',
+ 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]
+ '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()
\ No newline at end of file
diff --git a/pong/static/game.js b/pong/static/game.js
index f4e7216..1d79dcf 100644
--- a/pong/static/game.js
+++ b/pong/static/game.js
@@ -383,6 +383,7 @@ document.addEventListener('DOMContentLoaded', () => {
console.log('Entered the WAITING ROOM');
} else if (data.type === 'game_start') {
console.log('Game started:', data.game_id, '(', data.player1, 'vs', data.player2, ')');
+ gameContainer.style.display = 'flex';
document.addEventListener('keydown', handleKeyDown);
} else if (data.type === 'game_state_update') {
updateGameState(data.game_state);
@@ -392,7 +393,26 @@ document.addEventListener('DOMContentLoaded', () => {
console.log("Game ended:", data.game_id);
} else if (data.type === 'error') {
console.error(data.message);
- } else if (data.type === 'update_waiting_room') {
+ // Assuming you're inside some WebSocket message handling function
+ } else if (data.type === 'update_tournament_waiting_room') {
+ // Update the HTML content of the tournament bracket
+ document.getElementById('tournament-bracket').innerHTML = data.html;
+
+ // Reattach the event listener to the "Start Tournament" button
+ const startButton = document.getElementById('start-tournament-btn');
+ if (startButton) {
+ startButton.addEventListener('click', function() {
+ if (typeof socket !== 'undefined' && socket.readyState === WebSocket.OPEN) {
+ console.log("Start TOURNAMENT sent..");
+ socket.send(JSON.stringify({type: 'start_tournament'}));
+ } else {
+ console.error("WebSocket is not open or undefined");
+ }
+ });
+ } else {
+ console.error('Start button not found');
+ }
+ } else if (data.type === 'update_brackets') {
document.getElementById('tournament-bracket').innerHTML = data.html;
} else {
console.log('Message from server:', data.type, data.message);
@@ -481,7 +501,6 @@ document.addEventListener('DOMContentLoaded', () => {
startLocalGame2();
});
-
function checkForWinner() {
if (gameState.player1_score === 3 || gameState.player2_score === 3) {
gameControls.style.display = 'flex';