Game.py
class Game:
def __init__(self):
self.winner = None
# checks if both colors still has a piece
def check_piece(self, board):
red_piece = 0
black_piece = 0
for y in range(board.board_size):
for x in range(board.board_size):
tile = board.get_tile_from_pos((x, y))
if tile.occupying_piece != None:
if tile.occupying_piece.color == "red":
red_piece += 1
else:
black_piece += 1
return red_piece, black_piece
def is_game_over(self, board):
red_piece, black_piece = self.check_piece(board)
if red_piece == 0 or black_piece == 0:
self.winner = "red" if red_piece > black_piece else "black"
return True
else:
return False
def check_jump(self, board):
piece = None
for tile in board.tile_list:
if tile.occupying_piece != None:
piece = tile.occupying_piece
if len(piece.valid_jumps()) != 0 and board.turn == piece.color:
board.is_jump = True
break
else:
board.is_jump = False
if board.is_jump:
board.selected_piece = piece
board.handle_click(piece.pos)
return board.is_jump
def message(self):
print(f"{self.winner} Wins!!")
Main.py
import pygame
from Board import Board
from Game import Game
pygame.init()
class Checkers:
def __init__(self, screen):
self.screen = screen
self.running = True
self.FPS = pygame.time.Clock()
def _draw(self, board):
board.draw(self.screen)
pygame.display.update()
def main(self, window_width, window_height):
board_size = 8
tile_width, tile_height = window_width // board_size, window_height // board_size
board = Board(tile_width, tile_height, board_size)
game = Game()
while self.running:
game.check_jump(board)
for self.event in pygame.event.get():
if self.event.type == pygame.QUIT:
self.running = False
if not game.is_game_over(board):
if self.event.type == pygame.MOUSEBUTTONDOWN:
board.handle_click(self.event.pos)
else:
game.message()
self.running = False
self._draw(board)
self.FPS.tick(60)
if __name__ == "__main__":
window_size = (640, 640)
screen = pygame.display.set_mode(window_size)
pygame.display.set_caption("Checkers")
checkers = Checkers(screen)
checkers.main(window_size[0], window_size[1])
Pawn.py
import pygame
from Piece import Piece
class Pawn(Piece):
def __init__(self, x, y, color, board):
super().__init__(x, y, color, board)
img_path = f'images/{color}-pawn.png'
self.img = pygame.image.load(img_path)
self.img = pygame.transform.scale(self.img, (board.tile_width, board.tile_height))
self.notation = 'p'
def _possible_moves(self):
# (x, y) move for left and right
if self.color == "red":
possible_moves = ((-1, -1), (+1, -1))
else:
possible_moves = ((-1, +1), (+1, +1))
return possible_moves
def valid_moves(self):
tile_moves = []
moves = self._possible_moves()
for move in moves:
tile_pos = (self.x + move[0], self.y + move[-1])
if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:
pass
else:
tile = self.board.get_tile_from_pos(tile_pos)
if tile.occupying_piece == None:
tile_moves.append(tile)
return tile_moves
def valid_jumps(self):
tile_jumps = []
moves = self._possible_moves()
for move in moves:
tile_pos = (self.x + move[0], self.y + move[-1])
if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:
pass
else:
tile = self.board.get_tile_from_pos(tile_pos)
if self.board.turn == self.color:
if tile.occupying_piece != None and tile.occupying_piece.color != self.color:
next_pos = (tile_pos[0] + move[0], tile_pos[-1] + move[-1])
next_tile = self.board.get_tile_from_pos(next_pos)
if next_pos[0] < 0 or next_pos[0] > 7 or next_pos[-1] < 0 or next_pos[-1] > 7:
pass
else:
if next_tile.occupying_piece == None:
tile_jumps.append((next_tile, tile))
return tile_jumps
King.py
import pygame
from Piece import Piece
class King(Piece):
def __init__(self, x, y, color, board):
super().__init__(x, y, color, board)
img_path = f'images/{color}-king.png'
self.img = pygame.image.load(img_path)
self.img = pygame.transform.scale(self.img, (board.tile_width, board.tile_height))
self.notation = 'k'
def _possible_moves(self):
possible_moves = ((-1, -1), (+1, -1), (-1, +1), (+1, +1))
return possible_moves
def valid_moves(self):
tile_moves = []
moves = self._possible_moves()
for move in moves:
tile_pos = (self.x + move[0], self.y + move[-1])
if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:
pass
else:
tile = self.board.get_tile_from_pos(tile_pos)
if tile.occupying_piece == None:
tile_moves.append(tile)
return tile_moves
def valid_jumps(self):
tile_jumps = []
moves = self._possible_moves()
for move in moves:
tile_pos = (self.x + move[0], self.y + move[-1])
if tile_pos[0] < 0 or tile_pos[0] > 7 or tile_pos[-1] < 0 or tile_pos[-1] > 7:
pass
else:
tile = self.board.get_tile_from_pos(tile_pos)
if self.board.turn == self.color:
if tile.occupying_piece != None and tile.occupying_piece.color != self.color:
next_pos = (tile_pos[0] + move[0], tile_pos[-1] + move[-1])
next_tile = self.board.get_tile_from_pos(next_pos)
if next_pos[0] < 0 or next_pos[0] > 7 or next_pos[-1] < 0 or next_pos[-1] > 7:
pass
else:
if next_tile.occupying_piece == None:
tile_jumps.append((next_tile, tile))
return tile_jumps
Piece.py
import pygame
class Piece:
def __init__(self, x, y, color, board):
self.x = x
self.y = y
self.pos = (x, y)
self.board = board
self.color = color
def _move(self, tile):
for i in self.board.tile_list:
i.highlight = False
if tile in self.valid_moves() and not self.board.is_jump:
prev_tile = self.board.get_tile_from_pos(self.pos)
self.pos, self.x, self.y = tile.pos, tile.x, tile.y
prev_tile.occupying_piece = None
tile.occupying_piece = self
self.board.selected_piece = None
self.has_moved = True
# Pawn promotion
if self.notation == 'p':
if self.y == 0 or self.y == 7:
from King import King
tile.occupying_piece = King(
self.x, self.y, self.color, self.board
)
return True
elif self.board.is_jump:
for move in self.valid_jumps():
if tile in move:
prev_tile = self.board.get_tile_from_pos(self.pos)
jumped_piece = move[-1]
self.pos, self.x, self.y = tile.pos, tile.x, tile.y
prev_tile.occupying_piece = None
jumped_piece.occupying_piece = None
tile.occupying_piece = self
self.board.selected_piece = None
self.has_moved = True
# Pawn promotion
if self.notation == 'p':
if self.y == 0 or self.y == 7:
from King import King
tile.occupying_piece = King(
self.x, self.y, self.color, self.board
)
return True
else:
self.board.selected_piece = None
return False
Board.py
import pygame
from Tile import Tile
from Pawn import Pawn
class Board:
def __init__(self,tile_width, tile_height, board_size):
self.tile_width = tile_width
self.tile_height = tile_height
self.board_size = board_size
self.selected_piece = None
self.turn = "black"
self.is_jump = False
self.config = [
['', 'bp', '', 'bp', '', 'bp', '', 'bp'],
['bp', '', 'bp', '', 'bp', '', 'bp', ''],
['', 'bp', '', 'bp', '', 'bp', '', 'bp'],
['', '', '', '', '', '', '', ''],
['', '', '', '', '', '', '', ''],
['rp', '', 'rp', '', 'rp', '', 'rp', ''],
['', 'rp', '', 'rp', '', 'rp', '', 'rp'],
['rp', '', 'rp', '', 'rp', '', 'rp', '']
]
self.tile_list = self._generate_tiles()
self._setup()
def _generate_tiles(self):
output = []
for y in range(self.board_size):
for x in range(self.board_size):
output.append(
Tile(x, y, self.tile_width, self.tile_height)
)
return output
def get_tile_from_pos(self, pos):
for tile in self.tile_list:
if (tile.x, tile.y) == (pos[0], pos[1]):
return tile
def _setup(self):
for y_ind, row in enumerate(self.config):
for x_ind, x in enumerate(row):
tile = self.get_tile_from_pos((x_ind, y_ind))
if x != '':
if x[-1] == 'p':
color = 'red' if x[0] == 'r' else 'black'
tile.occupying_piece = Pawn(x_ind, y_ind, color, self)
def handle_click(self, pos):
x, y = pos[0], pos[-1]
if x >= self.board_size or y >= self.board_size:
x = x // self.tile_width
y = y // self.tile_height
clicked_tile = self.get_tile_from_pos((x, y))
if self.selected_piece is None:
if clicked_tile.occupying_piece is not None:
if clicked_tile.occupying_piece.color == self.turn:
self.selected_piece = clicked_tile.occupying_piece
elif self.selected_piece._move(clicked_tile):
if not self.is_jump:
self.turn = 'red' if self.turn == 'black' else 'black'
else:
if len(clicked_tile.occupying_piece.valid_jumps()) == 0:
self.turn = 'red' if self.turn == 'black' else 'black'
elif clicked_tile.occupying_piece is not None:
if clicked_tile.occupying_piece.color == self.turn:
self.selected_piece = clicked_tile.occupying_piece
def draw(self, display):
if self.selected_piece is not None:
self.get_tile_from_pos(self.selected_piece.pos).highlight = True
if not self.is_jump:
for tile in self.selected_piece.valid_moves():
tile.highlight = True
else:
for tile in self.selected_piece.valid_jumps():
tile[0].highlight = True
for tile in self.tile_list:
tile.draw(display)
Tile.py
import pygame
class Tile:
def __init__(self, x, y, tile_width, tile_height):
self.x = x
self.y = y
self.pos = (x, y)
self.tile_width = tile_width
self.tile_height = tile_height
self.abs_x = x * tile_width
self.abs_y = y * tile_height
self.abs_pos = (self.abs_x, self.abs_y)
self.color = 'light' if (x + y) % 2 == 0 else 'dark'
self.draw_color = (220, 189, 194) if self.color == 'light' else (53, 53, 53)
self.highlight_color = (100, 249, 83) if self.color == 'light' else (0, 228, 10)
self.occupying_piece = None
self.coord = self.get_coord()
self.highlight = False
self.rect = pygame.Rect(
self.abs_x,
self.abs_y,
self.tile_width,
self.tile_height
)
def get_coord(self):
columns = 'abcdefgh'
return columns[self.x] + str(self.y + 1)
def draw(self, display):
if self.highlight:
pygame.draw.rect(display, self.highlight_color, self.rect)
else:
pygame.draw.rect(display, self.draw_color, self.rect)
if self.occupying_piece != None:
centering_rect = self.occupying_piece.img.get_rect()
centering_rect.center = self.rect.center
display.blit(self.occupying_piece.img, centering_rect.topleft)