# ====================================================== # ChessPositionSolverTool # • Uses chessvision.ai’s open-source detector to turn a board image into FEN # • Feeds that FEN to Stockfish (via python-chess) and returns best move in # short algebraic notation (e.g. “Qh5#”) # • Requires: # pip install chessvision python-chess opencv-python # and Stockfish binary in PATH (adjust `STOCKFISH_PATH` if needed) # ====================================================== import requests, cv2, numpy as np, os, subprocess, json, tempfile from pydantic import BaseModel, Field from langchain_core.tools import tool import chess, chess.engine from chesscog import Chesscog # <- pip install chesscog STOCKFISH_PATH = os.getenv("STOCKFISH_PATH", "stockfish") # apt-get install stockfish class ChessInput(BaseModel): image_url: str = Field(..., description="PNG/JPG of the chess position") stockfish_depth: int = Field(18, ge=10, le=30, description="Search depth for Stockfish") @tool(args_schema=ChessInput) def chess_position_solver(image_url: str, stockfish_depth: int = 18) -> str: """ Converts a chessboard image to FEN with chesscog, sends it to Stockfish, and returns the best move in algebraic notation (e.g. 'Rxf2+'). """ try: # 1 - download image img_bytes = requests.get(image_url, timeout=30).content img = cv2.imdecode(np.frombuffer(img_bytes, np.uint8), cv2.IMREAD_COLOR) # 2 - infer FEN with chesscog detector = Chesscog(device="cpu") # auto-downloads model weights fen = detector.get_fen(img) if fen is None: return "chess_position_solver failed: board not recognised." board = chess.Board(fen) # 3 - Stockfish engine = chess.engine.SimpleEngine.popen_uci(STOCKFISH_PATH) result = engine.play(board, chess.engine.Limit(depth=stockfish_depth)) engine.quit() best_move_san = board.san(result.move) # algebraic, e.g. 'Qh5#' return best_move_san except Exception as e: return f"chess_position_solver failed: {e}"