main.py
import pygamefrom data.classes.Board import Boardpygame.init()WINDOW_SIZE = (600, 600)screen = pygame.display.set_mode(WINDOW_SIZE)board = Board(WINDOW_SIZE[0], WINDOW_SIZE[1])def draw(display):display.fill('white')board.draw(display)pygame.display.update()if __name__ == '__main__':running = Truewhile running:mx, my = pygame.mouse.get_pos()for event in pygame.event.get():if event.type == pygame.QUIT:running = Falseelif event.type == pygame.MOUSEBUTTONDOWN:if event.button == 1:board.handle_click(mx, my)if board.is_in_checkmate('black'):print('White wins!')running = Falseelif board.is_in_checkmate('white'):print('Black wins!')running = Falsedraw(screen)
classes/Board.py
import pygamefrom data.classes.Square import Squarefrom data.classes.pieces.Rook import Rookfrom data.classes.pieces.Bishop import Bishopfrom data.classes.pieces.Knight import Knightfrom data.classes.pieces.Queen import Queenfrom data.classes.pieces.King import Kingfrom data.classes.pieces.Pawn import Pawn# Game state checkerclass Board:def __init__(self, width, height):self.width = widthself.height = heightself.tile_width = width // 8self.tile_height = height // 8self.selected_piece = Noneself.turn = 'white'# try making it chess.board.fen()self.config = [['bR', 'bN', 'bB', 'bQ', 'bK', 'bB', 'bN', 'bR'],['bP', 'bP', 'bP', 'bP', 'bP', 'bP', 'bP', 'bP'],['','','','','','','',''],['','','','','','','',''],['','','','','','','',''],['','','','','','','',''],['wP', 'wP', 'wP', 'wP', 'wP', 'wP', 'wP', 'wP'],['wR', 'wN', 'wB', 'wQ', 'wK', 'wB', 'wN', 'wR'],]self.squares = self.generate_squares()self.setup_board()def generate_squares(self):output = []for y in range(8):for x in range(8):output.append(Square(x, y, self.tile_width, self.tile_height))return outputdef get_square_from_pos(self, pos):for square in self.squares:if (square.x, square.y) == (pos[0], pos[1]):return squaredef get_piece_from_pos(self, pos):return self.get_square_from_pos(pos).occupying_piecedef setup_board(self):# iterating 2d listfor y, row in enumerate(self.config):for x, piece in enumerate(row):if piece != '':square = self.get_square_from_pos((x, y))# looking inside contents, what piece does it haveif piece[1] == 'R':square.occupying_piece = Rook((x, y), 'white' if piece[0] == 'w' else 'black', self)# as you notice above, we put `self` as argument, or means our class Boardelif piece[1] == 'N':square.occupying_piece = Knight((x, y), 'white' if piece[0] == 'w' else 'black', self)elif piece[1] == 'B':square.occupying_piece = Bishop((x, y), 'white' if piece[0] == 'w' else 'black', self)elif piece[1] == 'Q':square.occupying_piece = Queen((x, y), 'white' if piece[0] == 'w' else 'black', self)elif piece[1] == 'K':square.occupying_piece = King((x, y), 'white' if piece[0] == 'w' else 'black', self)elif piece[1] == 'P':square.occupying_piece = Pawn((x, y), 'white' if piece[0] == 'w' else 'black', self)def handle_click(self, mx, my):x = mx // self.tile_widthy = my // self.tile_heightclicked_square = self.get_square_from_pos((x, y))if self.selected_piece is None:if clicked_square.occupying_piece is not None:if clicked_square.occupying_piece.color == self.turn:self.selected_piece = clicked_square.occupying_pieceelif self.selected_piece.move(self, clicked_square):self.turn = 'white' if self.turn == 'black' else 'black'elif clicked_square.occupying_piece is not None:if clicked_square.occupying_piece.color == self.turn:self.selected_piece = clicked_square.occupying_piecedef is_in_check(self, color, board_change=None): # board_change = [(x1, y1), (x2, y2)]output = Falseking_pos = Nonechanging_piece = Noneold_square = Nonenew_square = Nonenew_square_old_piece = Noneif board_change is not None:for square in self.squares:if square.pos == board_change[0]:changing_piece = square.occupying_pieceold_square = squareold_square.occupying_piece = Nonefor square in self.squares:if square.pos == board_change[1]:new_square = squarenew_square_old_piece = new_square.occupying_piecenew_square.occupying_piece = changing_piecepieces = [i.occupying_piece for i in self.squares if i.occupying_piece is not None]if changing_piece is not None:if changing_piece.notation == 'K':king_pos = new_square.posif king_pos == None:for piece in pieces:if piece.notation == 'K' and piece.color == color:king_pos = piece.posfor piece in pieces:if piece.color != color:for square in piece.attacking_squares(self):if square.pos == king_pos:output = Trueif board_change is not None:old_square.occupying_piece = changing_piecenew_square.occupying_piece = new_square_old_piecereturn outputdef is_in_checkmate(self, color):output = Falsefor piece in [i.occupying_piece for i in self.squares]:if piece != None:if piece.notation == 'K' and piece.color == color:king = pieceif king.get_valid_moves(self) == []:if self.is_in_check(color):output = Truereturn outputdef draw(self, display):if self.selected_piece is not None:self.get_square_from_pos(self.selected_piece.pos).highlight = Truefor square in self.selected_piece.get_valid_moves(self):square.highlight = Truefor square in self.squares:square.draw(display)
classes/Piece.py
import pygameclass Piece:def __init__(self, pos, color, board):self.pos = posself.x = pos[0]self.y = pos[1]self.color = colorself.has_moved = Falsedef move(self, board, square, force=False):for i in board.squares:i.highlight = Falseif square in self.get_valid_moves(board) or force:prev_square = board.get_square_from_pos(self.pos)self.pos, self.x, self.y = square.pos, square.x, square.yprev_square.occupying_piece = Nonesquare.occupying_piece = selfboard.selected_piece = Noneself.has_moved = True# Pawn promotionif self.notation == ' ':if self.y == 0 or self.y == 7:from data.classes.pieces.Queen import Queensquare.occupying_piece = Queen((self.x, self.y),self.color,board)# Move rook if king castlesif self.notation == 'K':if prev_square.x - self.x == 2:rook = board.get_piece_from_pos((0, self.y))rook.move(board, board.get_square_from_pos((3, self.y)), force=True)elif prev_square.x - self.x == -2:rook = board.get_piece_from_pos((7, self.y))rook.move(board, board.get_square_from_pos((5, self.y)), force=True)return Trueelse:board.selected_piece = Nonereturn Falsedef get_moves(self, board):output = []for direction in self.get_possible_moves(board):for square in direction:if square.occupying_piece is not None:if square.occupying_piece.color == self.color:breakelse:output.append(square)breakelse:output.append(square)return outputdef get_valid_moves(self, board):output = []for square in self.get_moves(board):if not board.is_in_check(self.color, board_change=[self.pos, square.pos]):output.append(square)return output# True for all pieces except pawndef attacking_squares(self, board):return self.get_moves(board)
classes/Square.py
import pygameclass Square:def __init__(self, x, y, width, height):self.x = xself.y = yself.width = widthself.height = heightself.abs_x = x * widthself.abs_y = y * heightself.abs_pos = (self.abs_x, self.abs_y)self.pos = (x, 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 = Noneself.coord = self.get_coord()self.highlight = Falseself.rect = pygame.Rect(self.abs_x,self.abs_y,self.width,self.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.centerdisplay.blit(self.occupying_piece.img, centering_rect.topleft)
classes/pieces/Bishop.py
import pygamefrom data.classes.Piece import Piececlass Bishop(Piece):def __init__(self, pos, color, board):super().__init__(pos, color, board)img_path = 'data/imgs/' + color[0] + '_bishop.png'self.img = pygame.image.load(img_path)self.img = pygame.transform.scale(self.img, (board.tile_width - 20, board.tile_height - 20))self.notation = 'B'def get_possible_moves(self, board):output = []moves_ne = []for i in range(1, 8):if self.x + i > 7 or self.y - i < 0:breakmoves_ne.append(board.get_square_from_pos((self.x + i, self.y - i)))output.append(moves_ne)moves_se = []for i in range(1, 8):if self.x + i > 7 or self.y + i > 7:breakmoves_se.append(board.get_square_from_pos((self.x + i, self.y + i)))output.append(moves_se)moves_sw = []for i in range(1, 8):if self.x - i < 0 or self.y + i > 7:breakmoves_sw.append(board.get_square_from_pos((self.x - i, self.y + i)))output.append(moves_sw)moves_nw = []for i in range(1, 8):if self.x - i < 0 or self.y - i < 0:breakmoves_nw.append(board.get_square_from_pos((self.x - i, self.y - i)))output.append(moves_nw)return output
classes/pieces/King.py
import pygamefrom data.classes.Piece import Piececlass King(Piece):def __init__(self, pos, color, board):super().__init__(pos, color, board)img_path = 'data/imgs/' + color[0] + '_king.png'self.img = pygame.image.load(img_path)self.img = pygame.transform.scale(self.img, (board.tile_width - 20, board.tile_height - 20))self.notation = 'K'def get_possible_moves(self, board):output = []moves = [(0,-1), # north(1, -1), # ne(1, 0), # east(1, 1), # se(0, 1), # south(-1, 1), # sw(-1, 0), # west(-1, -1), # nw]for move in moves:new_pos = (self.x + move[0], self.y + move[1])if (new_pos[0] < 8 andnew_pos[0] >= 0 and new_pos[1] < 8 and new_pos[1] >= 0):output.append([board.get_square_from_pos(new_pos)])return outputdef can_castle(self, board):if not self.has_moved:if self.color == 'white':queenside_rook = board.get_piece_from_pos((0, 7))kingside_rook = board.get_piece_from_pos((7, 7))if queenside_rook != None:if not queenside_rook.has_moved:if [board.get_piece_from_pos((i, 7)) for i in range(1, 4)] == [None, None, None]:return 'queenside'if kingside_rook != None:if not kingside_rook.has_moved:if [board.get_piece_from_pos((i, 7)) for i in range(5, 7)] == [None, None]:return 'kingside'elif self.color == 'black':queenside_rook = board.get_piece_from_pos((0, 0))kingside_rook = board.get_piece_from_pos((7, 0))if queenside_rook != None:if not queenside_rook.has_moved:if [board.get_piece_from_pos((i, 0)) for i in range(1, 4)] == [None, None, None]:return 'queenside'if kingside_rook != None:if not kingside_rook.has_moved:if [board.get_piece_from_pos((i, 0)) for i in range(5, 7)] == [None, None]:return 'kingside'def get_valid_moves(self, board):output = []for square in self.get_moves(board):if not board.is_in_check(self.color, board_change=[self.pos, square.pos]):output.append(square)if self.can_castle(board) == 'queenside':output.append(board.get_square_from_pos((self.x - 2, self.y)))if self.can_castle(board) == 'kingside':output.append(board.get_square_from_pos((self.x + 2, self.y)))return output
classes/pieces/Knight.py
import pygamefrom data.classes.Piece import Piececlass Knight(Piece):def __init__(self, pos, color, board):super().__init__(pos, color, board)img_path = 'data/imgs/' + color[0] + '_knight.png'self.img = pygame.image.load(img_path)self.img = pygame.transform.scale(self.img, (board.tile_width - 20, board.tile_height - 20))self.notation = 'N'def get_possible_moves(self, board):output = []moves = [(1, -2),(2, -1),(2, 1),(1, 2),(-1, 2),(-2, 1),(-2, -1),(-1, -2)]for move in moves:new_pos = (self.x + move[0], self.y + move[1])if (new_pos[0] < 8 andnew_pos[0] >= 0 and new_pos[1] < 8 and new_pos[1] >= 0):output.append([board.get_square_from_pos(new_pos)])return output
classes/pieces/Pawn.py
import pygamefrom data.classes.Piece import Piececlass Pawn(Piece):def __init__(self, pos, color, board):super().__init__(pos, color, board)img_path = 'data/imgs/' + color[0] + '_pawn.png'self.img = pygame.image.load(img_path)self.img = pygame.transform.scale(self.img, (board.tile_width - 35, board.tile_height - 35))self.notation = ' 'def get_possible_moves(self, board):output = []moves = []# move forwardif self.color == 'white':moves.append((0, -1))if not self.has_moved:moves.append((0, -2))elif self.color == 'black':moves.append((0, 1))if not self.has_moved:moves.append((0, 2))for move in moves:new_pos = (self.x, self.y + move[1])if new_pos[1] < 8 and new_pos[1] >= 0:output.append(board.get_square_from_pos(new_pos))return outputdef get_moves(self, board):output = []for square in self.get_possible_moves(board):if square.occupying_piece != None:breakelse:output.append(square)if self.color == 'white':if self.x + 1 < 8 and self.y - 1 >= 0:square = board.get_square_from_pos((self.x + 1, self.y - 1))if square.occupying_piece != None:if square.occupying_piece.color != self.color:output.append(square)if self.x - 1 >= 0 and self.y - 1 >= 0:square = board.get_square_from_pos((self.x - 1, self.y - 1))if square.occupying_piece != None:if square.occupying_piece.color != self.color:output.append(square)elif self.color == 'black':if self.x + 1 < 8 and self.y + 1 < 8:square = board.get_square_from_pos((self.x + 1, self.y + 1))if square.occupying_piece != None:if square.occupying_piece.color != self.color:output.append(square)if self.x - 1 >= 0 and self.y + 1 < 8:square = board.get_square_from_pos((self.x - 1, self.y + 1))if square.occupying_piece != None:if square.occupying_piece.color != self.color:output.append(square)return outputdef attacking_squares(self, board):moves = self.get_moves(board)# return the diagonal moves return [i for i in moves if i.x != self.x]
classes/pieces/Queen.py
import pygamefrom data.classes.Piece import Piececlass Queen(Piece):def __init__(self, pos, color, board):super().__init__(pos, color, board)img_path = 'data/imgs/' + color[0] + '_queen.png'self.img = pygame.image.load(img_path)self.img = pygame.transform.scale(self.img, (board.tile_width - 20, board.tile_height - 20))self.notation = 'Q'def get_possible_moves(self, board):output = []moves_north = []for y in range(self.y)[::-1]:moves_north.append(board.get_square_from_pos((self.x, y)))output.append(moves_north)moves_ne = []for i in range(1, 8):if self.x + i > 7 or self.y - i < 0:breakmoves_ne.append(board.get_square_from_pos((self.x + i, self.y - i)))output.append(moves_ne)moves_east = []for x in range(self.x + 1, 8):moves_east.append(board.get_square_from_pos((x, self.y)))output.append(moves_east)moves_se = []for i in range(1, 8):if self.x + i > 7 or self.y + i > 7:breakmoves_se.append(board.get_square_from_pos((self.x + i, self.y + i)))output.append(moves_se)moves_south = []for y in range(self.y + 1, 8):moves_south.append(board.get_square_from_pos((self.x, y)))output.append(moves_south)moves_sw = []for i in range(1, 8):if self.x - i < 0 or self.y + i > 7:breakmoves_sw.append(board.get_square_from_pos((self.x - i, self.y + i)))output.append(moves_sw)moves_west = []for x in range(self.x)[::-1]:moves_west.append(board.get_square_from_pos((x, self.y)))output.append(moves_west)moves_nw = []for i in range(1, 8):if self.x - i < 0 or self.y - i < 0:breakmoves_nw.append(board.get_square_from_pos((self.x - i, self.y - i)))output.append(moves_nw)return output
classes/pieces/Rook.py
import pygamefrom data.classes.Piece import Piececlass Rook(Piece):def __init__(self, pos, color, board):super().__init__(pos, color, board)img_path = 'data/imgs/' + color[0] + '_rook.png'self.img = pygame.image.load(img_path)self.img = pygame.transform.scale(self.img, (board.tile_width - 20, board.tile_height - 20))self.notation = 'R'def get_possible_moves(self, board):output = []moves_north = []for y in range(self.y)[::-1]:moves_north.append(board.get_square_from_pos((self.x, y)))output.append(moves_north)moves_east = []for x in range(self.x + 1, 8):moves_east.append(board.get_square_from_pos((x, self.y)))output.append(moves_east)moves_south = []for y in range(self.y + 1, 8):moves_south.append(board.get_square_from_pos((self.x, y)))output.append(moves_south)moves_west = []for x in range(self.x)[::-1]:moves_west.append(board.get_square_from_pos((x, self.y)))output.append(moves_west)return output