import { Chess } from "chess.js"; import type { GamePlugin, GameResult, GameOverResult } from "../types"; import type { ChessState, ChessAction } from "./types"; function getPlayerColor(state: ChessState, playerId: string): "white" | "black" | null { if (state.players.white === playerId) return "white"; if (state.players.black === playerId) return "black"; return null; } function deriveStatus(game: Chess): ChessState["status"] { if (game.isCheckmate()) return "checkmate"; if (game.isStalemate()) return "stalemate"; if (game.isDraw()) return "draw"; return "playing"; } export const chessPlugin: GamePlugin = { slug: "chess", name: "Chess", minPlayers: 2, maxPlayers: 2, createInitialState(players: string[]): ChessState { const game = new Chess(); return { fen: game.fen(), players: { white: players[0]!, black: players[1]! }, moveHistory: [], status: "playing", winner: null, }; }, handleAction(state: ChessState, action: ChessAction, playerId: string): GameResult { if (state.status !== "playing") { return { ok: false, error: "Game is already over" }; } const solo = state.players.white === state.players.black; if (action.type === "forfeit") { if (solo) return { ok: true, state: { ...state, status: "forfeit", winner: null } }; const color = getPlayerColor(state, playerId); if (!color) return { ok: false, error: "You are not a player in this game" }; const winner = color === "white" ? state.players.black : state.players.white; return { ok: true, state: { ...state, status: "forfeit", winner } }; } if (action.type === "move") { const game = new Chess(state.fen); const turn = game.turn() === "w" ? "white" : "black"; if (!solo) { const playerColor = getPlayerColor(state, playerId); if (!playerColor) return { ok: false, error: "You are not a player in this game" }; if (playerColor !== turn) return { ok: false, error: "It is not your turn" }; } let move; try { move = game.move({ from: action.from, to: action.to, promotion: action.promotion ?? "q", }); } catch { return { ok: false, error: "Invalid move" }; } if (!move) return { ok: false, error: "Invalid move" }; const status = deriveStatus(game); const winner = status === "checkmate" ? (turn === "white" ? state.players.white : state.players.black) : null; return { ok: true, state: { ...state, fen: game.fen(), moveHistory: [...state.moveHistory, move.san], status, winner, }, }; } return { ok: false, error: "Unknown action type" }; }, getPlayerView(state: ChessState, playerId: string): ChessState { const playerColor = getPlayerColor(state, playerId); if (!playerColor) return state; return state; }, getSpectatorView(state: ChessState): ChessState { return state; }, isGameOver(state: ChessState): GameOverResult | null { if (state.status === "playing") return null; return { winner: state.winner, reason: state.status }; }, onPlayerDisconnect(state: ChessState, playerId: string): ChessState { if (state.players.white === state.players.black) { return { ...state, status: "forfeit", winner: null }; } const color = getPlayerColor(state, playerId); if (!color) return state; const winner = color === "white" ? state.players.black : state.players.white; return { ...state, status: "forfeit", winner }; }, };