Spaces:
Sleeping
Sleeping
| # 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(" <move> - 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() |