import { v4 as uuidv4 } from 'uuid'; type PlayerColor = 'black' | 'white'; type GameStatus = 'waiting' | 'playing' | 'finished'; type BoardCell = null | 'black' | 'white'; export class GameInstance { public readonly id: string; public readonly board: BoardCell[][]; public currentPlayer: PlayerColor | null; public status: GameStatus; public winner: null | PlayerColor | 'draw'; public players: { black?: string; white?: string }; private readonly boardSize = 15; private moveCount = 0; constructor(id?: string) { this.id = id || uuidv4(); this.board = Array.from({ length: this.boardSize }, () => Array(this.boardSize).fill(null), ); this.currentPlayer = null; this.status = 'waiting'; this.winner = null; this.players = {}; } public getPlayerCount(): number { return Object.values(this.players).filter(Boolean).length; } public addPlayer(playerId: string): boolean { // If game is full, prevent new players from joining. if (this.getPlayerCount() >= 2) { return false; } // If player is already in the game, return true. if (Object.values(this.players).includes(playerId)) { return true; } // Assign black if available, otherwise white if (!this.players.black) { this.players.black = playerId; } else if (!this.players.white) { this.players.white = playerId; } else { return false; // Should not happen if getPlayerCount() check is correct } // If both players have joined, start the game. if (this.players.black && this.players.white) { this.currentPlayer = 'black'; this.status = 'playing'; } return true; } public makeMove( playerId: string, row: number, col: number, ): { success: boolean; error?: string } { // Find player's color let playerColor: PlayerColor | null = null; for (const [color, id] of Object.entries(this.players)) { if (id === playerId) { playerColor = color as PlayerColor; break; } } if (!playerColor) { return { success: false, error: 'Player not in this game' }; } // Validate it's the player's turn if (this.currentPlayer !== playerColor) { return { success: false, error: 'Not your turn' }; } // Validate move is within bounds if (row < 0 || row >= this.boardSize || col < 0 || col >= this.boardSize) { return { success: false, error: 'Move out of bounds' }; } // Validate cell is empty if (this.board[row][col] !== null) { return { success: false, error: 'Cell already occupied' }; } // Make the move this.board[row][col] = playerColor; this.moveCount++; // Check for win condition if (this.checkWin(row, col, playerColor)) { this.winner = playerColor; this.status = 'finished'; this.currentPlayer = null; return { success: true }; } // Check for draw condition if (this.moveCount === this.boardSize * this.boardSize) { this.winner = 'draw'; this.status = 'finished'; this.currentPlayer = null; return { success: true }; } // Switch turns this.currentPlayer = playerColor === 'black' ? 'white' : 'black'; return { success: true }; } private checkWin(row: number, col: number, color: PlayerColor): boolean { const directions = [ [1, 0], // vertical [0, 1], // horizontal [1, 1], // diagonal down-right [1, -1], // diagonal down-left ]; for (const [dx, dy] of directions) { let count = 1; // Check in positive direction for (let i = 1; i < 5; i++) { const newRow = row + dx * i; const newCol = col + dy * i; if ( newRow < 0 || newRow >= this.boardSize || newCol < 0 || newCol >= this.boardSize ) { break; } if (this.board[newRow][newCol] === color) { count++; } else { break; } } // Check in negative direction for (let i = 1; i < 5; i++) { const newRow = row - dx * i; const newCol = col - dy * i; if ( newRow < 0 || newRow >= this.boardSize || newCol < 0 || newCol >= this.boardSize ) { break; } if (this.board[newRow][newCol] === color) { count++; } else { break; } } if (count >= 5) { return true; } } return false; } }