Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 86 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.
24 changes: 24 additions & 0 deletions src/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from dragger import Dragger
from config import Config
from square import Square
from stockfish_ai import StockfishAI

class Game:

Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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]
Expand Down
1 change: 1 addition & 0 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
116 changes: 116 additions & 0 deletions src/stockfish_ai.py
Original file line number Diff line number Diff line change
@@ -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()