import type { DuplicateGame } from './game'; import type { GameStatus, GameResult, BoardId } from './types'; import { PLAYERS, PLAYER_BOARDS, BOARD_PLAYERS } from './boards'; import { legalSyncedMoves } from './legality'; /** PROVISIONAL (spec §6): the 50-move rule fires after this many rounds. */ const FIFTY_MOVE_ROUNDS = 50; const FIFTY_MOVE_PLIES = FIFTY_MOVE_ROUNDS * 4; function allDraw(): GameResult { return { N: 'draw', S: 'draw', E: 'draw', W: 'draw' }; } /** Evaluate the game from the perspective of the player to move. */ export function evaluateStatus(game: DuplicateGame): GameStatus { const player = game.currentPlayer; const [a, b] = PLAYER_BOARDS[player]; const checks: BoardId[] = []; if (game.boards[a].inCheck()) checks.push(a); if (game.boards[b].inCheck()) checks.push(b); const synced = legalSyncedMoves(game); if (synced.length === 0) { if (checks.length > 0) { // Checkmate. PROVISIONAL (spec §6): every opponent delivering a check wins. const winners = checks.map((board) => BOARD_PLAYERS[board].w === player ? BOARD_PLAYERS[board].b : BOARD_PLAYERS[board].w, ); const result = {} as GameResult; for (const p of PLAYERS) { result[p] = p === player ? 'loss' : winners.includes(p) ? 'win' : 'draw'; } return { state: 'checkmate', result, checks }; } // PROVISIONAL (spec §6): a no-synchronized-move stalemate ends the game, all draw. return { state: 'stalemate', result: allDraw(), reason: 'stalemate', checks }; } if (game.repetitionCount() >= 3) { return { state: 'draw', result: allDraw(), reason: 'threefold', checks }; } if (game.pliesSinceProgress >= FIFTY_MOVE_PLIES) { return { state: 'draw', result: allDraw(), reason: 'fifty-move', checks }; } return { state: 'playing', checks }; }