Make game fully playable with two players

This commit is contained in:
sepia 2025-07-15 21:23:16 -05:00
parent 8b7d40b6f8
commit d1dbebcc39
2 changed files with 47 additions and 37 deletions

View File

@ -26,6 +26,8 @@ let playerId: string; // Declare playerId here, accessible throughout the module
const gameStateManager = new GameStateManager(); const gameStateManager = new GameStateManager();
const wsClient = new WebSocketClient(WS_URL); const wsClient = new WebSocketClient(WS_URL);
let gameBoardUI: GameBoardUI;
const gameBoardElement = document.getElementById('game-board'); const gameBoardElement = document.getElementById('game-board');
console.log('gameBoardElement: ', gameBoardElement); // Log to check if element is found console.log('gameBoardElement: ', gameBoardElement); // Log to check if element is found
@ -40,13 +42,6 @@ if (!gameBoardElement || !messagesElement || !playerInfoElement) {
'Missing essential DOM elements (game-board, messages, or player-info)', 'Missing essential DOM elements (game-board, messages, or player-info)',
); );
} }
const gameBoardUI = new GameBoardUI(gameBoardElement);
console.log('GameBoardUI initialized.', gameBoardUI); // Log to confirm GameBoardUI construction
// --- Event Handlers and Wiring ---
// WebSocketClient -> GameStateManager -> GameBoardUI
wsClient.onMessage((message) => { wsClient.onMessage((message) => {
try { try {
const msg = JSON.parse(message); const msg = JSON.parse(message);
@ -95,35 +90,22 @@ wsClient.onMessage((message) => {
}); });
// GameBoardUI -> WebSocketClient (for making moves) // GameBoardUI -> WebSocketClient (for making moves)
gameBoardUI.setOnCellClick((row, col) => { // This will be set up inside wsClient.onOpen
const moveMessage = {
type: 'make_move',
row: row,
col: col,
};
console.log('Sending move:', moveMessage);
wsClient.send(JSON.stringify(moveMessage));
// Optimistic Update: Apply the move to local state immediately // Initial board render (empty board until server sends state)
const currentGameState = gameStateManager.getGameState(); // This initial render is no longer needed as updateBoard is called within onOpen and onMessage
const nextPlayer =
currentGameState.currentPlayer === 'black' ? 'white' : 'black';
const newBoard = currentGameState.board.map((rowArr) => [...rowArr]); // Deep copy board
newBoard[row][col] = currentGameState.currentPlayer; // Place stone optimistically
const optimisticState: GameStateType = { // Initial setup for player info
...currentGameState, if (playerInfoElement) {
board: newBoard, playerInfoElement.textContent = `You are: (Connecting...)`;
currentPlayer: nextPlayer, // Optimistically switch turn }
};
gameStateManager.updateGameState(optimisticState);
gameBoardUI.updateBoard(gameStateManager.getGameState());
});
// WebSocketClient connection status messages
wsClient.onOpen(() => { wsClient.onOpen(() => {
console.log('Connected to game server.'); console.log('Connected to game server.');
playerId = `player-${Math.random().toString(36).substring(2, 9)}`; playerId = `player-${Math.random().toString(36).substring(2, 9)}`;
gameBoardUI = new GameBoardUI(gameBoardElement, playerId);
console.log('GameBoardUI initialized.', gameBoardUI); // Log to confirm GameBoardUI construction
const joinMessage: any = { const joinMessage: any = {
type: 'join_game', type: 'join_game',
playerId: playerId, playerId: playerId,
@ -136,6 +118,34 @@ wsClient.onOpen(() => {
if (playerInfoElement) { if (playerInfoElement) {
playerInfoElement.textContent = `You are: ${playerId} (Waiting for game state...)`; playerInfoElement.textContent = `You are: ${playerId} (Waiting for game state...)`;
} }
// Initial board render (empty board until server sends state)
gameBoardUI.updateBoard(gameStateManager.getGameState());
gameBoardUI.setOnCellClick((row, col) => {
const moveMessage = {
type: 'make_move',
row: row,
col: col,
};
console.log('Sending move:', moveMessage);
wsClient.send(JSON.stringify(moveMessage));
// Optimistic Update: Apply the move to local state immediately
const currentGameState = gameStateManager.getGameState();
const nextPlayer =
currentGameState.currentPlayer === 'black' ? 'white' : 'black';
const newBoard = currentGameState.board.map((rowArr) => [...rowArr]); // Deep copy board
newBoard[row][col] = currentGameState.currentPlayer; // Place stone optimistically
const optimisticState: GameStateType = {
...currentGameState,
board: newBoard,
currentPlayer: nextPlayer, // Optimistically switch turn
};
gameStateManager.updateGameState(optimisticState);
gameBoardUI.updateBoard(gameStateManager.getGameState());
});
}); });
wsClient.onClose(() => { wsClient.onClose(() => {
@ -151,8 +161,6 @@ wsClient.onError((error: Event) => {
// --- Start Connection --- // --- Start Connection ---
wsClient.connect(); wsClient.connect();
// Initial board render (empty board until server sends state)
gameBoardUI.updateBoard(gameStateManager.getGameState());
// Initial setup for player info // Initial setup for player info
if (playerInfoElement) { if (playerInfoElement) {
playerInfoElement.textContent = `You are: (Connecting...)`; playerInfoElement.textContent = `You are: (Connecting...)`;

View File

@ -8,9 +8,11 @@ export class GameBoardUI {
private onCellClickCallback: ((row: number, col: number) => void) | null = private onCellClickCallback: ((row: number, col: number) => void) | null =
null; null;
private isInteractionEnabled: boolean = true; private isInteractionEnabled: boolean = true;
private thisClientPlayerId: string;
constructor(boardElement: HTMLElement) { constructor(boardElement: HTMLElement, thisClientPlayerId: string) {
this.boardElement = boardElement; this.boardElement = boardElement;
this.thisClientPlayerId = thisClientPlayerId;
this.initializeBoard(); this.initializeBoard();
} }
@ -47,6 +49,8 @@ export class GameBoardUI {
const board = gameState.board; const board = gameState.board;
const lastMove = { row: -1, col: -1 }; // Placeholder for last move highlighting (needs actual last move from state) const lastMove = { row: -1, col: -1 }; // Placeholder for last move highlighting (needs actual last move from state)
const thisClientColor = Object.entries(gameState.players).find(([color, id]) => id === this.thisClientPlayerId)?.[0] || null;
for (let row = 0; row < 15; row++) { for (let row = 0; row < 15; row++) {
for (let col = 0; col < 15; col++) { for (let col = 0; col < 15; col++) {
const cell = this.cells[row][col]; const cell = this.cells[row][col];
@ -76,10 +80,8 @@ export class GameBoardUI {
// Disable interaction if it's not our turn or game is over // Disable interaction if it's not our turn or game is over
// This logic needs to know which player 'we' are, and the current player from gameState // This logic needs to know which player 'we' are, and the current player from gameState
// For simplicity, let's assume 'black' is the client for now, and enable/disable
// based on if it's black's turn. This will need refinement for multi-player.
this.isInteractionEnabled = this.isInteractionEnabled =
gameState.status === 'playing' && gameState.currentPlayer === 'black'; // Simplified for now gameState.status === 'playing' && gameState.currentPlayer === (thisClientColor as 'black' | 'white');
this.boardElement.style.pointerEvents = this.isInteractionEnabled this.boardElement.style.pointerEvents = this.isInteractionEnabled
? 'auto' ? 'auto'
: 'none'; : 'none';