diff --git a/README.md b/README.md index 3dc6fe2..0cea568 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,86 @@ -# Game Instructions - -* Working on AI gamemode... - -- Entry point: main.py -- Press 't' to change theme (green, brown, blue, gray) -- Press 'r' to restart the game - -# Game Snapshots - -## Snapshot 1 - Start (green) -![snapshot1](snapshots/snapshot1.png) - -## Snapshot 2 - Start (brown) -![snapshot2](snapshots/snapshot2.png) - -## Snapshot 3 - Start (blue) -![snapshot3](snapshots/snapshot3.png) - -## Snapshot 4 - Start (gray) -![snapshot4](snapshots/snapshot4.png) - -## Snapshot 5 - Valid Moves -![snapshot5](snapshots/snapshot5.png) - -## Snapshot 6 - Castling -![snapshot6](snapshots/snapshot6.png) +# Python Chess Game with Stockfish AI + +A chess game implementation in Python using Pygame, featuring both basic AI and Stockfish engine integration. + +## Features +- Full chess game implementation with all standard rules +- Multiple themes (green, brown, blue, gray) +- Two AI modes: + - Basic AI (original implementation) + - Stockfish AI (professional chess engine) +- Visual move suggestions +- Move sound effects +- Drag and drop interface + +## Installation + +1. Clone the repository: +```bash +git clone https://github.com/YOUR_USERNAME/python-chess-ai-yt.git +cd python-chess-ai-yt +``` + +2. Install required packages: +```bash +pip install pygame python-chess stockfish +``` + +3. Download Stockfish: +- Download the latest version from https://stockfishchess.org/download/ +- Create a 'stockfish' folder in the project root +- Extract the Stockfish executable to the 'stockfish' folder +- Make sure the executable is named 'stockfish-windows-x86-64-avx2.exe' (Windows) or 'stockfish' (Linux/Mac) + +## How to Play + +1. Run the game: +```bash +python src/main.py +``` + +2. Controls: +- Click and drag pieces to move them +- Press 't' to change theme (green, brown, blue, gray) +- Press 'r' to restart the game + +3. AI Modes: +- Basic AI: Original implementation (easier) +- Stockfish AI: Professional chess engine (stronger) + - Configurable skill level (0-20) in stockfish_ai.py + +## Game Snapshots + +### Snapshot 1 - Start (green) +![snapshot1](snapshots/snapshot1.png) + +### Snapshot 2 - Start (brown) +![snapshot2](snapshots/snapshot2.png) + +### Snapshot 3 - Start (blue) +![snapshot3](snapshots/snapshot3.png) + +### Snapshot 4 - Start (gray) +![snapshot4](snapshots/snapshot4.png) + +## Technical Details + +- Built with Python and Pygame +- Chess logic implementation from scratch +- Stockfish integration using python-chess library +- Multiple AI difficulty levels +- FEN position conversion for Stockfish integration +- Visual feedback during AI calculations + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +## Credits + +- Original implementation based on YouTube tutorial +- Stockfish chess engine integration added by community +- Chess piece images and sound effects included in assets folder + +## License + +This project is open source and available under the MIT License. diff --git a/src/game.py b/src/game.py index cd86110..9140b96 100644 --- a/src/game.py +++ b/src/game.py @@ -5,6 +5,7 @@ from dragger import Dragger from config import Config from square import Square +from stockfish_ai import StockfishAI class Game: @@ -14,6 +15,8 @@ def __init__(self): self.board = Board() self.dragger = Dragger() self.config = Config() + self.ai = StockfishAI(self) + self.gamemode = 'ai' # 'ai' or 'pvp' # blit methods @@ -64,6 +67,14 @@ def show_pieces(self, surface): piece.texture_rect = img.get_rect(center=img_center) surface.blit(img, piece.texture_rect) + def show_thinking(self, surface): + if self.ai.thinking: + font = pygame.font.SysFont('Arial', 32) + text = font.render('Thinking...', True, (255, 255, 255)) + text_rect = text.get_rect(center=(WIDTH // 2, HEIGHT // 2)) + pygame.draw.rect(surface, (0, 0, 0), text_rect.inflate(20, 20)) + surface.blit(text, text_rect) + def show_moves(self, surface): theme = self.config.theme @@ -107,6 +118,19 @@ def show_hover(self, surface): def next_turn(self): self.next_player = 'white' if self.next_player == 'black' else 'black' + # If it's AI's turn (black), make AI move + if self.gamemode == 'ai' and self.next_player == 'black': + ai_move = self.ai.get_ai_move() + if ai_move: + initial = ai_move.initial + final = ai_move.final + piece = self.board.squares[initial.row][initial.col].piece + # Make the move + self.board.move(piece, ai_move) + # Play move sound + self.play_sound(self.board.squares[final.row][final.col].has_piece()) + # Change turn + self.next_turn() def set_hover(self, row, col): self.hovered_sqr = self.board.squares[row][col] diff --git a/src/main.py b/src/main.py index 96d20b2..a9df03e 100644 --- a/src/main.py +++ b/src/main.py @@ -28,6 +28,7 @@ def mainloop(self): game.show_moves(screen) game.show_pieces(screen) game.show_hover(screen) + game.show_thinking(screen) if dragger.dragging: dragger.update_blit(screen) diff --git a/src/stockfish_ai.py b/src/stockfish_ai.py new file mode 100644 index 0000000..56d387b --- /dev/null +++ b/src/stockfish_ai.py @@ -0,0 +1,116 @@ +import chess +import chess.engine +import os +from const import * +from move import Move +from square import Square + +class StockfishAI: + def __init__(self, game, depth=10): + self.game = game + self.board = game.board + self.thinking = False + self.depth = depth + # Путь к исполняемому файлу Stockfish + stockfish_path = self._get_stockfish_path() + try: + self.engine = chess.engine.SimpleEngine.popen_uci(stockfish_path) + self.engine.configure({"Skill Level": 20}) # Максимальный уровень мастерства + except Exception as e: + print(f"Error initializing Stockfish: {e}") + self.engine = None + + def _get_stockfish_path(self): + """Get the path to Stockfish executable based on OS""" + if os.name == 'nt': # Windows + return os.path.join(os.path.dirname(os.path.dirname(__file__)), "stockfish", "stockfish-windows-x86-64-avx2.exe") + elif os.name == 'posix': # Linux/Mac + return os.path.join(os.path.dirname(os.path.dirname(__file__)), "stockfish", "stockfish") + else: + raise Exception("Unsupported operating system") + + def get_ai_move(self): + """Get best move from Stockfish""" + if not self.engine: + return None + + self.thinking = True + try: + # Convert our board to chess.Board + fen = self._convert_to_fen() + chess_board = chess.Board(fen) + + # Get move from Stockfish + result = self.engine.play(chess_board, chess.engine.Limit(depth=self.depth)) + if result.move: + # Convert chess.Move to our Move format + move = self._convert_move(result.move) + self.thinking = False + return move + except Exception as e: + print(f"Error getting move from Stockfish: {e}") + + self.thinking = False + return None + + def _convert_to_fen(self): + """Convert our board representation to FEN string""" + fen = [] + empty = 0 + + # Board position + for row in range(ROWS): + for col in range(COLS): + square = self.board.squares[row][col] + if square.has_piece(): + if empty > 0: + fen.append(str(empty)) + empty = 0 + piece = square.piece + symbol = piece.name[0] + if piece.name == 'knight': + symbol = 'n' + if piece.color == 'white': + symbol = symbol.upper() + fen.append(symbol) + else: + empty += 1 + if empty > 0: + fen.append(str(empty)) + empty = 0 + if row < 7: + fen.append('/') + + # Active color + fen.append(' w ' if self.game.next_player == 'white' else ' b ') + + # Castling availability (simplified) + fen.append('KQkq ') + + # En passant target square (simplified) + fen.append('- ') + + # Halfmove clock and fullmove number (simplified) + fen.append('0 1') + + return ''.join(fen) + + def _convert_move(self, chess_move): + """Convert chess.Move to our Move format""" + from_square = chess_move.from_square + to_square = chess_move.to_square + + from_row = 7 - (from_square // 8) + from_col = from_square % 8 + to_row = 7 - (to_square // 8) + to_col = to_square % 8 + + initial = Square(from_row, from_col) + final = Square(to_row, to_col) + + return Move(initial, final) + + def quit(self): + """Properly close the Stockfish engine""" + if self.engine: + self.engine.quit()