From 298318a0fdea238aac95078d1b95c6084734b145 Mon Sep 17 00:00:00 2001 From: Stephanie Gredell Date: Mon, 8 Dec 2025 21:41:35 -0800 Subject: [PATCH] rematch if draw --- backend/src/services/game.service.ts | 29 ++++++------------- backend/src/services/websocket.service.ts | 34 ++++++++++++++++++++++- frontend/src/pages/TicTacToeApp.tsx | 32 ++++----------------- 3 files changed, 47 insertions(+), 48 deletions(-) diff --git a/backend/src/services/game.service.ts b/backend/src/services/game.service.ts index 7add06c..2f0b480 100644 --- a/backend/src/services/game.service.ts +++ b/backend/src/services/game.service.ts @@ -53,7 +53,7 @@ function checkDraw(board: (string | null)[]): boolean { return board.every(cell => cell !== null) && !checkWinner(board); } -function makeMove(roomId: string, playerId: string, position: number): { success: boolean; error?: string; autoStartNext?: boolean } { +function makeMove(roomId: string, playerId: string, position: number): { success: boolean; error?: string; autoStartNext?: boolean; autoRematch?: boolean } { const game = games.get(roomId); if (!game) { return { success: false, error: 'Game not found' }; @@ -88,11 +88,13 @@ function makeMove(roomId: string, playerId: string, position: number): { success // After broadcast, if there's a queue, we'll auto-start the next game } else if (isDraw) { game.isDraw = true; + // Auto-rematch on draw - same two players play again + // Note: The websocket handler will reset the board and keep same players } else { game.currentPlayer = game.currentPlayer === 'X' ? 'O' : 'X'; } - return { success: true, autoStartNext: winner !== null && game.queue.length > 0 }; + return { success: true, autoStartNext: winner !== null && game.queue.length > 0, autoRematch: isDraw }; } function addPlayer(roomId: string, playerId: string, ws: WS): { success: boolean; error?: string; symbol?: 'X' | 'O' | null } { @@ -196,25 +198,10 @@ function resetGame(roomId: string, resettingPlayerId?: string): void { nextPlayer.symbol = previousWinner === 'X' ? 'O' : 'X'; } } - } else if (wasDraw && hasQueue && activePlayers.length === 2) { - // If it was a draw and there's a queue, both players go to queue - activePlayers.forEach(p => { - p.symbol = null; - game.queue.push(p.id); - }); - - // Promote next 2 from queue - const newPlayer1 = game.queue.shift(); - const newPlayer2 = game.queue.shift(); - - if (newPlayer1) { - const p1 = game.players.find(p => p.id === newPlayer1); - if (p1) p1.symbol = 'X'; - } - if (newPlayer2) { - const p2 = game.players.find(p => p.id === newPlayer2); - if (p2) p2.symbol = 'O'; - } + } else if (wasDraw && activePlayers.length === 2) { + // If it was a draw, rematch - same two players play again + // Players keep their symbols, board is already reset above + // No changes needed - just reset the board (already done above) } else if (activePlayers.length > 2) { // If no winner or no queue, rotate all players activePlayers.forEach(p => { diff --git a/backend/src/services/websocket.service.ts b/backend/src/services/websocket.service.ts index e2274c8..fccd327 100644 --- a/backend/src/services/websocket.service.ts +++ b/backend/src/services/websocket.service.ts @@ -101,8 +101,40 @@ export function createWebSocketServer(server: any) { } }); + // Auto-rematch on draw - same two players play again + if (moveResult.autoRematch && updatedGame.isDraw) { + // Reset board, keep same players and symbols + updatedGame.board = Array(9).fill(null); + updatedGame.currentPlayer = 'X'; + updatedGame.winner = null; + updatedGame.isDraw = false; + // Players keep their symbols, so no change needed + + // Broadcast new game state immediately + setTimeout(() => { + const rematchGame = getGame(roomId); + if (rematchGame) { + rematchGame.players.forEach(player => { + if (player.ws.readyState === 1) { + player.ws.send(JSON.stringify({ + type: 'gameState', + game: { + board: rematchGame.board, + currentPlayer: rematchGame.currentPlayer, + winner: rematchGame.winner, + isDraw: rematchGame.isDraw, + yourSymbol: player.symbol, + players: rematchGame.players.map(p => ({ id: p.id, symbol: p.symbol })), + queue: rematchGame.queue, + }, + })); + } + }); + } + }, 1500); // Small delay to show draw message before rematch + } // Auto-start next game if there's a winner and queue - if (moveResult.autoStartNext && updatedGame.winner && updatedGame.queue.length > 0) { + else if (moveResult.autoStartNext && updatedGame.winner && updatedGame.queue.length > 0) { const winner = updatedGame.winner; const loser = updatedGame.players.find(p => p.symbol !== winner && p.symbol !== null); diff --git a/frontend/src/pages/TicTacToeApp.tsx b/frontend/src/pages/TicTacToeApp.tsx index fa9b1b6..f9facb4 100644 --- a/frontend/src/pages/TicTacToeApp.tsx +++ b/frontend/src/pages/TicTacToeApp.tsx @@ -255,33 +255,13 @@ export function TicTacToeApp() { } } - // Draw: if there's a queue, show "Get in line", otherwise "Play Again" + // Draw: rematch happens automatically, show message if (gameState.isDraw) { - if (hasQueue && !isInQueue) { - return ( - - ); - } else if (!hasQueue) { - return ( - - ); - } else { - return ( -

- You're in line to play again (position {gameState.queue.indexOf(playerIdRef.current) + 1}) -

- ); - } + return ( +

+ It's a draw! Rematch starting... +

+ ); } return null;