├── .gitignore ├── res ├── board.png ├── pieces.png └── chess_icon.png ├── main.py ├── README.md ├── utils.py ├── piece.py ├── game.py └── chess.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | __pycache__ 3 | *.pyc -------------------------------------------------------------------------------- /res/board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandrelbrotset/pygame-chess/HEAD/res/board.png -------------------------------------------------------------------------------- /res/pieces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandrelbrotset/pygame-chess/HEAD/res/pieces.png -------------------------------------------------------------------------------- /res/chess_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandrelbrotset/pygame-chess/HEAD/res/chess_icon.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from game import Game 2 | 3 | if __name__=="__main__": 4 | game = Game() 5 | game.start_game() 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pygame-chess 2 | Chess game written in python with the pygame library. 3 | 4 | ## Game Menu 5 | ![menu](https://user-images.githubusercontent.com/24194821/57589722-cf907c00-74eb-11e9-9318-822abd6c9942.png) 6 | 7 | ## Gameplay 8 | ![game](https://user-images.githubusercontent.com/24194821/57589721-cf907c00-74eb-11e9-8def-bf4782315ed9.png) 9 | 10 | ## Winner Menu 11 | ![checkmate](https://user-images.githubusercontent.com/24194821/57589723-cf907c00-74eb-11e9-8b42-aef703c3e1f8.png) 12 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | import queue 4 | 5 | class Utils: 6 | def get_mouse_event(self): 7 | # get coordinates of the mouse 8 | position = pygame.mouse.get_pos() 9 | 10 | # return left click status and mouse coordinates 11 | return position 12 | 13 | def left_click_event(self): 14 | # store mouse buttons 15 | mouse_btn = pygame.mouse.get_pressed() 16 | # create flag to check for left click event 17 | left_click = False 18 | 19 | if mouse_btn[0]: #and e.button == 1: 20 | # change left click flag 21 | left_click = True 22 | 23 | return left_click -------------------------------------------------------------------------------- /piece.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pygame 3 | 4 | class Piece(pygame.sprite.Sprite): 5 | def __init__(self, filename, cols, rows): 6 | pygame.sprite.Sprite.__init__(self) 7 | self.pieces = { 8 | "white_pawn": 5, 9 | "white_knight": 3, 10 | "white_bishop": 2, 11 | "white_rook": 4, 12 | "white_king": 0, 13 | "white_queen": 1, 14 | "black_pawn": 11, 15 | "black_knight": 9, 16 | "black_bishop": 8, 17 | "black_rook": 10, 18 | "black_king": 6, 19 | "black_queen": 7 20 | } 21 | self.spritesheet = pygame.image.load(filename).convert_alpha() 22 | 23 | self.cols = cols 24 | self.rows = rows 25 | self.cell_count = cols * rows 26 | 27 | self.rect = self.spritesheet.get_rect() 28 | w = self.cell_width = self.rect.width // self.cols 29 | h = self.cell_height = self.rect.height // self.rows 30 | 31 | self.cells = list([(i % cols * w, i // cols * h, w, h) for i in range(self.cell_count)]) 32 | 33 | def draw(self, surface, piece_name, coords): 34 | piece_index = self.pieces[piece_name] 35 | surface.blit(self.spritesheet, coords, self.cells[piece_index]) 36 | 37 | -------------------------------------------------------------------------------- /game.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pygame 3 | from pygame.locals import * 4 | from piece import Piece 5 | from chess import Chess 6 | from utils import Utils 7 | 8 | class Game: 9 | def __init__(self): 10 | # screen dimensions 11 | screen_width = 640 12 | screen_height = 750 13 | # flag to know if game menu has been showed 14 | self.menu_showed = False 15 | # flag to set game loop 16 | self.running = True 17 | # base folder for program resources 18 | self.resources = "res" 19 | 20 | # initialize game window 21 | pygame.display.init() 22 | # initialize font for text 23 | pygame.font.init() 24 | 25 | # create game window 26 | self.screen = pygame.display.set_mode([screen_width, screen_height]) 27 | 28 | # title of window 29 | window_title = "Chess" 30 | # set window caption 31 | pygame.display.set_caption(window_title) 32 | 33 | # get location of game icon 34 | icon_src = os.path.join(self.resources, "chess_icon.png") 35 | # load game icon 36 | icon = pygame.image.load(icon_src) 37 | # set game icon 38 | pygame.display.set_icon(icon) 39 | # update display 40 | pygame.display.flip() 41 | # set game clock 42 | self.clock = pygame.time.Clock() 43 | 44 | 45 | def start_game(self): 46 | """Function containing main game loop""" 47 | # chess board offset 48 | self.board_offset_x = 0 49 | self.board_offset_y = 50 50 | self.board_dimensions = (self.board_offset_x, self.board_offset_y) 51 | 52 | # get location of chess board image 53 | board_src = os.path.join(self.resources, "board.png") 54 | # load the chess board image 55 | self.board_img = pygame.image.load(board_src).convert() 56 | 57 | # get the width of a chess board square 58 | square_length = self.board_img.get_rect().width // 8 59 | 60 | # initialize list that stores all places to put chess pieces on the board 61 | self.board_locations = [] 62 | 63 | # calculate coordinates of the each square on the board 64 | for x in range(0, 8): 65 | self.board_locations.append([]) 66 | for y in range(0, 8): 67 | self.board_locations[x].append([self.board_offset_x+(x*square_length), 68 | self.board_offset_y+(y*square_length)]) 69 | 70 | # get location of image containing the chess pieces 71 | pieces_src = os.path.join(self.resources, "pieces.png") 72 | # create class object that handles the gameplay logic 73 | self.chess = Chess(self.screen, pieces_src, self.board_locations, square_length) 74 | 75 | # game loop 76 | while self.running: 77 | self.clock.tick(5) 78 | # poll events 79 | for event in pygame.event.get(): 80 | # get keys pressed 81 | key_pressed = pygame.key.get_pressed() 82 | # check if the game has been closed by the user 83 | if event.type == pygame.QUIT or key_pressed[K_ESCAPE]: 84 | # set flag to break out of the game loop 85 | self.running = False 86 | elif key_pressed[K_SPACE]: 87 | self.chess.reset() 88 | 89 | winner = self.chess.winner 90 | 91 | if self.menu_showed == False: 92 | self.menu() 93 | elif len(winner) > 0: 94 | self.declare_winner(winner) 95 | else: 96 | self.game() 97 | 98 | 99 | 100 | # for testing mechanics of the game 101 | #self.game() 102 | #self.declare_winner(winner) 103 | 104 | # update display 105 | pygame.display.flip() 106 | # update events 107 | pygame.event.pump() 108 | 109 | # call method to stop pygame 110 | pygame.quit() 111 | 112 | 113 | def menu(self): 114 | """method to show game menu""" 115 | # background color 116 | bg_color = (255, 255, 255) 117 | # set background color 118 | self.screen.fill(bg_color) 119 | # black color 120 | black_color = (0, 0, 0) 121 | # coordinates for "Play" button 122 | start_btn = pygame.Rect(270, 300, 100, 50) 123 | # show play button 124 | pygame.draw.rect(self.screen, black_color, start_btn) 125 | 126 | # white color 127 | white_color = (255, 255, 255) 128 | # create fonts for texts 129 | big_font = pygame.font.SysFont("comicsansms", 50) 130 | small_font = pygame.font.SysFont("comicsansms", 20) 131 | # create text to be shown on the game menu 132 | welcome_text = big_font.render("Chess", False, black_color) 133 | created_by = small_font.render("Created by Sheriff", True, black_color) 134 | start_btn_label = small_font.render("Play", True, white_color) 135 | 136 | # show welcome text 137 | self.screen.blit(welcome_text, 138 | ((self.screen.get_width() - welcome_text.get_width()) // 2, 139 | 150)) 140 | # show credit text 141 | self.screen.blit(created_by, 142 | ((self.screen.get_width() - created_by.get_width()) // 2, 143 | self.screen.get_height() - created_by.get_height() - 100)) 144 | # show text on the Play button 145 | self.screen.blit(start_btn_label, 146 | ((start_btn.x + (start_btn.width - start_btn_label.get_width()) // 2, 147 | start_btn.y + (start_btn.height - start_btn_label.get_height()) // 2))) 148 | 149 | # get pressed keys 150 | key_pressed = pygame.key.get_pressed() 151 | # 152 | util = Utils() 153 | 154 | # check if left mouse button was clicked 155 | if util.left_click_event(): 156 | # call function to get mouse event 157 | mouse_coords = util.get_mouse_event() 158 | 159 | # check if "Play" button was clicked 160 | if start_btn.collidepoint(mouse_coords[0], mouse_coords[1]): 161 | # change button behavior as it is hovered 162 | pygame.draw.rect(self.screen, white_color, start_btn, 3) 163 | 164 | # change menu flag 165 | self.menu_showed = True 166 | # check if enter or return key was pressed 167 | elif key_pressed[K_RETURN]: 168 | self.menu_showed = True 169 | 170 | 171 | def game(self): 172 | # background color 173 | color = (0,0,0) 174 | # set backgound color 175 | self.screen.fill(color) 176 | 177 | # show the chess board 178 | self.screen.blit(self.board_img, self.board_dimensions) 179 | 180 | # call self.chess. something 181 | self.chess.play_turn() 182 | # draw pieces on the chess board 183 | self.chess.draw_pieces() 184 | 185 | 186 | def declare_winner(self, winner): 187 | # background color 188 | bg_color = (255, 255, 255) 189 | # set background color 190 | self.screen.fill(bg_color) 191 | # black color 192 | black_color = (0, 0, 0) 193 | # coordinates for play again button 194 | reset_btn = pygame.Rect(250, 300, 140, 50) 195 | # show reset button 196 | pygame.draw.rect(self.screen, black_color, reset_btn) 197 | 198 | # white color 199 | white_color = (255, 255, 255) 200 | # create fonts for texts 201 | big_font = pygame.font.SysFont("comicsansms", 50) 202 | small_font = pygame.font.SysFont("comicsansms", 20) 203 | 204 | # text to show winner 205 | text = winner + " wins!" 206 | winner_text = big_font.render(text, False, black_color) 207 | 208 | # create text to be shown on the reset button 209 | reset_label = "Play Again" 210 | reset_btn_label = small_font.render(reset_label, True, white_color) 211 | 212 | # show winner text 213 | self.screen.blit(winner_text, 214 | ((self.screen.get_width() - winner_text.get_width()) // 2, 215 | 150)) 216 | 217 | # show text on the reset button 218 | self.screen.blit(reset_btn_label, 219 | ((reset_btn.x + (reset_btn.width - reset_btn_label.get_width()) // 2, 220 | reset_btn.y + (reset_btn.height - reset_btn_label.get_height()) // 2))) 221 | 222 | # get pressed keys 223 | key_pressed = pygame.key.get_pressed() 224 | # 225 | util = Utils() 226 | 227 | # check if left mouse button was clicked 228 | if util.left_click_event(): 229 | # call function to get mouse event 230 | mouse_coords = util.get_mouse_event() 231 | 232 | # check if reset button was clicked 233 | if reset_btn.collidepoint(mouse_coords[0], mouse_coords[1]): 234 | # change button behavior as it is hovered 235 | pygame.draw.rect(self.screen, white_color, reset_btn, 3) 236 | 237 | # change menu flag 238 | self.menu_showed = False 239 | # check if enter or return key was pressed 240 | elif key_pressed[K_RETURN]: 241 | self.menu_showed = False 242 | # reset game 243 | self.chess.reset() 244 | # clear winner 245 | self.chess.winner = "" -------------------------------------------------------------------------------- /chess.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | import random 4 | 5 | from piece import Piece 6 | from utils import Utils 7 | 8 | import time 9 | 10 | class Chess(object): 11 | def __init__(self, screen, pieces_src, square_coords, square_length): 12 | # display surface 13 | self.screen = screen 14 | # create an object of class to show chess pieces on the board 15 | self.chess_pieces = Piece(pieces_src, cols=6, rows=2) 16 | # store coordinates of the chess board squares 17 | self.board_locations = square_coords 18 | # length of the side of a chess board square 19 | self.square_length = square_length 20 | # dictionary to keeping track of player turn 21 | self.turn = {"black": 0, 22 | "white": 0} 23 | 24 | # list containing possible moves for the selected piece 25 | self.moves = [] 26 | # 27 | self.utils = Utils() 28 | 29 | # mapping of piece names to index of list containing piece coordinates on spritesheet 30 | self.pieces = { 31 | "white_pawn": 5, 32 | "white_knight": 3, 33 | "white_bishop": 2, 34 | "white_rook": 4, 35 | "white_king": 0, 36 | "white_queen": 1, 37 | "black_pawn": 11, 38 | "black_knight": 9, 39 | "black_bishop": 8, 40 | "black_rook": 10, 41 | "black_king": 6, 42 | "black_queen": 7 43 | } 44 | 45 | # list containing captured pieces 46 | self.captured = [] 47 | # 48 | self.winner = "" 49 | 50 | self.reset() 51 | 52 | def reset(self): 53 | # clear moves lists 54 | self.moves = [] 55 | 56 | # randomize player turn 57 | x = random.randint(0, 1) 58 | if(x == 1): 59 | self.turn["black"] = 1 60 | elif(x == 0): 61 | self.turn["white"] = 1 62 | 63 | # two dimensonal dictionary containing details about each board location 64 | # storage format is [piece_name, currently_selected, x_y_coordinate] 65 | self.piece_location = {} 66 | x = 0 67 | for i in range(97, 105): 68 | a = 8 69 | y = 0 70 | self.piece_location[chr(i)] = {} 71 | while a>0: 72 | # [piece name, currently selected, board coordinates] 73 | self.piece_location[chr(i)][a] = ["", False, [x,y]] 74 | a = a - 1 75 | y = y + 1 76 | x = x + 1 77 | 78 | # reset the board 79 | for i in range(97, 105): 80 | x = 8 81 | while x>0: 82 | if(x==8): 83 | if(chr(i)=='a' or chr(i)=='h'): 84 | self.piece_location[chr(i)][x][0] = "black_rook" 85 | elif(chr(i)=='b' or chr(i)=='g'): 86 | self.piece_location[chr(i)][x][0] = "black_knight" 87 | elif(chr(i)=='c' or chr(i)=='f'): 88 | self.piece_location[chr(i)][x][0] = "black_bishop" 89 | elif(chr(i)=='d'): 90 | self.piece_location[chr(i)][x][0] = "black_queen" 91 | elif(chr(i)=='e'): 92 | self.piece_location[chr(i)][x][0] = "black_king" 93 | elif(x==7): 94 | self.piece_location[chr(i)][x][0] = "black_pawn" 95 | elif(x==2): 96 | self.piece_location[chr(i)][x][0] = "white_pawn" 97 | elif(x==1): 98 | if(chr(i)=='a' or chr(i)=='h'): 99 | self.piece_location[chr(i)][x][0] = "white_rook" 100 | elif(chr(i)=='b' or chr(i)=='g'): 101 | self.piece_location[chr(i)][x][0] = "white_knight" 102 | elif(chr(i)=='c' or chr(i)=='f'): 103 | self.piece_location[chr(i)][x][0] = "white_bishop" 104 | elif(chr(i)=='d'): 105 | self.piece_location[chr(i)][x][0] = "white_queen" 106 | elif(chr(i)=='e'): 107 | self.piece_location[chr(i)][x][0] = "white_king" 108 | x = x - 1 109 | 110 | 111 | # 112 | def play_turn(self): 113 | # white color 114 | white_color = (255, 255, 255) 115 | # create fonts for texts 116 | small_font = pygame.font.SysFont("comicsansms", 20) 117 | # create text to be shown on the game menu 118 | if self.turn["black"]: 119 | turn_text = small_font.render("Turn: Black", True, white_color) 120 | elif self.turn["white"]: 121 | turn_text = small_font.render("Turn: White", True, white_color) 122 | 123 | # show welcome text 124 | self.screen.blit(turn_text, 125 | ((self.screen.get_width() - turn_text.get_width()) // 2, 126 | 10)) 127 | 128 | # let player with black piece play 129 | if(self.turn["black"]): 130 | self.move_piece("black") 131 | # let player with white piece play 132 | elif(self.turn["white"]): 133 | self.move_piece("white") 134 | 135 | # method to draw pieces on the chess board 136 | def draw_pieces(self): 137 | transparent_green = (0,194,39,170) 138 | transparent_blue = (28,21,212,170) 139 | 140 | # create a transparent surface 141 | surface = pygame.Surface((self.square_length, self.square_length), pygame.SRCALPHA) 142 | surface.fill(transparent_green) 143 | 144 | surface1 = pygame.Surface((self.square_length, self.square_length), pygame.SRCALPHA) 145 | surface1.fill(transparent_blue) 146 | 147 | # loop to change background color of selected piece 148 | for val in self.piece_location.values(): 149 | for value in val.values() : 150 | # name of the piece in the current location 151 | piece_name = value[0] 152 | # x, y coordinates of the current piece 153 | piece_coord_x, piece_coord_y = value[2] 154 | 155 | # change background color of piece if it is selected 156 | if value[1] and len(value[0]) > 5: 157 | # if the piece selected is a black piece 158 | if value[0][:5] == "black": 159 | self.screen.blit(surface, self.board_locations[piece_coord_x][piece_coord_y]) 160 | if len(self.moves) > 0: 161 | for move in self.moves: 162 | x_coord = move[0] 163 | y_coord = move[1] 164 | if x_coord >= 0 and y_coord >= 0 and x_coord < 8 and y_coord < 8: 165 | self.screen.blit(surface, self.board_locations[x_coord][y_coord]) 166 | # if the piece selected is a white piece 167 | elif value[0][:5] == "white": 168 | self.screen.blit(surface1, self.board_locations[piece_coord_x][piece_coord_y]) 169 | if len(self.moves) > 0: 170 | for move in self.moves: 171 | x_coord = move[0] 172 | y_coord = move[1] 173 | if x_coord >= 0 and y_coord >= 0 and x_coord < 8 and y_coord < 8: 174 | self.screen.blit(surface1, self.board_locations[x_coord][y_coord]) 175 | 176 | # draw all chess pieces 177 | for val in self.piece_location.values(): 178 | for value in val.values() : 179 | # name of the piece in the current location 180 | piece_name = value[0] 181 | # x, y coordinates of the current piece 182 | piece_coord_x, piece_coord_y = value[2] 183 | # check if there is a piece at the square 184 | if(len(value[0]) > 1): 185 | # draw piece on the board 186 | self.chess_pieces.draw(self.screen, piece_name, 187 | self.board_locations[piece_coord_x][piece_coord_y]) 188 | 189 | 190 | # method to find the possible moves of the selected piece 191 | def possible_moves(self, piece_name, piece_coord): 192 | # list to store possible moves of the selected piece 193 | positions = [] 194 | # find the possible locations to put a piece 195 | if len(piece_name) > 0: 196 | # get x, y coordinate 197 | x_coord, y_coord = piece_coord 198 | # calculate moves for bishop 199 | if piece_name[6:] == "bishop": 200 | positions = self.diagonal_moves(positions, piece_name, piece_coord) 201 | 202 | # calculate moves for pawn 203 | elif piece_name[6:] == "pawn": 204 | # convert list index to dictionary key 205 | columnChar = chr(97 + x_coord) 206 | rowNo = 8 - y_coord 207 | 208 | # calculate moves for white pawn 209 | if piece_name == "black_pawn": 210 | if y_coord + 1 < 8: 211 | # get row in front of black pawn 212 | rowNo = rowNo - 1 213 | front_piece = self.piece_location[columnChar][rowNo][0] 214 | 215 | # pawns cannot move when blocked by another another pawn 216 | if(front_piece[6:] != "pawn"): 217 | positions.append([x_coord, y_coord+1]) 218 | # black pawns can move two positions ahead for first move 219 | if y_coord < 2: 220 | positions.append([x_coord, y_coord+2]) 221 | 222 | # EM PASSANT 223 | # diagonal to the left 224 | if x_coord - 1 >= 0 and y_coord + 1 < 8: 225 | x = x_coord - 1 226 | y = y_coord + 1 227 | 228 | # convert list index to dictionary key 229 | columnChar = chr(97 + x) 230 | rowNo = 8 - y 231 | to_capture = self.piece_location[columnChar][rowNo] 232 | 233 | if(to_capture[0][:5] == "white"): 234 | positions.append([x, y]) 235 | 236 | # diagonal to the right 237 | if x_coord + 1 < 8 and y_coord + 1 < 8: 238 | x = x_coord + 1 239 | y = y_coord + 1 240 | 241 | # convert list index to dictionary key 242 | columnChar = chr(97 + x) 243 | rowNo = 8 - y 244 | to_capture = self.piece_location[columnChar][rowNo] 245 | 246 | if(to_capture[0][:5] == "white"): 247 | positions.append([x, y]) 248 | 249 | # calculate moves for white pawn 250 | elif piece_name == "white_pawn": 251 | if y_coord - 1 >= 0: 252 | # get row in front of black pawn 253 | rowNo = rowNo + 1 254 | front_piece = self.piece_location[columnChar][rowNo][0] 255 | 256 | # pawns cannot move when blocked by another another pawn 257 | if(front_piece[6:] != "pawn"): 258 | positions.append([x_coord, y_coord-1]) 259 | # black pawns can move two positions ahead for first move 260 | if y_coord > 5: 261 | positions.append([x_coord, y_coord-2]) 262 | 263 | # EM PASSANT 264 | # diagonal to the left 265 | if x_coord - 1 >= 0 and y_coord - 1 >= 0: 266 | x = x_coord - 1 267 | y = y_coord - 1 268 | 269 | # convert list index to dictionary key 270 | columnChar = chr(97 + x) 271 | rowNo = 8 - y 272 | to_capture = self.piece_location[columnChar][rowNo] 273 | 274 | if(to_capture[0][:5] == "black"): 275 | positions.append([x, y]) 276 | 277 | 278 | # diagonal to the right 279 | if x_coord + 1 < 8 and y_coord - 1 >= 0: 280 | x = x_coord + 1 281 | y = y_coord - 1 282 | 283 | # convert list index to dictionary key 284 | columnChar = chr(97 + x) 285 | rowNo = 8 - y 286 | to_capture = self.piece_location[columnChar][rowNo] 287 | 288 | if(to_capture[0][:5] == "black"): 289 | positions.append([x, y]) 290 | 291 | 292 | # calculate moves for rook 293 | elif piece_name[6:] == "rook": 294 | # find linear moves 295 | positions = self.linear_moves(positions, piece_name, piece_coord) 296 | 297 | # calculate moves for knight 298 | elif piece_name[6:] == "knight": 299 | # left positions 300 | if(x_coord - 2) >= 0: 301 | if(y_coord - 1) >= 0: 302 | positions.append([x_coord-2, y_coord-1]) 303 | if(y_coord + 1) < 8: 304 | positions.append([x_coord-2, y_coord+1]) 305 | # top positions 306 | if(y_coord - 2) >= 0: 307 | if(x_coord - 1) >= 0: 308 | positions.append([x_coord-1, y_coord-2]) 309 | if(x_coord + 1) < 8: 310 | positions.append([x_coord+1, y_coord-2]) 311 | # right positions 312 | if(x_coord + 2) < 8: 313 | if(y_coord - 1) >= 0: 314 | positions.append([x_coord+2, y_coord-1]) 315 | if(y_coord + 1) < 8: 316 | positions.append([x_coord+2, y_coord+1]) 317 | # bottom positions 318 | if(y_coord + 2) < 8: 319 | if(x_coord - 1) >= 0: 320 | positions.append([x_coord-1, y_coord+2]) 321 | if(x_coord + 1) < 8: 322 | positions.append([x_coord+1, y_coord+2]) 323 | 324 | # calculate movs for king 325 | elif piece_name[6:] == "king": 326 | if(y_coord - 1) >= 0: 327 | # top spot 328 | positions.append([x_coord, y_coord-1]) 329 | 330 | if(y_coord + 1) < 8: 331 | # bottom spot 332 | positions.append([x_coord, y_coord+1]) 333 | 334 | if(x_coord - 1) >= 0: 335 | # left spot 336 | positions.append([x_coord-1, y_coord]) 337 | # top left spot 338 | if(y_coord - 1) >= 0: 339 | positions.append([x_coord-1, y_coord-1]) 340 | # bottom left spot 341 | if(y_coord + 1) < 8: 342 | positions.append([x_coord-1, y_coord+1]) 343 | 344 | if(x_coord + 1) < 8: 345 | # right spot 346 | positions.append([x_coord+1, y_coord]) 347 | # top right spot 348 | if(y_coord - 1) >= 0: 349 | positions.append([x_coord+1, y_coord-1]) 350 | # bottom right spot 351 | if(y_coord + 1) < 8: 352 | positions.append([x_coord+1, y_coord+1]) 353 | 354 | # calculate movs for queen 355 | elif piece_name[6:] == "queen": 356 | # find diagonal positions 357 | positions = self.diagonal_moves(positions, piece_name, piece_coord) 358 | 359 | # find linear moves 360 | positions = self.linear_moves(positions, piece_name, piece_coord) 361 | 362 | # list of positions to be removed 363 | to_remove = [] 364 | 365 | # remove positions that overlap other pieces of the current player 366 | for pos in positions: 367 | x, y = pos 368 | 369 | # convert list index to dictionary key 370 | columnChar = chr(97 + x) 371 | rowNo = 8 - y 372 | 373 | # find the pieces to remove 374 | des_piece_name = self.piece_location[columnChar][rowNo][0] 375 | if(des_piece_name[:5] == piece_name[:5]): 376 | to_remove.append(pos) 377 | 378 | # remove position from positions list 379 | for i in to_remove: 380 | positions.remove(i) 381 | 382 | # return list containing possible moves for the selected piece 383 | return positions 384 | 385 | 386 | def move_piece(self, turn): 387 | # get the coordinates of the square selected on the board 388 | square = self.get_selected_square() 389 | 390 | # if a square was selected 391 | if square: 392 | # get name of piece on the selected square 393 | piece_name = square[0] 394 | # color of piece on the selected square 395 | piece_color = piece_name[:5] 396 | # board column character 397 | columnChar = square[1] 398 | # board row number 399 | rowNo = square[2] 400 | 401 | # get x, y coordinates 402 | x, y = self.piece_location[columnChar][rowNo][2] 403 | 404 | # if there's a piece on the selected square 405 | if(len(piece_name) > 0) and (piece_color == turn): 406 | # find possible moves for thr piece 407 | self.moves = self.possible_moves(piece_name, [x,y]) 408 | 409 | # checkmate mechanism 410 | p = self.piece_location[columnChar][rowNo] 411 | 412 | for i in self.moves: 413 | if i == [x, y]: 414 | if(p[0][:5] == turn) or len(p[0]) == 0: 415 | self.validate_move([x,y]) 416 | else: 417 | self.capture_piece(turn, [columnChar, rowNo], [x,y]) 418 | 419 | # only the player with the turn gets to play 420 | if(piece_color == turn): 421 | # change selection flag from all other pieces 422 | for k in self.piece_location.keys(): 423 | for key in self.piece_location[k].keys(): 424 | self.piece_location[k][key][1] = False 425 | 426 | # change selection flag of the selected piece 427 | self.piece_location[columnChar][rowNo][1] = True 428 | 429 | 430 | def get_selected_square(self): 431 | # get left event 432 | left_click = self.utils.left_click_event() 433 | 434 | # if there's a mouse event 435 | if left_click: 436 | # get mouse event 437 | mouse_event = self.utils.get_mouse_event() 438 | 439 | for i in range(len(self.board_locations)): 440 | for j in range(len(self.board_locations)): 441 | rect = pygame.Rect(self.board_locations[i][j][0], self.board_locations[i][j][1], 442 | self.square_length, self.square_length) 443 | collision = rect.collidepoint(mouse_event[0], mouse_event[1]) 444 | if collision: 445 | selected = [rect.x, rect.y] 446 | # find x, y coordinates the selected square 447 | for k in range(len(self.board_locations)): 448 | # 449 | try: 450 | l = None 451 | l = self.board_locations[k].index(selected) 452 | if l != None: 453 | #reset color of all selected pieces 454 | for val in self.piece_location.values(): 455 | for value in val.values() : 456 | # [piece name, currently selected, board coordinates] 457 | if not value[1]: 458 | value[1] = False 459 | 460 | # get column character and row number of the chess piece 461 | columnChar = chr(97 + k) 462 | rowNo = 8 - l 463 | # get the name of the 464 | piece_name = self.piece_location[columnChar][rowNo][0] 465 | 466 | return [piece_name, columnChar, rowNo] 467 | except: 468 | pass 469 | else: 470 | return None 471 | 472 | 473 | def capture_piece(self, turn, chess_board_coord, piece_coord): 474 | # get x, y coordinate of the destination piece 475 | x, y = piece_coord 476 | 477 | # get chess board coordinate 478 | columnChar, rowNo = chess_board_coord 479 | 480 | p = self.piece_location[columnChar][rowNo] 481 | 482 | if p[0] == "white_king": 483 | self.winner = "Black" 484 | print("Black wins") 485 | elif p[0] == "black_king": 486 | self.winner = "White" 487 | print("White wins") 488 | 489 | # add the captured piece to list 490 | self.captured.append(p) 491 | # move source piece to its destination 492 | self.validate_move(piece_coord) 493 | 494 | 495 | def validate_move(self, destination): 496 | desColChar = chr(97 + destination[0]) 497 | desRowNo = 8 - destination[1] 498 | 499 | for k in self.piece_location.keys(): 500 | for key in self.piece_location[k].keys(): 501 | board_piece = self.piece_location[k][key] 502 | 503 | if board_piece[1]: 504 | # unselect the source piece 505 | self.piece_location[k][key][1] = False 506 | # get the name of the source piece 507 | piece_name = self.piece_location[k][key][0] 508 | # move the source piece to the destination piece 509 | self.piece_location[desColChar][desRowNo][0] = piece_name 510 | 511 | src_name = self.piece_location[k][key][0] 512 | # remove source piece from its current position 513 | self.piece_location[k][key][0] = "" 514 | 515 | # change turn 516 | if(self.turn["black"]): 517 | self.turn["black"] = 0 518 | self.turn["white"] = 1 519 | elif("white"): 520 | self.turn["black"] = 1 521 | self.turn["white"] = 0 522 | 523 | src_location = k + str(key) 524 | des_location = desColChar + str(desRowNo) 525 | print("{} moved from {} to {}".format(src_name, src_location, des_location)) 526 | 527 | 528 | # helper function to find diagonal moves 529 | def diagonal_moves(self, positions, piece_name, piece_coord): 530 | # reset x and y coordinate values 531 | x, y = piece_coord 532 | # find top left diagonal spots 533 | while(True): 534 | x = x - 1 535 | y = y - 1 536 | if(x < 0 or y < 0): 537 | break 538 | else: 539 | positions.append([x,y]) 540 | 541 | # convert list index to dictionary key 542 | columnChar = chr(97 + x) 543 | rowNo = 8 - y 544 | p = self.piece_location[columnChar][rowNo] 545 | 546 | # stop finding possible moves if blocked by a piece 547 | if len(p[0]) > 0 and piece_name[:5] != p[:5]: 548 | break 549 | 550 | # reset x and y coordinate values 551 | x, y = piece_coord 552 | # find bottom right diagonal spots 553 | while(True): 554 | x = x + 1 555 | y = y + 1 556 | if(x > 7 or y > 7): 557 | break 558 | else: 559 | positions.append([x,y]) 560 | 561 | # convert list index to dictionary key 562 | columnChar = chr(97 + x) 563 | rowNo = 8 - y 564 | p = self.piece_location[columnChar][rowNo] 565 | 566 | # stop finding possible moves if blocked by a piece 567 | if len(p[0]) > 0 and piece_name[:5] != p[:5]: 568 | break 569 | 570 | # reset x and y coordinate values 571 | x, y = piece_coord 572 | # find bottom left diagonal spots 573 | while(True): 574 | x = x - 1 575 | y = y + 1 576 | if (x < 0 or y > 7): 577 | break 578 | else: 579 | positions.append([x,y]) 580 | 581 | # convert list index to dictionary key 582 | columnChar = chr(97 + x) 583 | rowNo = 8 - y 584 | p = self.piece_location[columnChar][rowNo] 585 | 586 | # stop finding possible moves if blocked by a piece 587 | if len(p[0]) > 0 and piece_name[:5] != p[:5]: 588 | break 589 | 590 | # reset x and y coordinate values 591 | x, y = piece_coord 592 | # find top right diagonal spots 593 | while(True): 594 | x = x + 1 595 | y = y - 1 596 | if(x > 7 or y < 0): 597 | break 598 | else: 599 | positions.append([x,y]) 600 | 601 | # convert list index to dictionary key 602 | columnChar = chr(97 + x) 603 | rowNo = 8 - y 604 | p = self.piece_location[columnChar][rowNo] 605 | 606 | # stop finding possible moves if blocked by a piece 607 | if len(p[0]) > 0 and piece_name[:5] != p[:5]: 608 | break 609 | 610 | return positions 611 | 612 | 613 | # helper function to find horizontal and vertical moves 614 | def linear_moves(self, positions, piece_name, piece_coord): 615 | # reset x, y coordniate value 616 | x, y = piece_coord 617 | # horizontal moves to the left 618 | while(x > 0): 619 | x = x - 1 620 | positions.append([x,y]) 621 | 622 | # convert list index to dictionary key 623 | columnChar = chr(97 + x) 624 | rowNo = 8 - y 625 | p = self.piece_location[columnChar][rowNo] 626 | 627 | # stop finding possible moves if blocked by a piece 628 | if len(p[0]) > 0 and piece_name[:5] != p[:5]: 629 | break 630 | 631 | 632 | # reset x, y coordniate value 633 | x, y = piece_coord 634 | # horizontal moves to the right 635 | while(x < 7): 636 | x = x + 1 637 | positions.append([x,y]) 638 | 639 | # convert list index to dictionary key 640 | columnChar = chr(97 + x) 641 | rowNo = 8 - y 642 | p = self.piece_location[columnChar][rowNo] 643 | 644 | # stop finding possible moves if blocked by a piece 645 | if len(p[0]) > 0 and piece_name[:5] != p[:5]: 646 | break 647 | 648 | # reset x, y coordniate value 649 | x, y = piece_coord 650 | # vertical moves upwards 651 | while(y > 0): 652 | y = y - 1 653 | positions.append([x,y]) 654 | 655 | # convert list index to dictionary key 656 | columnChar = chr(97 + x) 657 | rowNo = 8 - y 658 | p = self.piece_location[columnChar][rowNo] 659 | 660 | # stop finding possible moves if blocked by a piece 661 | if len(p[0]) > 0 and piece_name[:5] != p[:5]: 662 | break 663 | 664 | # reset x, y coordniate value 665 | x, y = piece_coord 666 | # vertical moves downwards 667 | while(y < 7): 668 | y = y + 1 669 | positions.append([x,y]) 670 | 671 | # convert list index to dictionary key 672 | columnChar = chr(97 + x) 673 | rowNo = 8 - y 674 | p = self.piece_location[columnChar][rowNo] 675 | 676 | # stop finding possible moves if blocked by a piece 677 | if len(p[0]) > 0 and piece_name[:5] != p[:5]: 678 | break 679 | 680 | 681 | return positions --------------------------------------------------------------------------------