# chess_engine/api/cli.py import chess import argparse import sys import os from typing import Optional, Dict, Any import time from chess_engine.api.game_controller import GameController, GameOptions from chess_engine.ai.stockfish_wrapper import DifficultyLevel class ChessCLI: """Command-line interface for chess engine""" def __init__(self): self.controller = None self.options = None def setup_game(self, args): """Setup game with command line arguments""" # Parse color player_color = chess.WHITE if args.color and args.color.lower() == 'black': player_color = chess.BLACK # Parse difficulty difficulty_map = { 'beginner': DifficultyLevel.BEGINNER, 'easy': DifficultyLevel.EASY, 'medium': DifficultyLevel.MEDIUM, 'hard': DifficultyLevel.HARD, 'expert': DifficultyLevel.EXPERT, 'master': DifficultyLevel.MASTER } difficulty = difficulty_map.get(args.difficulty.lower(), DifficultyLevel.MEDIUM) # Create options self.options = GameOptions( player_color=player_color, difficulty=difficulty, time_limit=args.time, use_opening_book=not args.no_book, enable_analysis=not args.no_analysis, stockfish_path=args.stockfish_path ) # Create controller self.controller = GameController(self.options) def print_board(self, unicode: bool = True): """Print chess board to console""" board = self.controller.board.board # Get board as string if unicode: board_str = str(board) else: board_str = board.unicode() print("\n" + board_str + "\n") # Print whose turn it is turn = "White" if board.turn == chess.WHITE else "Black" print(f"{turn} to move") # Print check status if board.is_check(): print("CHECK!") def print_analysis(self, analysis: Dict[str, Any]): """Print position analysis""" if not analysis: return eval_data = analysis.get("evaluation", {}) best_moves = analysis.get("best_moves", []) print("\nPosition Analysis:") print(f"Evaluation: {eval_data.get('total', 0):.2f} pawns") print(f"Stockfish: {eval_data.get('stockfish', 0):.2f} pawns") if best_moves: print("\nBest moves:") for i, (move, eval_score) in enumerate(best_moves[:3], 1): print(f"{i}. {move} ({eval_score:.2f})") def run(self): """Run the CLI game loop""" # Start new game result = self.controller.start_new_game() if result["status"] == "error": print(f"Error: {result['message']}") return print("Chess game started!") print("Enter moves in UCI notation (e.g., 'e2e4') or SAN (e.g., 'e4')") print("Commands: 'quit', 'help', 'hint', 'undo', 'board', 'resign'") self.print_board() # Game loop while True: # Get user input try: user_input = input("\nYour move: ").strip() except (KeyboardInterrupt, EOFError): print("\nGame terminated.") break # Process commands if user_input.lower() in ('quit', 'exit', 'q'): print("Game terminated.") break elif user_input.lower() == 'help': print("\nCommands:") print(" - Make a move (e.g., 'e2e4' or 'e4')") print(" board - Display the board") print(" hint - Get a move suggestion") print(" undo - Undo last move") print(" resign - Resign the game") print(" quit - Exit the game") continue elif user_input.lower() == 'board': self.print_board() continue elif user_input.lower() == 'hint': hint_result = self.controller.get_hint() if hint_result["status"] == "success": print(f"\nHint: {hint_result['hint']}") print(f"Explanation: {hint_result['explanation']}") else: print(f"Error: {hint_result['message']}") continue elif user_input.lower() == 'undo': undo_result = self.controller.undo_move() if undo_result["status"] in ("success", "partial"): print("Move undone.") self.print_board() else: print(f"Error: {undo_result['message']}") continue elif user_input.lower() == 'resign': resign_result = self.controller.resign() print("You resigned. Game over.") break # Process move move_result = self.controller.make_player_move(user_input) if move_result["status"] == "error": print(f"Error: {move_result['message']}") continue # Print board after move self.print_board() # Print AI's move if "ai_move" in move_result and move_result["ai_move"]: print(f"AI plays: {move_result['ai_move']}") # Print analysis if available if "analysis" in move_result and move_result["analysis"]: self.print_analysis(move_result["analysis"]) # Check if game is over if move_result["status"] == "game_over": result = move_result["result"] reason = move_result["reason"] print("\nGame over!") if result == "draw": print(f"Result: Draw ({reason})") else: winner = move_result["winner"].capitalize() print(f"Result: {winner} wins by {reason}") break # Clean up if self.controller: self.controller.close() def main(args=None): """ Main entry point for CLI Args: args: Command-line arguments (optional, parsed from sys.argv if None) """ if args is None: # Parse arguments only if not provided parser = argparse.ArgumentParser(description="Chess Engine CLI") parser.add_argument("--color", "-c", choices=["white", "black"], default="white", help="Player's color (default: white)") parser.add_argument("--difficulty", "-d", choices=["beginner", "easy", "medium", "hard", "expert", "master"], default="medium", help="AI difficulty level (default: medium)") parser.add_argument("--time", "-t", type=float, default=1.0, help="Time limit for AI moves in seconds (default: 1.0)") parser.add_argument("--stockfish-path", "-s", type=str, default=None, help="Path to Stockfish executable") parser.add_argument("--no-book", action="store_true", help="Disable opening book") parser.add_argument("--no-analysis", action="store_true", help="Disable position analysis") args = parser.parse_args() cli = ChessCLI() cli.setup_game(args) cli.run() if __name__ == "__main__": main()