-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e99bf21
commit 1b90f6e
Showing
6 changed files
with
277 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
venv/ | ||
__pycache__/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# board.py | ||
import chess | ||
|
||
class ChessBoard: | ||
def __init__(self): | ||
self.board = chess.Board() | ||
|
||
def get_legal_moves(self): | ||
"""Returns a list of legal moves in the current position.""" | ||
return list(self.board.legal_moves) | ||
|
||
def make_move(self, move): | ||
""" | ||
Attempts to make a move on the board. | ||
Returns True if successful, False if illegal. | ||
""" | ||
try: | ||
if move in self.board.legal_moves: | ||
self.board.push(move) | ||
return True | ||
return False | ||
except: | ||
return False | ||
|
||
def is_game_over(self): | ||
"""Returns True if the game is over.""" | ||
return self.board.is_game_over() | ||
|
||
def get_board_state(self): | ||
"""Returns the current board state.""" | ||
return self.board | ||
|
||
def get_result(self): | ||
"""Returns the game result if the game is over.""" | ||
if self.is_game_over(): | ||
return self.board.outcome() | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import chess | ||
from board import ChessBoard | ||
|
||
class ChessBot: | ||
def __init__(self): | ||
pass | ||
|
||
def get_move(self, board: ChessBoard): | ||
""" | ||
Given the current board state, returns the chosen move. | ||
This is the main function students will implement. | ||
""" | ||
# For now, just return the first legal move | ||
legal_moves = board.get_legal_moves() | ||
if legal_moves: | ||
return legal_moves[0] | ||
return None |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
# game.py | ||
import chess | ||
import chess.svg | ||
from board import ChessBoard | ||
from bot import ChessBot | ||
from human import HumanPlayer | ||
import pygame | ||
import cairosvg | ||
import io | ||
from PIL import Image | ||
|
||
IS_BOT = False # Set to False for human vs bot, True for bot vs bot | ||
|
||
class ChessGame: | ||
def __init__(self): | ||
self.board = ChessBoard() | ||
|
||
# Initialize players based on IS_BOT flag | ||
if IS_BOT: | ||
self.white_player = ChessBot() | ||
self.black_player = ChessBot() | ||
else: | ||
self.white_player = HumanPlayer(chess.WHITE) | ||
self.black_player = ChessBot() | ||
|
||
# Initialize Pygame | ||
pygame.init() | ||
self.WINDOW_SIZE = 600 | ||
self.screen = pygame.display.set_mode((self.WINDOW_SIZE, self.WINDOW_SIZE)) | ||
pygame.display.set_caption("Chess Game") | ||
|
||
def svg_to_pygame_surface(self, svg_string): | ||
"""Convert SVG string to Pygame surface""" | ||
png_data = cairosvg.svg2png(bytestring=svg_string.encode('utf-8')) | ||
image = Image.open(io.BytesIO(png_data)) | ||
image = image.resize((self.WINDOW_SIZE, self.WINDOW_SIZE)) | ||
mode = image.mode | ||
size = image.size | ||
data = image.tobytes() | ||
return pygame.image.fromstring(data, size, mode) | ||
|
||
def display_board(self, last_move=None): | ||
"""Display the current board state""" | ||
# Create SVG with highlighted last move if exists | ||
svg = chess.svg.board( | ||
board=self.board.get_board_state(), | ||
lastmove=last_move, | ||
size=self.WINDOW_SIZE | ||
) | ||
|
||
# Convert SVG to Pygame surface and display | ||
py_image = self.svg_to_pygame_surface(svg) | ||
self.screen.blit(py_image, (0, 0)) | ||
pygame.display.flip() | ||
|
||
def play_game(self): | ||
"""Main game loop""" | ||
last_move = None | ||
|
||
while not self.board.is_game_over(): | ||
# Display current board | ||
self.display_board(last_move) | ||
|
||
# Determine current player | ||
current_player = self.white_player if self.board.get_board_state().turn else self.black_player | ||
|
||
# Get player's move | ||
move = current_player.get_move(self.board) | ||
|
||
if move is None: | ||
print("Game ended by player") | ||
break | ||
|
||
# Make the move | ||
if not self.board.make_move(move): | ||
print(f"Illegal move attempted: {move}") | ||
break | ||
|
||
print(f"Move played: {move}") | ||
last_move = move | ||
|
||
# Add delay only for bot moves | ||
if isinstance(current_player, ChessBot): | ||
pygame.time.wait(1000) # 1 second delay | ||
|
||
# Display final position | ||
self.display_board(last_move) | ||
result = self.board.get_result() | ||
print(f"Game Over! Result: {result}") | ||
|
||
# Keep window open until closed | ||
while True: | ||
event = pygame.event.wait() | ||
if event.type == pygame.QUIT: | ||
break | ||
|
||
pygame.quit() | ||
|
||
if __name__ == "__main__": | ||
game = ChessGame() | ||
game.play_game() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import chess | ||
import pygame | ||
|
||
class HumanPlayer: | ||
def __init__(self, color): | ||
self.color = color | ||
self.selected_square = None | ||
|
||
def get_square_from_coords(self, x, y, flipped=False): | ||
"""Convert screen coordinates to chess square.""" | ||
file_idx = x * 8 // 600 | ||
rank_idx = y * 8 // 600 | ||
if flipped: | ||
file_idx = 7 - file_idx | ||
rank_idx = 7 - rank_idx | ||
else: | ||
rank_idx = 7 - rank_idx | ||
return chess.square(file_idx, rank_idx) | ||
|
||
def is_promotion_move(self, board, from_square, to_square): | ||
"""Check if the move would be a pawn promotion.""" | ||
piece = board.get_board_state().piece_at(from_square) | ||
if piece and piece.piece_type == chess.PAWN: | ||
rank = chess.square_rank(to_square) | ||
return (self.color == chess.WHITE and rank == 7) or \ | ||
(self.color == chess.BLACK and rank == 0) | ||
return False | ||
|
||
def get_promotion_choice(self): | ||
"""Get the promotion piece choice from the player.""" | ||
# Create a simple text-based menu for promotion choices | ||
pygame.font.init() | ||
font = pygame.font.Font(None, 36) | ||
screen = pygame.display.get_surface() | ||
|
||
pieces = { | ||
'Q': (chess.QUEEN, "Queen"), | ||
'R': (chess.ROOK, "Rook"), | ||
'B': (chess.BISHOP, "Bishop"), | ||
'K': (chess.KNIGHT, "Knight") | ||
} | ||
choices = [] | ||
y = 250 | ||
|
||
# Draw promotion options | ||
for key, piece in pieces.items(): | ||
text = font.render(f"Press {key} for {piece[1]}", True, (255, 255, 255)) | ||
rect = text.get_rect(center=(300, y)) | ||
screen.blit(text, rect) | ||
choices.append((piece, rect)) | ||
y += 50 | ||
|
||
pygame.display.flip() | ||
|
||
# Wait for valid choice | ||
while True: | ||
event = pygame.event.wait() | ||
if event.type == pygame.QUIT: | ||
return None | ||
if event.type == pygame.KEYDOWN: | ||
for key, piece_type in [ | ||
(pygame.K_q, chess.QUEEN), | ||
(pygame.K_r, chess.ROOK), | ||
(pygame.K_b, chess.BISHOP), | ||
(pygame.K_k, chess.KNIGHT) | ||
]: | ||
if event.key == key: | ||
return piece_type | ||
|
||
def get_move(self, board): | ||
"""Get move from human player through GUI interaction.""" | ||
pygame.event.clear() | ||
|
||
while True: | ||
event = pygame.event.wait() | ||
|
||
if event.type == pygame.QUIT: | ||
return None | ||
|
||
if event.type == pygame.MOUSEBUTTONDOWN: | ||
x, y = event.pos | ||
square = self.get_square_from_coords(x, y, self.color == chess.BLACK) | ||
|
||
if self.selected_square is None: | ||
# First click - select piece | ||
piece = board.get_board_state().piece_at(square) | ||
if piece and piece.color == self.color: | ||
self.selected_square = square | ||
else: | ||
# Second click - try to make move | ||
from_square = self.selected_square | ||
to_square = square | ||
|
||
# Check if this is a promotion move | ||
if self.is_promotion_move(board, from_square, to_square): | ||
promotion_piece = self.get_promotion_choice() | ||
if promotion_piece is None: | ||
self.selected_square = None | ||
continue | ||
move = chess.Move(from_square, to_square, promotion=promotion_piece) | ||
else: | ||
move = chess.Move(from_square, to_square) | ||
|
||
# Check if move is legal | ||
if move in board.get_legal_moves(): | ||
self.selected_square = None | ||
return move | ||
|
||
# If illegal move, clear selection | ||
self.selected_square = None | ||
|
||
return None |