"""Deterministic chess calculations after a board has been transcribed to FEN.""" from __future__ import annotations import shutil def resolve_stockfish(stockfish_path: str | None = None) -> str: """Resolve Stockfish or fail closed so the model never guesses a move.""" resolved = stockfish_path or shutil.which("stockfish") if not resolved: raise RuntimeError( "Stockfish is required for chess answers. Set STOCKFISH_PATH or install stockfish." ) return resolved def best_move_san(fen: str, stockfish_path: str | None = None, depth: int = 18) -> str: import chess import chess.engine board = chess.Board(fen) with chess.engine.SimpleEngine.popen_uci( resolve_stockfish(stockfish_path) ) as engine: move = engine.play(board, chess.engine.Limit(depth=depth)).move return board.san(move) def analyze_chess_fen( fen: str, stockfish_path: str | None = None, depth: int = 18 ) -> str: """Return legal checks/mates and an optional engine best move in SAN.""" import chess board = chess.Board(fen) checks: list[str] = [] mates: list[str] = [] for move in board.legal_moves: san = board.san(move) board.push(move) if board.is_checkmate(): mates.append(san) elif board.is_check(): checks.append(san) board.pop() best = None if stockfish_path or shutil.which("stockfish"): best = best_move_san(fen, stockfish_path, depth) return f"FEN: {fen}\nLegal moves: {board.legal_moves.count()}\nChecks: {checks}\nMates in one: {mates}\nEngine best SAN: {best}"