| | import chess
|
| | import chess.engine
|
| | import os
|
| | import threading
|
| |
|
| | class EngineHandler:
|
| | def __init__(self, engine_path="stockfish.exe"):
|
| | self.engine_path = engine_path
|
| | self.engine = None
|
| | self.lock = threading.Lock()
|
| |
|
| | def initialize_engine(self):
|
| |
|
| | if not os.path.exists(self.engine_path):
|
| | return False, f"Engine not found at {self.engine_path}. Please place 'stockfish.exe' in the project folder."
|
| |
|
| |
|
| | final_path = self.engine_path
|
| | if os.path.isdir(self.engine_path):
|
| | found_exe = None
|
| | for root, dirs, files in os.walk(self.engine_path):
|
| | for file in files:
|
| | if "stockfish" in file.lower() and file.lower().endswith(".exe"):
|
| | found_exe = os.path.join(root, file)
|
| | break
|
| | if found_exe:
|
| | break
|
| |
|
| | if found_exe:
|
| | final_path = found_exe
|
| | else:
|
| | return False, f"Directory found at {self.engine_path}, but no 'stockfish' executable was found inside."
|
| |
|
| | try:
|
| | self.engine = chess.engine.SimpleEngine.popen_uci(final_path)
|
| | return True, f"Engine initialized successfully ({os.path.basename(final_path)})."
|
| | except PermissionError:
|
| | return False, f"Permission denied accessing {self.engine_path}. Try running as Administrator or check file properties."
|
| | except Exception as e:
|
| | return False, f"Failed to initialize engine: {e}"
|
| |
|
| | def get_best_move(self, fen, time_limit=1.0):
|
| | if not self.engine:
|
| | return None
|
| |
|
| | with self.lock:
|
| | try:
|
| | board = chess.Board(fen)
|
| | result = self.engine.play(board, chess.engine.Limit(time=time_limit))
|
| | return result.move
|
| | except Exception as e:
|
| | import traceback
|
| | traceback.print_exc()
|
| | print(f"Error getting best move: {e!r}")
|
| |
|
| | self._try_reinit()
|
| | return None
|
| |
|
| | def _try_reinit(self):
|
| | """Attempt to reinitialize the engine if it crashed."""
|
| | try:
|
| | if self.engine:
|
| | try:
|
| | self.engine.quit()
|
| | except:
|
| | pass
|
| | self.engine = None
|
| | self.initialize_engine()
|
| | except:
|
| | pass
|
| |
|
| | def get_top_moves(self, fen, limit=3, time_limit=1.0):
|
| | if not self.engine:
|
| | return []
|
| |
|
| | with self.lock:
|
| | try:
|
| | board = chess.Board(fen)
|
| | info = self.engine.analyse(board, chess.engine.Limit(time=time_limit), multipv=limit)
|
| |
|
| | if isinstance(info, dict):
|
| | info = [info]
|
| |
|
| | top_moves = []
|
| | for i, line in enumerate(info):
|
| | if "pv" in line:
|
| | move = line["pv"][0]
|
| | score = line["score"].relative.score(mate_score=10000)
|
| | top_moves.append({
|
| | "rank": i + 1,
|
| | "move": move,
|
| | "score": score,
|
| | "pv": line["pv"]
|
| | })
|
| | return top_moves
|
| |
|
| | except Exception as e:
|
| | print(f"Error analyzing: {e}")
|
| | self._try_reinit()
|
| | return []
|
| |
|
| | def quit(self):
|
| | if self.engine:
|
| | try:
|
| | self.engine.quit()
|
| | except:
|
| | pass
|
| |
|
| | def get_evaluation(self, fen):
|
| | if not self.engine:
|
| | return None
|
| |
|
| | with self.lock:
|
| | try:
|
| | board = chess.Board(fen)
|
| | info = self.engine.analyse(board, chess.engine.Limit(depth=15))
|
| | return info["score"].relative.score(mate_score=10000)
|
| | except Exception as e:
|
| | print(f"Error in evaluation: {e}")
|
| | return None
|
| |
|