From 6ac9629f6afb2df66de24e67a9c66a75bf37dd87 Mon Sep 17 00:00:00 2001 From: CHIBOUB Chakib Date: Thu, 22 Aug 2024 19:07:24 +0200 Subject: [PATCH 01/14] endfortheouche doesnt work yet --- docker-compose.yml | 108 ++++++++++++++++++++++----------------------- pong/game/game.py | 5 ++- pong/game/utils.py | 41 ++++++++++++----- requirements.txt | 1 + 4 files changed, 88 insertions(+), 67 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 86419bb..6e96098 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -58,61 +58,6 @@ services: timeout: 5s retries: 120 - - - backend: - build: - context: . - dockerfile: Dockerfile - image: backend - container_name: backend - restart: always - command: /bin/sh -c "sleep 5 && - venv/bin/python manage.py makemigrations --noinput && - venv/bin/python manage.py migrate --noinput && - venv/bin/python manage.py collectstatic --noinput && - venv/bin/daphne -b 0.0.0.0 -p 8080 pong.asgi:application" - volumes: - - pong:/transcendence/pong - ports: - - 8080:8080 - networks: - - app-network - environment: - DB_HOST: db - DB_PORT: 5432 - DB_NAME: ${POSTGRES_DB} - DB_USER: ${POSTGRES_USER} - DB_PASSWORD: ${POSTGRES_PASSWORD} - depends_on: - - db - healthcheck: - test: ["CMD-SHELL", "curl", "http://localhost:8080"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 10s - - db: - image: postgres:latest - container_name: postgres - restart: always - volumes: - - pong_pg_data:/var/lib/postgresql/data - ports: - - "5432:5432" - networks: - - app-network - environment: - POSTGRES_DB: ${POSTGRES_DB} - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] - interval: 10s - timeout: 5s - retries: 5 - es01: image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} container_name: es01 @@ -209,6 +154,59 @@ services: - ELASTIC_USER=${ELASTIC_USERNAME} - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} - xpack.monitoring.enabled=false + + backend: + build: + context: . + dockerfile: Dockerfile + image: backend + container_name: backend + restart: always + command: /bin/sh -c "sleep 5 && + venv/bin/python manage.py makemigrations --noinput && + venv/bin/python manage.py migrate --noinput && + venv/bin/python manage.py collectstatic --noinput && + venv/bin/daphne -b 0.0.0.0 -p 8080 pong.asgi:application" + volumes: + - pong:/transcendence/pong + ports: + - 8080:8080 + networks: + - app-network + environment: + DB_HOST: db + DB_PORT: 5432 + DB_NAME: ${POSTGRES_DB} + DB_USER: ${POSTGRES_USER} + DB_PASSWORD: ${POSTGRES_PASSWORD} + depends_on: + - db + healthcheck: + test: ["CMD-SHELL", "curl", "http://localhost:8080"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s + + db: + image: postgres:latest + container_name: postgres + restart: always + volumes: + - pong_pg_data:/var/lib/postgresql/data + ports: + - "5432:5432" + networks: + - app-network + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 volumes: pong: diff --git a/pong/game/game.py b/pong/game/game.py index 31048af..2baeae2 100644 --- a/pong/game/game.py +++ b/pong/game/game.py @@ -100,12 +100,14 @@ class Game: self.game_state['player2_score'] += 1 if self.game_state['player2_score'] > 2: print("Here !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + await self.send_game_state() await self.end_game() self.reset_ball() elif self.game_state['ball_position']['x'] >= 790: self.game_state['player1_score'] += 1 if self.game_state['player1_score'] > 2: print("Here !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + await self.send_game_state() await self.end_game() self.reset_ball() @@ -197,7 +199,8 @@ class Game: 'player': disconnected_name }) if not self.botgame: - await remaining_player.send(message) + if not self.localgame: + await remaining_player.send(message) # Notify both players that the game has ended end_message = json.dumps({ 'type': 'game_ended', diff --git a/pong/game/utils.py b/pong/game/utils.py index b01c212..070da36 100644 --- a/pong/game/utils.py +++ b/pong/game/utils.py @@ -4,25 +4,34 @@ from django.shortcuts import get_object_or_404 from django.db.models import Max, Sum, F from datetime import timedelta from channels.db import database_sync_to_async -#from asgiref.sync import database_sync_to_async - async def endfortheouche(p1, p2, s_p1, s_p2, bt_p1, bt_2, dur, is_tournoi, name_tournament): try: print("here endfortheouche §!!!") - if not await database_sync_to_async(Player.objects.filter(name=p1).exists)(): + + # Handle Player 1 + exists = await database_sync_to_async(Player.objects.filter(name=p1).exists)() + if exists: + print(f"Player {p1} exists.") + else: + print(f"Player {p1} does not exist.") + + player_1 = await get_name(p1) + print(f"Player 1 retrieval result: {player_1}") + if player_1 is None: + print("############# CREATING PLAYER") player_1 = await create_player(p1) - print("############# PLAYER DONE") else: - player_1 = await database_sync_to_async(Player.objects.get)(name=p1) - - print("ok") - - if not await database_sync_to_async(Player.objects.filter(name=p2).exists)(): + print("############# PLAYER FOUND") + + # Handle Player 2 + player_2 = await get_name(p2) + print(f"Player 2 retrieval result: {player_2}") + if player_2 is None: + print("############# CREATING PLAYER") player_2 = await create_player(p2) - print("############# PLAYER DONE") else: - player_2 = await database_sync_to_async(Player.objects.get)(name=p2) + print("############# PLAYER FOUND") print("############# BEFORE MATCH") await create_match(player_1, player_2, s_p1, s_p2, bt_p1, bt_2, dur, is_tournoi, name_tournament) @@ -31,9 +40,19 @@ async def endfortheouche(p1, p2, s_p1, s_p2, bt_p1, bt_2, dur, is_tournoi, name_ await update_player_statistics(p1) print("############# END STAT P1") await update_player_statistics(p2) + print("############# END STAT P2") except Exception as e: print(f"Error in endfortheouche: {e}") +@database_sync_to_async +def get_name(p): + print(f"in get_name({p})..") + try: + return Player.objects.get(name=p) + except Player.DoesNotExist: + print("get_name() exception") + return None + @database_sync_to_async def create_player( name, diff --git a/requirements.txt b/requirements.txt index ceb5c49..3c91bd0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ channels daphne djangorestframework web3 +asyncpg From 2a4aef62b205d3ff9961dcd20e87cc53dcebae4f Mon Sep 17 00:00:00 2001 From: Theouche Date: Tue, 27 Aug 2024 18:19:36 +0200 Subject: [PATCH 02/14] theouche --- pong/game/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pong/game/utils.py b/pong/game/utils.py index 7ef52e9..95e8c38 100644 --- a/pong/game/utils.py +++ b/pong/game/utils.py @@ -213,7 +213,8 @@ def get_player_p_win(player_name): ######## try synchrone version ######## def endfortheouche(p1, p2, s_p1, s_p2, bt_p1, bt_2, dur, is_tournoi, name_tournament): try: - print("here endfortheouche §!!!") + print("Debut de endfortheouche") + print(f"Paramètres : p1={p1}, p2={p2}, s_p1={s_p1}, s_p2={s_p2}") # Vérification de l'existence des joueurs et création si nécessaire player_1 = get_or_create_player(p1) player_2 = get_or_create_player(p2) From 697d8b5d0f14793936867911842cb7ad28630941 Mon Sep 17 00:00:00 2001 From: CHIBOUB Chakib Date: Tue, 27 Aug 2024 18:46:56 +0200 Subject: [PATCH 03/14] starting the tournament code --- pong/game/consumers.py | 18 +++++++ pong/game/game.py | 25 ++++++---- pong/game/matchmaking.py | 3 -- .../pong/tournament_waiting_room.html | 10 ++++ pong/game/tournament.py | 36 +++++++++++++ pong/static/game.js | 50 +++++++++---------- pong/static/index.html | 4 +- pong/static/styles.css | 19 +++---- requirements.txt | 4 +- 9 files changed, 116 insertions(+), 53 deletions(-) create mode 100644 pong/game/templates/pong/tournament_waiting_room.html create mode 100644 pong/game/tournament.py diff --git a/pong/game/consumers.py b/pong/game/consumers.py index e3d68a1..a5e3853 100644 --- a/pong/game/consumers.py +++ b/pong/game/consumers.py @@ -5,6 +5,7 @@ from channels.generic.websocket import AsyncWebsocketConsumer from django.contrib.auth.models import User from channels.db import database_sync_to_async from .matchmaking import match_maker +from .tournament import tournament_match_maker class GameConsumer(AsyncWebsocketConsumer): async def connect(self): @@ -18,6 +19,8 @@ class GameConsumer(AsyncWebsocketConsumer): await self.authenticate(data['token']) elif data['type'] == 'authenticate2': await self.authenticate2(data['token_1'], data['token_2']) + elif data['type'] == 'authenticate3': + await self.authenticate3(data['token']) elif data['type'] == 'key_press': if self.game: await self.game.handle_key_press(self, data['key']) @@ -74,10 +77,25 @@ class GameConsumer(AsyncWebsocketConsumer): except User.DoesNotExist: return None + async def authenticate3(self, token): + user = await self.get_user_from_token(token) + if user: + self.user = user + await self.send(text_data=json.dumps({'type': 'authenticated'})) + print(f"User {self.user} authenticated") + await self.join_tournament_waiting_room() + else: + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'})) + print("Authentication failed") + + async def join_tournament_waiting_room(self): + await tournament_match_maker.add_player(self) + async def disconnect(self, close_code): if self.game: await self.game.end_game(disconnected_player=self) await match_maker.remove_player(self) + await tournament_match_maker.remove_player(self) print(f"User {self.user.username if hasattr(self, 'user') else 'Unknown'} disconnected") async def set_game(self, game): diff --git a/pong/game/game.py b/pong/game/game.py index 2baeae2..4708878 100644 --- a/pong/game/game.py +++ b/pong/game/game.py @@ -22,7 +22,8 @@ class Game: 'ball_position': {'x': 390, 'y': 190}, 'ball_velocity': {'x': random.choice([-5, 5]), 'y': random.choice([-5, 5])}, 'player1_score': 0, - 'player2_score': 0 + 'player2_score': 0, + 'game_text': '' } else: self.botgame = player2 is None @@ -34,7 +35,8 @@ class Game: 'ball_position': {'x': 390, 'y': 190}, 'ball_velocity': {'x': random.choice([-5, 5]), 'y': random.choice([-5, 5])}, 'player1_score': 0, - 'player2_score': 0 + 'player2_score': 0, + 'game-text': '' } self.speed = 1 self.game_loop_task = None @@ -53,9 +55,13 @@ class Game: async def game_loop(self): print(" In the game loop..") + x = 0 while not self.ended: if self.botgame: - await self.update_bot_position() + x += 1 + if x == 60: + await self.update_bot_position() + x = 0 await self.handle_pad_movement() await self.update_game_state() await self.send_game_state() @@ -66,9 +72,9 @@ class Game: if self.game_state['player2_position'] < target_y < self.game_state['player2_position'] + 80: pass elif self.game_state['player2_position'] < target_y: - self.game_state['player2_position'] = min(self.game_state['player2_position'] + (5 * self.speed), 300) + self.game_state['player2_position'] = min(self.game_state['player2_position'] + (50 * self.speed), 300) elif self.game_state['player2_position'] + 80 > target_y: - self.game_state['player2_position'] = max(self.game_state['player2_position'] - (5 * self.speed), 0) + self.game_state['player2_position'] = max(self.game_state['player2_position'] - (50 * self.speed), 0) async def update_game_state(self): if self.ended: @@ -92,21 +98,18 @@ class Game: self.game_state['ball_velocity']['x'] *= -1 self.bt2 += 1 self.update_ball_velocity() - # Check for scoring - #print(f"########### score user 1 {self.game_state['player1_score']} ###########") - #print(f"§§§§§§§§§§§ score user 2 {self.game_state['player2_score']} §§§§§§§§§§§") - + # Check if some player won the game if self.game_state['ball_position']['x'] <= 10: self.game_state['player2_score'] += 1 if self.game_state['player2_score'] > 2: - print("Here !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + self.game_state['game_text'] = f"{self.game_state['player2_name']} WINS!" await self.send_game_state() await self.end_game() self.reset_ball() elif self.game_state['ball_position']['x'] >= 790: self.game_state['player1_score'] += 1 if self.game_state['player1_score'] > 2: - print("Here !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + self.game_state['game_text'] = f"{self.game_state['player1_name']} WINS!" await self.send_game_state() await self.end_game() self.reset_ball() diff --git a/pong/game/matchmaking.py b/pong/game/matchmaking.py index 9d20580..5ca454c 100644 --- a/pong/game/matchmaking.py +++ b/pong/game/matchmaking.py @@ -110,6 +110,3 @@ class MatchMaker: # Instance of the class match_maker = MatchMaker() - -# to get what you want use get_player_p_win(player_name) !! (voir utils.py) -# \ 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 new file mode 100644 index 0000000..dc09859 --- /dev/null +++ b/pong/game/templates/pong/tournament_waiting_room.html @@ -0,0 +1,10 @@ + +
+

Tournament Waiting Room

+

Players currently waiting: {{ players|length }}

+
    + {% for player in players %} +
  • {{ player }}
  • + {% endfor %} +
+
\ No newline at end of file diff --git a/pong/game/tournament.py b/pong/game/tournament.py new file mode 100644 index 0000000..60dd111 --- /dev/null +++ b/pong/game/tournament.py @@ -0,0 +1,36 @@ +# /pong/game/tournament.py + +import json +from django.template.loader import render_to_string + +class TournamentMatchMaker: + def __init__(self): + self.waiting_players = [] + + async def add_player(self, player): + if 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 player.send(json.dumps({ + 'type': 'update_waiting_room', + 'html': html + })) + + def generate_waiting_room_html(self): + context = { + 'players': [player.user.username for player in self.waiting_players] + } + return render_to_string('pong/tournament_waiting_room.html', context) + +# 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 1a65cdf..6eb7096 100644 --- a/pong/static/game.js +++ b/pong/static/game.js @@ -28,6 +28,7 @@ document.addEventListener('DOMContentLoaded', () => { const loginButton2 = document.getElementById('login2'); const gameContainer = document.getElementById('game1'); + const tournamentContainer = document.getElementById('tournament-bracket'); const menuButton = document.querySelector('.burger-menu'); const dropdownMenu = document.getElementById('dropdown-menu'); @@ -351,7 +352,12 @@ document.addEventListener('DOMContentLoaded', () => { } function startTournament() { - console.log("For now, do nothing, hurry up and work Senor chaku !!!!") + tournamentContainer.style.display = 'flex'; + logo.style.display = 'none'; + pongElements.style.display = 'none'; + //menuButton.style.display = 'none'; + formBlock.style.display = 'none'; + startWebSocketConnection(token, 42); } function startWebSocketConnection(token, players) { @@ -360,11 +366,14 @@ document.addEventListener('DOMContentLoaded', () => { socket.onopen = function (event) { console.log('WebSocket connection established'); if (players === 1) { - console.log("Sending token for 1 player game"); + console.log("Sending token for a quick match game"); socket.send(JSON.stringify({ type: 'authenticate', token: token })); - } else { - console.log("Sending tokens for 2 player game"); + } else if (players === 2) { + console.log("Sending tokens for a local game"); socket.send(JSON.stringify({ type: 'authenticate2', token_1: token, token_2: token2 })); + } else { + console.log("Sending token for a tournament game") + socket.send(JSON.stringify({ type: 'authenticate3', token: token })); } }; @@ -376,7 +385,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, ')'); - startGame(data.game_id, data.player1, data.player2); + document.addEventListener('keydown', handleKeyDown); } else if (data.type === 'game_state_update') { updateGameState(data.game_state); } else if (data.type === 'player_disconnected') { @@ -385,6 +394,8 @@ 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') { + document.getElementById('tournament-bracket').innerHTML = data.html; } else { console.log('Message from server:', data.type, data.message); } @@ -399,14 +410,6 @@ document.addEventListener('DOMContentLoaded', () => { }; } - function startGame(gameCode, player1_name, player2_name) { - document.getElementById('gameCode').textContent = `Game Code: ${gameCode}`; - document.getElementById('player1-name').textContent = `${player1_name}`; - document.getElementById('player2-name').textContent = `${player2_name}`; - document.addEventListener('keydown', handleKeyDown); - - } - function handleKeyDown(event) { if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'w' || event.key === 's') { //console.log('Key press: ', event.key); @@ -427,24 +430,21 @@ document.addEventListener('DOMContentLoaded', () => { } function renderGame() { - const player1Pad = document.getElementById('player1-pad'); - player1Pad.style.top = `${gameState.player1_position}px`; + document.getElementById('player1-name').textContent = `${gameState.player1_name}`; + document.getElementById('player2-name').textContent = `${gameState.player2_name}`; - const player2Pad = document.getElementById('player2-pad'); - player2Pad.style.top = `${gameState.player2_position}px`; + document.getElementById('player1-pad').style.top = `${gameState.player1_position}px`; + document.getElementById('player2-pad').style.top = `${gameState.player2_position}px`; - const ball = document.getElementById('ball'); - ball.style.left = `${gameState.ball_position.x}px`; - ball.style.top = `${gameState.ball_position.y}px`; + document.getElementById('ball').style.left = `${gameState.ball_position.x}px`; + document.getElementById('ball').style.top = `${gameState.ball_position.y}px`; - const player1Score = document.getElementById('player1-score'); - player1Score.textContent = gameState.player1_score; + document.getElementById('player1-score').textContent = gameState.player1_score; + document.getElementById('player2-score').textContent = gameState.player2_score; - const player2Score = document.getElementById('player2-score'); - player2Score.textContent = gameState.player2_score; + document.getElementById('game-text').textContent = gameState.game_text; } - ////////////////////////////// BEG BURGER BUTTON //////////////////////////////// menuButton.addEventListener('click', toggleMenu); diff --git a/pong/static/index.html b/pong/static/index.html index 63f1278..7731712 100644 --- a/pong/static/index.html +++ b/pong/static/index.html @@ -111,8 +111,9 @@ + + diff --git a/pong/static/styles.css b/pong/static/styles.css index 450ac4e..4103df0 100644 --- a/pong/static/styles.css +++ b/pong/static/styles.css @@ -59,20 +59,10 @@ button:hover { align-items: center; } -.game-code { - font-size: 24px; - position: absolute; - top: 10px; -} - -#gameCode { - left: 10px; -} - .name { font-size: 24px; position: absolute; - top: 30px; + top: 20px; } #player1-name { @@ -144,6 +134,13 @@ button:hover { position: absolute; } +#game-text { + font-size: 64px; + color: #00ffff; + position: absolute; + top: 150px; +} + .logo { position: absolute; top: 20px; diff --git a/requirements.txt b/requirements.txt index 3c91bd0..54b2e96 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ -Django +django psycopg2 python-dotenv channels daphne djangorestframework web3 -asyncpg +asyncpg \ No newline at end of file From dcab0489b9baacef340f595e2149c2a214849159 Mon Sep 17 00:00:00 2001 From: CHIBOUB Chakib Date: Tue, 27 Aug 2024 19:09:11 +0200 Subject: [PATCH 04/14] merging with theouche and adrien ok --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index 0c6d19d..e795e69 100644 --- a/makefile +++ b/makefile @@ -32,7 +32,7 @@ ps: db-shell: $(COMPOSE) exec db psql -U 42student players_db -re: destroy down up +re: destroy up help: @echo "Usage:" From c9e806897f26d63ca6e5e2612b942bbb30625688 Mon Sep 17 00:00:00 2001 From: estellon Date: Tue, 27 Aug 2024 21:30:18 +0200 Subject: [PATCH 05/14] css tournament --- pong/static/styles.css | 67 ++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/pong/static/styles.css b/pong/static/styles.css index 4103df0..61748ff 100644 --- a/pong/static/styles.css +++ b/pong/static/styles.css @@ -260,9 +260,9 @@ button:hover { .navbar { - position: absolute; - top: 30px; - right: 10px; + position: absolute; + top: 30px; + right: 10px; padding: 10px; } @@ -270,28 +270,28 @@ button:hover { font-size: 24px; background: none; border: none; - color: #00ffff; + color: #00ffff; cursor: pointer; transition: color 0.3s ease; } .burger-menu:hover { - color: #ffffff; + color: #ffffff; } .dropdown-content { display: none; position: absolute; - top: 100%; - right: 0; - margin-top: 10px; - background-color: #1a1a2e; - color: #ffffff; + top: 100%; + right: 0; + margin-top: 10px; + background-color: #1a1a2e; + color: #ffffff; box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.3); border-radius: 5px; z-index: 1; - width: max-content; + width: max-content; } .dropdown-content.show { @@ -300,18 +300,18 @@ button:hover { .dropdown-content a { - color: #ffffff; + color: #ffffff; padding: 12px 16px; text-decoration: none; display: block; - border-bottom: 1px solid rgba(255, 255, 255, 0.2); - transition: background-color 0.3s ease; + border-bottom: 1px solid rgba(255, 255, 255, 0.2); + transition: background-color 0.3s ease; } .dropdown-content a:hover { - background-color: #333; - color: #00ffff; + background-color: #333; + color: #00ffff; } .language-switcher { @@ -371,8 +371,8 @@ button:hover { width: 100%; height: 100%; overflow: auto; - background-color: rgb(0,0,0); - background-color: rgba(0,0,0,0.4); + background-color: rgb(0, 0, 0); + background-color: rgba(0, 0, 0, 0.4); padding-top: 60px; } @@ -403,4 +403,35 @@ button:hover { pre { white-space: pre-wrap; word-wrap: break-word; +} + +.tournament-waiting-room { + background-color: rgba(0, 0, 0, 0.6); + padding: 20px; + border-radius: 15px; + color: #00ffff; + width: 50%; + margin: auto; + text-align: center; + box-shadow: 0 0 30px #00ffff, inset 0 0 20px #00ffff; + +} + +.tournament-waiting-room h2 { + font-family: 'Arial', sans-serif; + font-size: 2em; + margin-bottom: 15px; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7); +} + +.tournament-waiting-room p { + font-family: 'Verdana', sans-serif; + font-size: 1.2em; + margin-bottom: 20px; + text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.6); +} + +.tournament-waiting-room ul { + list-style-type: none; + padding: 0; } \ No newline at end of file From 4129f927a8382be68f5db1f95fa3993e003027a9 Mon Sep 17 00:00:00 2001 From: Theouche Date: Wed, 28 Aug 2024 15:21:50 +0200 Subject: [PATCH 06/14] add research bar --- pong/game/game.py | 36 +++++-- pong/game/utils.py | 206 +---------------------------------------- pong/static/game.js | 86 ++++++++++++----- pong/static/index.html | 3 + 4 files changed, 94 insertions(+), 237 deletions(-) diff --git a/pong/game/game.py b/pong/game/game.py index 17f796f..7bd34d7 100644 --- a/pong/game/game.py +++ b/pong/game/game.py @@ -70,13 +70,35 @@ class Game: await asyncio.sleep(1/60) # Around 60 FPS async def update_bot_position(self): - target_y = self.game_state['ball_position']['y'] - if self.game_state['player2_position'] < target_y < self.game_state['player2_position'] + 80: - pass - elif self.game_state['player2_position'] < target_y: - self.game_state['player2_position'] = min(self.game_state['player2_position'] + (50 * self.speed), 300) - elif self.game_state['player2_position'] + 80 > target_y: - self.game_state['player2_position'] = max(self.game_state['player2_position'] - (50 * self.speed), 0) + future_ball_position = self.predict_ball_trajectory() + + target_y = future_ball_position['y'] + player2_position = self.game_state['player2_position'] + + # Ajuste la position du bot en fonction de la position prévue de la balle + if player2_position < target_y < player2_position + 80: + pass # Pas besoin de bouger, le bot est déjà bien placé + elif player2_position < target_y: + self.game_state['player2_position'] = min(player2_position + (50 * self.speed), 300) + elif player2_position + 80 > target_y: + self.game_state['player2_position'] = max(player2_position - (50 * self.speed), 0) + + def predict_ball_trajectory(self, steps=60): + + future_x = self.game_state['ball_position']['x'] + future_y = self.game_state['ball_position']['y'] + velocity_x = self.game_state['ball_velocity']['x'] + velocity_y = self.game_state['ball_velocity']['y'] + + for _ in range(steps): + future_x += velocity_x + future_y += velocity_y + + # Gérer les rebonds sur les murs + if future_y <= 0 or future_y >= 300: + velocity_y = -velocity_y # Inverser la direction du mouvement vertical + + return {'x': future_x, 'y': future_y} async def update_game_state(self): if self.ended: diff --git a/pong/game/utils.py b/pong/game/utils.py index fc31941..099ced4 100644 --- a/pong/game/utils.py +++ b/pong/game/utils.py @@ -6,211 +6,6 @@ from datetime import timedelta from channels.db import database_sync_to_async #from asgiref.sync import database_sync_to_async -""" async def endfortheouche(p1, p2, s_p1, s_p2, bt_p1, bt_2, dur, is_tournoi, name_tournament): - try: - print("here endfortheouche §!!!") - # Vérification de l'existence des joueurs et création si nécessaire - player_1 = await get_or_create_player(p1) - player_2 = await get_or_create_player(p2) - - print("ok") - - print("############# BEFORE MATCH") - await create_match(player_1, player_2, s_p1, s_p2, bt_p1, bt_2, dur, is_tournoi, name_tournament) - print("############# AFTER DONE") - - await update_player_statistics(p1) - print("############# END STAT P1") - await update_player_statistics(p2) - print("############# END STAT P2") - except Exception as e: - print(f"Error in endfortheouche: {e}") - -@database_sync_to_async -def get_player_by_name(name): - print(f"Checking if player '{name}' exists") - exists = Player.objects.filter(name=name).exists() - print(f"Player exists: {exists}") - return exists - - -@database_sync_to_async -def get_player(name): - return Player.objects.get(name=name) - - -async def get_or_create_player(name): - print("here !!") - print(f"Checking existence for player: {name}") - player_exists = await get_player_by_name(name) - print(f"END search in database!! (Player exists: {player_exists})") - if not player_exists: - print("Player does not exist, creating player...") - player = await create_player(name) - print(f"Player created: {player}") - return player - else: - print("Player exists, fetching player...") - player = await get_player(name) - print(f"Player fetched: {player}") - return player - - -@database_sync_to_async -def create_player( - name, - total_match=0, - total_win=0, - p_win= None, - m_score_match= None, - m_score_adv_match= None, - best_score=0, - m_nbr_ball_touch= None, - total_duration= None, - m_duration= None, - num_participated_tournaments=0, - num_won_tournaments=0 -): - print("create player !!!") - - player = Player( - name=name, - total_match=total_match, - total_win=total_win, - p_win=p_win, - m_score_match=m_score_match, - m_score_adv_match=m_score_adv_match, - best_score=best_score, - m_nbr_ball_touch=m_nbr_ball_touch, - total_duration=total_duration, - m_duration=m_duration, - num_participated_tournaments=num_participated_tournaments, - num_won_tournaments=num_won_tournaments - ) - player.save() - return player - -@database_sync_to_async -def create_tournoi(name, nbr_player, date, winner): - tournoi = Tournoi(name=name, nbr_player=nbr_player, date=date, winner=winner) - tournoi.save() - return tournoi - -@database_sync_to_async -def create_match(player1, player2, score_player1, score_player2, nbr_ball_touch_p1, nbr_ball_touch_p2, duration, is_tournoi, tournoi): - match = Match( - player1=player1, - player2=player2, - score_player1=score_player1, - score_player2=score_player2, - nbr_ball_touch_p1=nbr_ball_touch_p1, - nbr_ball_touch_p2=nbr_ball_touch_p2, - duration=duration, - is_tournoi=is_tournoi, - tournoi=tournoi - ) - - if score_player1 > score_player2: - match.winner = match.player1 - elif score_player2 > score_player1: - match.winner = match.player2 - else: - match.winner = None - - match.save() - return match - -@database_sync_to_async -def update_player_statistics(player_name): - print("############# BEG STAT P") - player = get_object_or_404(Player, name=player_name) - - # Filtrer les matchs où le joueur est joueur 1 ou joueur 2 - print("############# HERE") - matches_as_player1 = Match.objects.filter(player1=player) - matches_as_player2 = Match.objects.filter(player2=player) - print("############# ACTUALLY, IT'S GOOD") - - # Calculer les statistiques - total_match = matches_as_player1.count() + matches_as_player2.count() - - if total_match == 0: - # Eviter la division par zéro - player.total_match = total_match - player.total_win = 0 - player.p_win = 0 - player.m_score_match = 0 - player.m_score_adv_match = 0 - player.best_score = 0 - player.m_nbr_ball_touch = 0 - player.total_duration = 0 - player.m_duration = 0 - player.num_participated_tournaments = 0 - player.num_won_tournaments = 0 - player.save() - return - - won_matches = Match.objects.filter(winner=player) - #part_tourn_as_p1 = Tournoi.objects.filter(matches__is_tournoi=True, matches__matches_as_player1=player) - #part_tourn_as_p2 = Tournoi.objects.filter(matches__is_tournoi=True, matches__matches_as_player2=player) - #won_tourn = Tournoi.objects.filter(winner=player) - - total_score = matches_as_player1.aggregate(Sum('score_player1'))['score_player1__sum'] or 0 - total_score += matches_as_player2.aggregate(Sum('score_player2'))['score_player2__sum'] or 0 - - total_score_adv = matches_as_player1.aggregate(Sum('score_player2'))['score_player2__sum'] or 0 - total_score_adv += matches_as_player2.aggregate(Sum('score_player1'))['score_player1__sum'] or 0 - - total_win = won_matches.count() - p_win = (total_win / total_match) * 100 - - m_score_match = total_score / total_match - m_score_adv_match = total_score_adv / total_match - - nbr_ball_touch = matches_as_player1.aggregate(Sum('nbr_ball_touch_p1'))['nbr_ball_touch_p1__sum'] or 0 - nbr_ball_touch += matches_as_player2.aggregate(Sum('nbr_ball_touch_p2'))['nbr_ball_touch_p2__sum'] or 0 - m_nbr_ball_touch = nbr_ball_touch / total_match - - total_duration = matches_as_player1.aggregate(Sum('duration'))['duration__sum'] or 0 - total_duration += matches_as_player2.aggregate(Sum('duration'))['duration__sum'] or 0 - m_duration = total_duration / total_match - - #total_tourn_p = part_tourn_as_p1.count() + part_tourn_as_p2.count() - #total_win_tourn = won_tourn.count() - #p_win_tourn = (total_win_tourn / total_tourn_p) * 100 if total_tourn_p else 0 - - best_score_as_player1 = matches_as_player1.aggregate(Max('score_player1'))['score_player1__max'] or 0 - best_score_as_player2 = matches_as_player2.aggregate(Max('score_player2'))['score_player2__max'] or 0 - best_score = max(best_score_as_player1, best_score_as_player2) - - # Mettre à jour les champs du joueur - player.total_match = total_match - player.total_win = total_win - player.p_win = p_win - player.m_score_match = m_score_match - player.m_score_adv_match = m_score_adv_match - player.best_score = best_score - player.m_nbr_ball_touch = m_nbr_ball_touch - player.total_duration = total_duration - player.m_duration = m_duration - # player.num_participated_tournaments = total_tourn_p - #player.num_won_tournaments = total_win_tourn - - player.save() - print("CHAKU IS THE BEST") - -def get_player_p_win(player_name): - # Rechercher le joueur par son nom - player = get_object_or_404(Player, name=player_name) - # Retourner la valeur de p_win - return player.p_win - """ - - - - - - ######## try synchrone version ######## def endfortheouche(p1, p2, s_p1, s_p2, bt_p1, bt_2, dur, is_tournoi, name_tournament): try: @@ -229,6 +24,7 @@ def endfortheouche(p1, p2, s_p1, s_p2, bt_p1, bt_2, dur, is_tournoi, name_tourna update_player_statistics(p1) print("############# END STAT P1") update_player_statistics(p2) + except Exception as e: print(f"Error in endfortheouche: {e}") diff --git a/pong/static/game.js b/pong/static/game.js index 08efa26..df93785 100644 --- a/pong/static/game.js +++ b/pong/static/game.js @@ -45,11 +45,6 @@ document.addEventListener('DOMContentLoaded', () => { const quickMatchButton = document.getElementById('quick-match'); const tournamentButton = document.getElementById('tournament'); - /* const modal = document.getElementById("myModal"); - const btn = document.getElementById("myBtn"); - const span = document.getElementsByClassName("close")[0]; - const jsonContent = document.getElementById("jsonContent"); */ - let socket; let token; let gameState; @@ -646,6 +641,67 @@ document.addEventListener('DOMContentLoaded', () => { ////////////////////////////// END BURGER BUTTON //////////////////////////////// + ////////////////////////////// BEG STAT SPE //////////////////////////////// + + document.getElementById('search-player').addEventListener('input', filterPlayers); + document.getElementById('search-match-player').addEventListener('input', filterMatches); + document.getElementById('search-match-date').addEventListener('input', filterMatches); + + function filterPlayers() { + const searchValue = document.getElementById('search-player').value.toLowerCase(); + const playersListBody = document.querySelector('#player-list tbody'); + const rows = playersListBody.getElementsByTagName('tr'); + + for (let i = 0; i < rows.length; i++) { + const nameCell = rows[i].getElementsByTagName('td')[1]; // The 'Name' column + if (nameCell) { + const nameValue = nameCell.textContent || nameCell.innerText; + if (nameValue.toLowerCase().indexof(searchValue) > -1 ) { + rows[i].style.display = ''; + } else { + rows[i].style.display = 'none'; + } + } + } + } + + function filterMatches() { + const playerSearchValue = document.getElementById('search-match-player').value.toLowerCase(); + const dateSearchValue = document.getElementById('search-match-date').value; + const matchListBody = document.querySelector('#match-list tbody'); + const rows = matchListBody.getElementsByTagName('tr'); + + for (let i = 0; i < rows.length; i++) { + const player1Cell = rows[i].getElementsByTagName('td')[1]; // The 'Player 1' column + const player2Cell = rows[i].getElementsByTagName('td')[2]; // The 'Player 2' column + const dateCell = rows[i].getElementsByTagName('td')[9]; // The 'Date' column + + let playerMatch = true; + if (playerSearchValue) { + const player1Value = player1Cell.textContent || player1Cell.innerText; + const player2Value = player2Cell.textContent || player2Cell.innerText; + playerMatch = player1Value.toLowerCase().indexOf(playerSearchValue) > -1 || + player2Value.toLowerCase().indexOf(playerSearchValue) > -1; + } + + let dateMatch = true; + if (dateSearchValue) { + const dateValue = dateCell.textContent || dateCell.innerText; + dateMatch = dateValue.startsWith(dateSearchValue); + } + + if (playerMatch && dateMatch) { + rows[i].style.display = ''; + } else { + rows[i].style.display = 'none'; + } + } + } + + ////////////////////////////// END STAT SPE //////////////////////////////// + + + ////////////////////////////// BEG STARS //////////////////////////////// const starsContainer = document.getElementById('stars'); @@ -662,26 +718,6 @@ document.addEventListener('DOMContentLoaded', () => { ////////////////////////////// END STARS //////////////////////////////// - - /* btn.onclick = function() { - fetch('/web3/') - .then(response => response.json()) - .then(data => { - console.log('ok here !!'); - jsonContent.textContent = JSON.stringify(data, null, 2); - modal.style.display = "block"; - }); - } - - span.onclick = function() { - modal.style.display = "none"; - } - - window.onclick = function(event) { - if (event.target == modal) { - modal.style.display = "none"; - } - } */ ////////////////////////////// BEG LANGAGE //////////////////////////////// const translations = { diff --git a/pong/static/index.html b/pong/static/index.html index 426cc25..ed10b9d 100644 --- a/pong/static/index.html +++ b/pong/static/index.html @@ -128,6 +128,8 @@ + + + + diff --git a/pong/static/styles.css b/pong/static/styles.css index 6e601bc..36bd2dd 100644 --- a/pong/static/styles.css +++ b/pong/static/styles.css @@ -11,7 +11,7 @@ html { align-items: center; justify-content: center; height: 100%; - overflow: hidden; + overflow: auto; } @@ -255,6 +255,7 @@ button:hover { position: relative; z-index: 10; max-width: 80%; + overflow: auto; } @@ -439,4 +440,10 @@ body { color: rgb(0, 255, 255); /* Valeur par défaut */ font-family: Arial, sans-serif; font-size: 16px; +} + +canvas { + max-width: 100%; + height: auto; + margin-top: 20px; } \ No newline at end of file From dccbfd1601e65b3396fbbfabe37190e07b7c7e13 Mon Sep 17 00:00:00 2001 From: Theouche Date: Fri, 30 Aug 2024 15:41:59 +0200 Subject: [PATCH 09/14] logo hide when graph shown --- pong/static/burger.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pong/static/burger.js b/pong/static/burger.js index fc66d94..a98343c 100644 --- a/pong/static/burger.js +++ b/pong/static/burger.js @@ -27,6 +27,7 @@ document.addEventListener('DOMContentLoaded', () => { if (matchList) matchList.style.display = 'none'; if (tournoiList) tournoiList.style.display = 'none'; if (blockchainList) blockchainList.style.display = 'none'; + logo.style.display = 'block'; } const links = document.querySelectorAll('#dropdown-menu a'); @@ -41,7 +42,7 @@ document.addEventListener('DOMContentLoaded', () => { function showTable(tableId) { hideAllTables(); - + logo.style.display = 'none'; if (tableId === 'player-list') { playerList.style.display = 'block'; From c0634938a6fbc66dfe51c4688eaa454c3213b081 Mon Sep 17 00:00:00 2001 From: Theouche Date: Fri, 30 Aug 2024 18:23:04 +0200 Subject: [PATCH 10/14] bouton fonctionne --- pong/static/game.js | 60 ++++++++++++++++++++++++++++++++++++++++++ pong/static/index.html | 5 ++++ pong/static/styles.css | 26 ++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/pong/static/game.js b/pong/static/game.js index 0d6fe1b..f4e7216 100644 --- a/pong/static/game.js +++ b/pong/static/game.js @@ -33,6 +33,7 @@ document.addEventListener('DOMContentLoaded', () => { const pongElements = document.getElementById('pong-elements'); const logo = document.querySelector('.logo'); + const postFormButtons = document.getElementById('post-form-buttons'); const localGameButton = document.getElementById('local-game'); const quickMatchButton = document.getElementById('quick-match'); const tournamentButton = document.getElementById('tournament'); @@ -40,6 +41,7 @@ document.addEventListener('DOMContentLoaded', () => { let socket; let token; let gameState; + let saveData = null; // Auto-focus and key handling for AUTH-FORM nicknameInput.focus(); @@ -66,6 +68,7 @@ document.addEventListener('DOMContentLoaded', () => { async function handleCheckNickname() { const nickname = nicknameInput.value.trim(); if (nickname) { + window.firstPlayerName = nickname; try { const exists = await checkUserExists(nickname); if (exists) { @@ -316,6 +319,13 @@ document.addEventListener('DOMContentLoaded', () => { } function startLocalGame2() { + nickname = nicknameInput.value.trim(); + nickname2 = nicknameInput2.value.trim(); + saveData = { + type: 'local', + player1_name: nickname, + player2_name: nickname2 + }; gameContainer.style.display = 'flex'; logo.style.display = 'none'; pongElements.style.display = 'none'; @@ -324,10 +334,19 @@ document.addEventListener('DOMContentLoaded', () => { } function startQuickMatch() { + saveData = { + type: 'quick' + } gameContainer.style.display = 'flex'; logo.style.display = 'none'; pongElements.style.display = 'none'; formBlock.style.display = 'none'; + document.getElementById('player1-name').textContent = "player 1"; + document.getElementById('player2-name').textContent = "player 2"; + document.getElementById('game-text').textContent = ""; + document.getElementById('player1-score').textContent = 0; + document.getElementById('player2-score').textContent = 0; + startWebSocketConnection(token, 1); } @@ -404,6 +423,7 @@ document.addEventListener('DOMContentLoaded', () => { function updateGameState(newState) { gameState = newState; renderGame(); + checkForWinner(); } function renderGame() { @@ -434,4 +454,44 @@ document.addEventListener('DOMContentLoaded', () => { starsContainer.appendChild(star); } + const homeButton = document.getElementById('home'); + const replayButton = document.getElementById('retry'); + const gameControls = document.getElementById('game-controls'); + + homeButton.addEventListener('click', () => { + gameContainer.style.display = 'none'; + gameControls.style.display = 'none'; + + logo.style.display = 'block' + + formBlock.style.display = 'block'; + postFormButtons.style.display = 'flex'; + + setupFirstPlayer(); + }); + + function setupFirstPlayer() { + const firstPlayerName = window.firstPlayerName; + document.getElementById('player1-name').textContent = firstPlayerName; + } + + replayButton.addEventListener('click', () => { + document.getElementById('player1-name').textContent = saveData.player1_name; + document.getElementById('player2-name').textContent = saveData.player2_name; + startLocalGame2(); + }); + + + function checkForWinner() { + if (gameState.player1_score === 3 || gameState.player2_score === 3) { + gameControls.style.display = 'flex'; + homeButton.style.display = 'block'; + replayButton.style.display = 'none'; + console.log(saveData.type); + if (saveData.type === 'local'){ + replayButton.style.display = 'block'; + } + } + } + }); diff --git a/pong/static/index.html b/pong/static/index.html index 763222e..0cf0b7f 100644 --- a/pong/static/index.html +++ b/pong/static/index.html @@ -104,6 +104,11 @@ + +