Piece

File containing implementation for each figure in the game. Code structure allows to easily add new Figures for more game variants.

  1"""File containing implementation for each figure in the game. Code structure allows to easily add new Figures for more game variants.
  2"""
  3
  4import customtkinter as ctk
  5from typing import Callable, Any
  6from PIL import Image
  7import platform
  8import os
  9if platform.system() == 'Windows':
 10    import pywinstyles
 11
 12from tools import resource_path, get_from_config, update_error_log
 13from properties import COLOR
 14
 15class Piece:
 16    """Parent class for every figure. Has general implementation of functions that can be reused.
 17    Abstract method which raises NotImplementedError if check_possible_moves() is not implemented to prevent adding figure that could cause errors when used.
 18    """
 19    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
 20        """Constructor:
 21             - loading assets
 22             - virtual function for checking possible moves
 23             - checking turns
 24             - updating assets
 25             - representation of the class for easier debugging
 26
 27        Args:
 28            color (str): Color of the figure.
 29            board : Board object on which figures will be placed.
 30            position (tuple[int, int]): Position on the board.
 31        """
 32        self.color: str = color
 33        self.board = board
 34        self.position: tuple[int, int] = position
 35        self.first_move: bool = False
 36        self.image: ctk.CTkImage | None = None
 37
 38    def check_possible_moves(self, color: str, checking: bool=False):
 39        """Virtual function.
 40
 41        Args:
 42            color (str): Color of the figure to move.
 43            checking (bool, optional): Flag indicating search. Defaults to False.
 44
 45        Raises:
 46            NotImplementedError: All Figures have to have this function implemented.
 47        """
 48        raise NotImplementedError
 49
 50    def check_turn(self, current_color: str) -> bool:
 51        """Checks which player has its right to move.
 52
 53        Args:
 54            current_color (str): Color of the clicked figure.
 55
 56        Returns:
 57            bool: True if its given color turn, False otherwise.
 58        """
 59        return False if current_color == self.color else True
 60
 61    def load_image(self, piece: str | None=None) -> None | ctk.CTkImage:
 62        """Loads asset for the piece.
 63
 64        Args:
 65            piece (str | None, optional): Piece string representation. Defaults to None.
 66
 67        Returns:
 68            None | ctk.CTkImage: If piece representation passed function will try to load asset. None otherwise.
 69
 70        Raises:
 71            FileExistsError: If file doesn't exist game will crash and give feedback in the console.
 72            FileNotFoundError: If file couldn't be found game will crash and give feedback in the console.
 73        """
 74        if not piece:
 75            piece_name = (self.__class__.__name__).lower()
 76        else:
 77            piece_name = piece
 78        path: str = resource_path(os.path.join('assets', f'{get_from_config('theme')}', f'{piece_name}_{self.color}.png'))
 79        try: 
 80            loaded_image = Image.open(path).convert('RGBA')
 81            size = int(get_from_config('size')) - 10
 82            if piece:
 83                return ctk.CTkImage(
 84                    light_image=loaded_image,
 85                    dark_image=loaded_image,
 86                    size=(size, size))
 87            self.image = ctk.CTkImage(
 88                light_image=loaded_image,
 89                dark_image=loaded_image,
 90                size=(size, size))
 91        except (FileExistsError, FileNotFoundError) as e:
 92            update_error_log(e)
 93        return None
 94
 95    def update_image(self) -> None:
 96        """Function updating cell asset.
 97        """
 98        self.load_image()
 99        self.board.board[self.position[0]][self.position[1]].configure(image=self.image)
100
101    def __str__(self) -> str:
102        """Overriding string representation of the class used in print() for example.
103
104        Returns:
105            str: Representation of the class Piece: {piece name} Color:{piece color}
106        """
107        return f'Piece: {self.__class__.__name__} | Color: {'white' if self.color == 'w' else 'black'} | Position {self.position}'
108
109class Pawn(Piece):
110    """Implementation of the pawn. Supports en passant, promotions, moving and capturing.
111
112    Args:
113        Piece (Piece): Inheritance from the master class Piece to access general functions of the figures.
114    """
115    def __init__(self, color: str, board, position: tuple[int, int], notation_func: Callable) -> None:
116        """Constructor:
117            basic setup of master class Piece
118            loads additional flags necessary for correct pawn implementation
119
120        Args:
121            color (str): Color of the pawn.
122            board : Board object on which the pawn will be placed. 
123            position (tuple[int, int]): Position of the pawn on the board.
124            notation_func (Callable): Callable function from notation module used to note the move.
125        """
126        super().__init__(color, board, position)
127        self.color: str = color # b | w
128        self.position: tuple[int, int] = position
129        self.board = board # Board object (import loop occurs if imported for type annotation)
130        self.load_image()
131        self.first_move: bool = True
132        self.moved_by_two: bool = False
133        self.can_en_passant: bool = False
134        self.move: int = 1 if self.color == 'b' else -1
135        self.notation_func: Callable = notation_func
136
137    def check_possible_moves(self, color: str, checking: bool=False) -> list[tuple[int, int]]:
138        """Function checking all possible moves for the Pawn.
139
140        Args:
141            color (str): Color of the pawn.
142            checking (bool, optional): Flag to know when player makes move and when algorithm checks possible moves. Defaults to False.
143
144        Returns:
145            list[tuple[int, int]]: Returns list of all legal positions to which pawn can move.
146        """
147        possible_moves: list[tuple[int, int]] = []
148        if self.check_turn(color):
149            return possible_moves
150        move = self.move
151        x: int = self.position[0]
152        y: int = self.position[1]
153        forward_one = (x + move, y)
154        forward_two = (x + move + move, y)
155        if not self.board.board[forward_one[0]][forward_one[1]].figure:
156            possible_moves.append(forward_one)
157            if self.first_move and not self.board.board[forward_two[0]][forward_two[1]].figure:
158                possible_moves.append(forward_two)
159        for offset in [-1, 1]:
160            if 0 <= y + offset < 8:
161                capture_position = (x + move, y + offset)
162                target_square = self.board.board[capture_position[0]][capture_position[1]]
163                if target_square.figure and target_square.figure.color != self.color:
164                    possible_moves.append(capture_position)
165                adjacent_pawn = self.board.board[x][y + offset].figure
166                if isinstance(adjacent_pawn, Pawn) and adjacent_pawn.color != self.color and adjacent_pawn.moved_by_two:
167                    possible_moves.append((x + move, y + offset))
168                    self.can_en_passant = True
169        return possible_moves
170
171    def choose_figure(self, event: Any, figure, choose_piece_menu: ctk.CTkLabel, choose_piece_menu_1: ctk.CTkFrame) -> None:
172        """Function responsible of promoting the pawn to other figure.
173
174        Args:
175            event (Any): Event type. Doesn't matter but is required parameter by customtkinter.
176            figure : Figure chosen by the player.
177            choose_piece_menu (ctk.CTkLabel): Label widget.
178            choose_piece_menu_1 (ctk.CTkFrame): Frame widget.
179        """
180        x, y = self.position[0], self.position[1]
181        self.board.board[x][y].figure = figure(self.color, self.board, self.position)
182        self.board.board[x][y].update()
183        self.notation_func(self.board.board[x][y].figure.__class__.__name__)
184        choose_piece_menu.destroy()
185        choose_piece_menu_1.destroy()
186
187    def create_button(self, choose_piece_menu: ctk.CTkLabel, figure, choose_piece_menu_1: ctk.CTkFrame) -> None:
188        """Function creating the button for one of the figure possible to choose from the menu.
189
190        Args:
191            choose_piece_menu (ctk.CTkLabel): Label in which button will be created.
192            figure : Figure that will be possible to choose by clicking the button.
193            choose_piece_menu_1 (ctk.CTkFrame): Frame widget.
194        """
195        piece_image: ctk.CTkImage | None = self.load_image(str(figure.__name__))
196        button_figure: ctk.CTkLabel = ctk.CTkLabel(
197            master        = choose_piece_menu,
198            text          = '',
199            image         = piece_image,
200            corner_radius = 0
201        )
202        button_figure.pack(side=ctk.LEFT, padx=10, pady=10)
203        button_figure.bind('<Button-1>', lambda e: self.choose_figure(e, figure, choose_piece_menu, choose_piece_menu_1))
204
205    def promote(self) -> bool:
206        """Function checking if pawn is ont the end of the board. If so it force the player to choose the figure they want to promote to.
207
208        Returns:
209            bool: True if pawn was promoted. False otherwise.
210        """
211        if self.position[0] in {0, 7}:
212            choose_piece_menu_1: ctk.CTkFrame = ctk.CTkFrame(
213                master   = self.board, corner_radius=0,
214                fg_color = COLOR.BACKGROUND
215            )
216            choose_piece_menu_1.place(relx=0, rely=0, relwidth=1, relheight=1)
217            if platform.system() == 'Windows':
218                pywinstyles.set_opacity(choose_piece_menu_1, value=0.01, color="#000001")
219            choose_piece_menu: ctk.CTkFrame = ctk.CTkFrame(
220                master        = self.board,
221                fg_color      = COLOR.BACKGROUND,
222                corner_radius = 0,
223                border_color  = COLOR.DARK_TEXT,
224                border_width  = 4
225            )
226            choose_piece_menu.place(relx=0.5, rely=0.5, anchor=ctk.CENTER)
227            possible_figures = [Knight, Bishop, Rook, Queen]
228            for figure in possible_figures:
229                self.create_button(choose_piece_menu, figure, choose_piece_menu_1)
230            return True
231        return False
232
233    def notate(self, figure_name: Piece, moves_record, capture: bool, check: bool, checkmate: bool) -> None:
234        """Helper function to note the move of the pawn.
235
236        Args:
237            figure_name (Piece): Name of the figure.
238            moves_record : Object of MovesRecord. Cannot specify type due to circular imports.
239            capture (bool): Flag to check if capture occurred.
240            check (bool): Flag to check if after pawn move check occurred.
241            checkmate (bool): Flag to check if after pawn move checkmate occurred.
242        """
243        moves_record.record_move(figure_name,
244            capture=capture, castle=None, check=check, checkmate=checkmate,
245            promotion=f'{self.board.board[self.position[0]][self.position[1]].figure.__class__.__name__[0]}'
246        )
247
248class Knight(Piece):
249    """Implementation of the Knight.
250
251    Args:
252        Piece : Inheritance from the master class Piece to access general functions of the figures.
253    """
254    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
255        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
256
257        Args:
258            color (str): Color of the Knight.
259            board : Board object on which figures will be placed.
260            position (tuple[int, int]): Position on the board.
261        """
262        super().__init__(color, board, position)
263        self.color: str = color
264        self.board = board
265        self.load_image()
266        self.moves: list[tuple[int, int]] = [   
267            (-2,-1), (-2, 1),
268            (-1,-2), (-1, 2),
269            ( 1,-2), ( 1, 2),
270            ( 2,-1), ( 2, 1)   
271        ]
272        self.special_cases: dict[tuple[int, int], list[int]] = {
273            (1, 1): [0, 1, 2, 4],
274            (1, 6): [0, 1, 3, 5],
275            (6, 6): [3, 5, 6, 7],
276            (6, 1): [2, 4, 3, 5],
277            (0, 0): [0, 1, 2, 3, 4, 6],
278            (0, 1): [0, 1, 2, 3, 4],
279            (1, 0): [0, 1, 2, 4, 7]
280        }
281
282    def check_moves(self, exceptions: list[int]) -> list[tuple[int, int]]:
283        """Function checking possible moves for the Knight.
284
285        Args:
286            exceptions (list[int]): List of position that are not legal.
287
288        Returns:
289            list[tuple[int, int]]: List of all legal moves.
290        """
291        possible_moves: list[tuple[int, int]] = []
292        for i, move in enumerate(self.moves):
293            if i in exceptions:
294                continue
295            new_position = (self.position[0] + move[0], self.position[1] + move[1])
296            if 0 <= new_position[0] <= 7 and 0 <= new_position[1] <= 7:
297                target_square = self.board.board[new_position[0]][new_position[1]]
298                if not target_square.figure or target_square.figure.color != self.color:
299                    possible_moves.append(new_position)
300        return possible_moves
301
302    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
303        """Function checking all possible moves for the Knight.
304
305        Args:
306            color (str): Color of the Knight.
307            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
308
309        Returns:
310            list[tuple[int, int]]: List of all legal position to which Knight can move.
311        """
312        if self.check_turn(color) and not checking:
313            return []
314        if 2 <= self.position[0] <= 5 and self.position[1] in {1, 6} and self.position not in self.special_cases:
315            if self.position[1] == 1:
316                return self.check_moves([2, 4])
317            if self.position[1] == 6:
318                return self.check_moves([3, 5])
319        if 2 <= self.position[1] <= 5 and self.position[0] in {1, 6} and self.position not in self.special_cases:
320            if self.position[0] == 1:
321                return self.check_moves([0, 1])
322            if self.position[0] == 6:
323                return self.check_moves([6, 7])
324        if self.position in self.special_cases:
325            return self.check_moves(self.special_cases[self.position])
326        return self.check_moves([])
327
328class Bishop(Piece):
329    """Implementation of the Bishop.
330
331    Args:
332        Piece : Inheritance from the master class Piece to access general functions of the figures.
333    """
334    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
335        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
336
337        Args:
338            color (str): Color of the Bishop.
339            board : Board object on which figures will be placed.
340            position (tuple[int, int]): Position on the board.
341        """
342        super().__init__(color, board, position)
343        self.color: str = color
344        self.board = board
345        self.load_image()
346        self.moves_vec = [
347            (-1, -1), (-1, 1),
348            ( 1, -1), (1,  1)
349        ]
350
351    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
352        """Function checking all possible moves of the Bishop.
353
354        Args:
355            color (str): Color of the Bishop.
356            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
357
358        Returns:
359            list[tuple[int, int]]: List of all legal position to which Bishop can move.
360        """
361        possible_moves: list[tuple[int, int]] = []
362        if self.check_turn(color) and not checking:
363            return possible_moves
364        for move in self.moves_vec:
365            for i in range(1, 8):
366                multiplied_vec = tuple(x * i for x in move)
367                x = self.position[0] + multiplied_vec[0]
368                y = self.position[1] + multiplied_vec[1]
369                if 0 <= x <= 7 and 0 <= y <= 7:
370                    if not self.board.board[x][y].figure:
371                        possible_moves.append((x, y))
372                    elif self.board.board[x][y].figure.color != self.color:
373                        possible_moves.append((x, y))
374                        break
375                    else:
376                        break
377                else:
378                    break
379        return possible_moves
380
381class Rook(Piece):
382    """Implementation of the Rook.
383
384    Args:
385        Piece : Inheritance from the master class Piece to access general functions of the figures.
386    """
387    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
388        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
389
390        Args:
391            color (str): Color of the Rook.
392            board : Board object on which figures will be placed.
393            position (tuple[int, int]): Position on the board.
394        """
395        super().__init__(color, board, position)
396        self.color: str = color
397        self.board = board
398        self.load_image()
399        self.first_move: bool = True
400        self.moves_vec = [
401            (-1, 0), (0,-1),
402            ( 1, 0), (0, 1)
403        ]
404
405    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
406        """Function checking all possible moves of the Rook.
407
408        Args:
409            color (str): Color of the Rook.
410            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
411
412        Returns:
413            list[tuple[int, int]]: List of all legal position to which Rook can move.
414        """
415        possible_moves: list[tuple[int, int]] = []
416        if self.check_turn(color) and not checking:
417            return possible_moves
418        for move in self.moves_vec:
419            for i in range(1, 8):
420                multiplied_vec = tuple(x * i for x in move)
421                x = self.position[0] + multiplied_vec[0]
422                y = self.position[1] + multiplied_vec[1]
423                if 0 <= x <= 7 and 0 <= y <= 7:
424                    if not self.board.board[x][y].figure:
425                        possible_moves.append((x, y))
426                    elif self.board.board[x][y].figure.color != self.color:
427                        possible_moves.append((x, y))
428                        break
429                    else:
430                        break
431                else:
432                    break
433        return possible_moves
434
435class Queen(Piece):
436    """Implementation of the Queen.
437
438    Args:
439        Piece : Inheritance from the master class Piece to access general functions of the figures.
440    """
441    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
442        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
443
444        Args:
445            color (str): Color of the Queen.
446            board : Board object on which figures will be placed.
447            position (tuple[int, int]): Position on the board.
448        """
449        super().__init__(color, board, position)
450        self.color: str = color
451        self.board = board
452        self.load_image()
453        self.moves_vec = [
454            (-1, 0), (0,-1),
455            ( 1, 0), (0, 1),
456            (-1, -1), (-1, 1),
457            ( 1, -1), (1,  1)
458        ]
459
460    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
461        """Function checking all possible moves of the Queen.
462
463        Args:
464            color (str): Color of the Queen.
465            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
466
467        Returns:
468            list[tuple[int, int]]: List of all legal position to which Queen can move.
469        """
470        possible_moves: list[tuple[int, int]] = []
471        if self.check_turn(color) and not checking:
472            return possible_moves
473        for move in self.moves_vec:
474            for i in range(1, 8):
475                multiplied_vec = tuple(x * i for x in move)
476                x = self.position[0] + multiplied_vec[0]
477                y = self.position[1] + multiplied_vec[1]
478                if 0 <= x <= 7 and 0 <= y <= 7:
479                    if not self.board.board[x][y].figure:
480                        possible_moves.append((x, y))
481                    elif self.board.board[x][y].figure.color != self.color:
482                        possible_moves.append((x, y))
483                        break
484                    else:
485                        break
486                else:
487                    break
488        return possible_moves
489
490class King(Piece):
491    """Implementation of the King.
492
493    Args:
494        Piece : Inheritance from the master class Piece to access general functions of the figures.
495    """
496    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
497        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
498
499        Args:
500            color (str): Color of the King.
501            board : Board object on which figures will be placed.
502            position (tuple[int, int]): Position on the board.
503        """
504        super().__init__(color, board, position)
505        self.color: str = color
506        self.board = board
507        self.load_image()
508        self.first_move: bool = True
509        self.can_castle: bool = False
510
511    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
512        """Function checking all possible moves of the King.
513
514        Args:
515            color (str): Color of the King.
516            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
517
518        Returns:
519            list[tuple[int, int]]: List of all legal position to which King can move.
520        """
521        possible_moves: list[tuple[int, int]] = []
522        if self.check_turn(color) and not checking:
523            return possible_moves
524        for i in range(max(0, self.position[0] - 1), min(8, self.position[0] + 2)):
525            for j in range(max(0, self.position[1] - 1), min(8, self.position[1] + 2)):
526                if not self.board.board[i][j].figure:
527                    possible_moves.append((i, j))
528                if self.board.board[i][j].figure and self.board.board[i][j].figure.color != self.color:
529                    possible_moves.append((i, j))
530        if self.first_move and not checking:
531            possible_moves.extend(self.get_castling_moves())
532        return possible_moves
533
534    def get_castling_moves(self) -> list[tuple[int, int]]:
535        """Function checking moves for castle.
536
537        Returns:
538            list[tuple[int, int]]: List of legal positions.
539        """
540        castling_moves = []
541        row = self.position[0]
542        if self.can_castle_kingside():
543            castling_moves.append((row, 6))
544        if self.can_castle_queenside():
545            castling_moves.append((row, 2))
546        return castling_moves
547
548    def can_castle_kingside(self) -> bool:
549        """Function checking if King can perform castle from King side.
550
551        Returns:
552            bool: True if King can castle. False otherwise.
553        """
554        row, col = self.position
555        if self.first_move and isinstance(self.board.board[row][7].figure, Rook) and self.board.board[row][7].figure.first_move:
556            for i in range(col + 1, 7):
557                if self.board.board[row][i].figure or self.board.is_under_attack((row, i), self.color):
558                    return False
559            if self.board.is_under_attack((row, 6), self.color) or self.board.is_under_attack((row, col), self.color):
560                return False
561            return True
562        return False
563
564    def can_castle_queenside(self) -> bool:
565        """Function checking if King can perform castle from Queen side.
566
567        Returns:
568            bool: True if King can castle. False otherwise.
569        """
570        row, col = self.position
571        if self.first_move and isinstance(self.board.board[row][0].figure, Rook) and self.board.board[row][0].figure.first_move:
572            for i in range(col - 1, 0, -1):
573                if self.board.board[row][i].figure or self.board.is_under_attack((row, i), self.color):
574                    return False
575            if self.board.is_under_attack((row, 2), self.color) or self.board.is_under_attack((row, col), self.color):
576                return False
577            return True
578        return False
class Piece:
 16class Piece:
 17    """Parent class for every figure. Has general implementation of functions that can be reused.
 18    Abstract method which raises NotImplementedError if check_possible_moves() is not implemented to prevent adding figure that could cause errors when used.
 19    """
 20    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
 21        """Constructor:
 22             - loading assets
 23             - virtual function for checking possible moves
 24             - checking turns
 25             - updating assets
 26             - representation of the class for easier debugging
 27
 28        Args:
 29            color (str): Color of the figure.
 30            board : Board object on which figures will be placed.
 31            position (tuple[int, int]): Position on the board.
 32        """
 33        self.color: str = color
 34        self.board = board
 35        self.position: tuple[int, int] = position
 36        self.first_move: bool = False
 37        self.image: ctk.CTkImage | None = None
 38
 39    def check_possible_moves(self, color: str, checking: bool=False):
 40        """Virtual function.
 41
 42        Args:
 43            color (str): Color of the figure to move.
 44            checking (bool, optional): Flag indicating search. Defaults to False.
 45
 46        Raises:
 47            NotImplementedError: All Figures have to have this function implemented.
 48        """
 49        raise NotImplementedError
 50
 51    def check_turn(self, current_color: str) -> bool:
 52        """Checks which player has its right to move.
 53
 54        Args:
 55            current_color (str): Color of the clicked figure.
 56
 57        Returns:
 58            bool: True if its given color turn, False otherwise.
 59        """
 60        return False if current_color == self.color else True
 61
 62    def load_image(self, piece: str | None=None) -> None | ctk.CTkImage:
 63        """Loads asset for the piece.
 64
 65        Args:
 66            piece (str | None, optional): Piece string representation. Defaults to None.
 67
 68        Returns:
 69            None | ctk.CTkImage: If piece representation passed function will try to load asset. None otherwise.
 70
 71        Raises:
 72            FileExistsError: If file doesn't exist game will crash and give feedback in the console.
 73            FileNotFoundError: If file couldn't be found game will crash and give feedback in the console.
 74        """
 75        if not piece:
 76            piece_name = (self.__class__.__name__).lower()
 77        else:
 78            piece_name = piece
 79        path: str = resource_path(os.path.join('assets', f'{get_from_config('theme')}', f'{piece_name}_{self.color}.png'))
 80        try: 
 81            loaded_image = Image.open(path).convert('RGBA')
 82            size = int(get_from_config('size')) - 10
 83            if piece:
 84                return ctk.CTkImage(
 85                    light_image=loaded_image,
 86                    dark_image=loaded_image,
 87                    size=(size, size))
 88            self.image = ctk.CTkImage(
 89                light_image=loaded_image,
 90                dark_image=loaded_image,
 91                size=(size, size))
 92        except (FileExistsError, FileNotFoundError) as e:
 93            update_error_log(e)
 94        return None
 95
 96    def update_image(self) -> None:
 97        """Function updating cell asset.
 98        """
 99        self.load_image()
100        self.board.board[self.position[0]][self.position[1]].configure(image=self.image)
101
102    def __str__(self) -> str:
103        """Overriding string representation of the class used in print() for example.
104
105        Returns:
106            str: Representation of the class Piece: {piece name} Color:{piece color}
107        """
108        return f'Piece: {self.__class__.__name__} | Color: {'white' if self.color == 'w' else 'black'} | Position {self.position}'

Parent class for every figure. Has general implementation of functions that can be reused. Abstract method which raises NotImplementedError if check_possible_moves() is not implemented to prevent adding figure that could cause errors when used.

Piece(color: str, board, position: tuple[int, int])
20    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
21        """Constructor:
22             - loading assets
23             - virtual function for checking possible moves
24             - checking turns
25             - updating assets
26             - representation of the class for easier debugging
27
28        Args:
29            color (str): Color of the figure.
30            board : Board object on which figures will be placed.
31            position (tuple[int, int]): Position on the board.
32        """
33        self.color: str = color
34        self.board = board
35        self.position: tuple[int, int] = position
36        self.first_move: bool = False
37        self.image: ctk.CTkImage | None = None
Constructor:
  • loading assets
  • virtual function for checking possible moves
  • checking turns
  • updating assets
  • representation of the class for easier debugging
Arguments:
  • color (str): Color of the figure.
  • board : Board object on which figures will be placed.
  • position (tuple[int, int]): Position on the board.
color: str
board
position: tuple[int, int]
first_move: bool
image: customtkinter.windows.widgets.image.ctk_image.CTkImage | None
def check_possible_moves(self, color: str, checking: bool = False):
39    def check_possible_moves(self, color: str, checking: bool=False):
40        """Virtual function.
41
42        Args:
43            color (str): Color of the figure to move.
44            checking (bool, optional): Flag indicating search. Defaults to False.
45
46        Raises:
47            NotImplementedError: All Figures have to have this function implemented.
48        """
49        raise NotImplementedError

Virtual function.

Arguments:
  • color (str): Color of the figure to move.
  • checking (bool, optional): Flag indicating search. Defaults to False.
Raises:
  • NotImplementedError: All Figures have to have this function implemented.
def check_turn(self, current_color: str) -> bool:
51    def check_turn(self, current_color: str) -> bool:
52        """Checks which player has its right to move.
53
54        Args:
55            current_color (str): Color of the clicked figure.
56
57        Returns:
58            bool: True if its given color turn, False otherwise.
59        """
60        return False if current_color == self.color else True

Checks which player has its right to move.

Arguments:
  • current_color (str): Color of the clicked figure.
Returns:

bool: True if its given color turn, False otherwise.

def load_image( self, piece: str | None = None) -> None | customtkinter.windows.widgets.image.ctk_image.CTkImage:
62    def load_image(self, piece: str | None=None) -> None | ctk.CTkImage:
63        """Loads asset for the piece.
64
65        Args:
66            piece (str | None, optional): Piece string representation. Defaults to None.
67
68        Returns:
69            None | ctk.CTkImage: If piece representation passed function will try to load asset. None otherwise.
70
71        Raises:
72            FileExistsError: If file doesn't exist game will crash and give feedback in the console.
73            FileNotFoundError: If file couldn't be found game will crash and give feedback in the console.
74        """
75        if not piece:
76            piece_name = (self.__class__.__name__).lower()
77        else:
78            piece_name = piece
79        path: str = resource_path(os.path.join('assets', f'{get_from_config('theme')}', f'{piece_name}_{self.color}.png'))
80        try: 
81            loaded_image = Image.open(path).convert('RGBA')
82            size = int(get_from_config('size')) - 10
83            if piece:
84                return ctk.CTkImage(
85                    light_image=loaded_image,
86                    dark_image=loaded_image,
87                    size=(size, size))
88            self.image = ctk.CTkImage(
89                light_image=loaded_image,
90                dark_image=loaded_image,
91                size=(size, size))
92        except (FileExistsError, FileNotFoundError) as e:
93            update_error_log(e)
94        return None

Loads asset for the piece.

Arguments:
  • piece (str | None, optional): Piece string representation. Defaults to None.
Returns:

None | ctk.CTkImage: If piece representation passed function will try to load asset. None otherwise.

Raises:
  • FileExistsError: If file doesn't exist game will crash and give feedback in the console.
  • FileNotFoundError: If file couldn't be found game will crash and give feedback in the console.
def update_image(self) -> None:
 96    def update_image(self) -> None:
 97        """Function updating cell asset.
 98        """
 99        self.load_image()
100        self.board.board[self.position[0]][self.position[1]].configure(image=self.image)

Function updating cell asset.

class Pawn(Piece):
110class Pawn(Piece):
111    """Implementation of the pawn. Supports en passant, promotions, moving and capturing.
112
113    Args:
114        Piece (Piece): Inheritance from the master class Piece to access general functions of the figures.
115    """
116    def __init__(self, color: str, board, position: tuple[int, int], notation_func: Callable) -> None:
117        """Constructor:
118            basic setup of master class Piece
119            loads additional flags necessary for correct pawn implementation
120
121        Args:
122            color (str): Color of the pawn.
123            board : Board object on which the pawn will be placed. 
124            position (tuple[int, int]): Position of the pawn on the board.
125            notation_func (Callable): Callable function from notation module used to note the move.
126        """
127        super().__init__(color, board, position)
128        self.color: str = color # b | w
129        self.position: tuple[int, int] = position
130        self.board = board # Board object (import loop occurs if imported for type annotation)
131        self.load_image()
132        self.first_move: bool = True
133        self.moved_by_two: bool = False
134        self.can_en_passant: bool = False
135        self.move: int = 1 if self.color == 'b' else -1
136        self.notation_func: Callable = notation_func
137
138    def check_possible_moves(self, color: str, checking: bool=False) -> list[tuple[int, int]]:
139        """Function checking all possible moves for the Pawn.
140
141        Args:
142            color (str): Color of the pawn.
143            checking (bool, optional): Flag to know when player makes move and when algorithm checks possible moves. Defaults to False.
144
145        Returns:
146            list[tuple[int, int]]: Returns list of all legal positions to which pawn can move.
147        """
148        possible_moves: list[tuple[int, int]] = []
149        if self.check_turn(color):
150            return possible_moves
151        move = self.move
152        x: int = self.position[0]
153        y: int = self.position[1]
154        forward_one = (x + move, y)
155        forward_two = (x + move + move, y)
156        if not self.board.board[forward_one[0]][forward_one[1]].figure:
157            possible_moves.append(forward_one)
158            if self.first_move and not self.board.board[forward_two[0]][forward_two[1]].figure:
159                possible_moves.append(forward_two)
160        for offset in [-1, 1]:
161            if 0 <= y + offset < 8:
162                capture_position = (x + move, y + offset)
163                target_square = self.board.board[capture_position[0]][capture_position[1]]
164                if target_square.figure and target_square.figure.color != self.color:
165                    possible_moves.append(capture_position)
166                adjacent_pawn = self.board.board[x][y + offset].figure
167                if isinstance(adjacent_pawn, Pawn) and adjacent_pawn.color != self.color and adjacent_pawn.moved_by_two:
168                    possible_moves.append((x + move, y + offset))
169                    self.can_en_passant = True
170        return possible_moves
171
172    def choose_figure(self, event: Any, figure, choose_piece_menu: ctk.CTkLabel, choose_piece_menu_1: ctk.CTkFrame) -> None:
173        """Function responsible of promoting the pawn to other figure.
174
175        Args:
176            event (Any): Event type. Doesn't matter but is required parameter by customtkinter.
177            figure : Figure chosen by the player.
178            choose_piece_menu (ctk.CTkLabel): Label widget.
179            choose_piece_menu_1 (ctk.CTkFrame): Frame widget.
180        """
181        x, y = self.position[0], self.position[1]
182        self.board.board[x][y].figure = figure(self.color, self.board, self.position)
183        self.board.board[x][y].update()
184        self.notation_func(self.board.board[x][y].figure.__class__.__name__)
185        choose_piece_menu.destroy()
186        choose_piece_menu_1.destroy()
187
188    def create_button(self, choose_piece_menu: ctk.CTkLabel, figure, choose_piece_menu_1: ctk.CTkFrame) -> None:
189        """Function creating the button for one of the figure possible to choose from the menu.
190
191        Args:
192            choose_piece_menu (ctk.CTkLabel): Label in which button will be created.
193            figure : Figure that will be possible to choose by clicking the button.
194            choose_piece_menu_1 (ctk.CTkFrame): Frame widget.
195        """
196        piece_image: ctk.CTkImage | None = self.load_image(str(figure.__name__))
197        button_figure: ctk.CTkLabel = ctk.CTkLabel(
198            master        = choose_piece_menu,
199            text          = '',
200            image         = piece_image,
201            corner_radius = 0
202        )
203        button_figure.pack(side=ctk.LEFT, padx=10, pady=10)
204        button_figure.bind('<Button-1>', lambda e: self.choose_figure(e, figure, choose_piece_menu, choose_piece_menu_1))
205
206    def promote(self) -> bool:
207        """Function checking if pawn is ont the end of the board. If so it force the player to choose the figure they want to promote to.
208
209        Returns:
210            bool: True if pawn was promoted. False otherwise.
211        """
212        if self.position[0] in {0, 7}:
213            choose_piece_menu_1: ctk.CTkFrame = ctk.CTkFrame(
214                master   = self.board, corner_radius=0,
215                fg_color = COLOR.BACKGROUND
216            )
217            choose_piece_menu_1.place(relx=0, rely=0, relwidth=1, relheight=1)
218            if platform.system() == 'Windows':
219                pywinstyles.set_opacity(choose_piece_menu_1, value=0.01, color="#000001")
220            choose_piece_menu: ctk.CTkFrame = ctk.CTkFrame(
221                master        = self.board,
222                fg_color      = COLOR.BACKGROUND,
223                corner_radius = 0,
224                border_color  = COLOR.DARK_TEXT,
225                border_width  = 4
226            )
227            choose_piece_menu.place(relx=0.5, rely=0.5, anchor=ctk.CENTER)
228            possible_figures = [Knight, Bishop, Rook, Queen]
229            for figure in possible_figures:
230                self.create_button(choose_piece_menu, figure, choose_piece_menu_1)
231            return True
232        return False
233
234    def notate(self, figure_name: Piece, moves_record, capture: bool, check: bool, checkmate: bool) -> None:
235        """Helper function to note the move of the pawn.
236
237        Args:
238            figure_name (Piece): Name of the figure.
239            moves_record : Object of MovesRecord. Cannot specify type due to circular imports.
240            capture (bool): Flag to check if capture occurred.
241            check (bool): Flag to check if after pawn move check occurred.
242            checkmate (bool): Flag to check if after pawn move checkmate occurred.
243        """
244        moves_record.record_move(figure_name,
245            capture=capture, castle=None, check=check, checkmate=checkmate,
246            promotion=f'{self.board.board[self.position[0]][self.position[1]].figure.__class__.__name__[0]}'
247        )

Implementation of the pawn. Supports en passant, promotions, moving and capturing.

Arguments:
  • Piece (Piece): Inheritance from the master class Piece to access general functions of the figures.
Pawn( color: str, board, position: tuple[int, int], notation_func: Callable)
116    def __init__(self, color: str, board, position: tuple[int, int], notation_func: Callable) -> None:
117        """Constructor:
118            basic setup of master class Piece
119            loads additional flags necessary for correct pawn implementation
120
121        Args:
122            color (str): Color of the pawn.
123            board : Board object on which the pawn will be placed. 
124            position (tuple[int, int]): Position of the pawn on the board.
125            notation_func (Callable): Callable function from notation module used to note the move.
126        """
127        super().__init__(color, board, position)
128        self.color: str = color # b | w
129        self.position: tuple[int, int] = position
130        self.board = board # Board object (import loop occurs if imported for type annotation)
131        self.load_image()
132        self.first_move: bool = True
133        self.moved_by_two: bool = False
134        self.can_en_passant: bool = False
135        self.move: int = 1 if self.color == 'b' else -1
136        self.notation_func: Callable = notation_func
Constructor:

basic setup of master class Piece loads additional flags necessary for correct pawn implementation

Arguments:
  • color (str): Color of the pawn.
  • board : Board object on which the pawn will be placed.
  • position (tuple[int, int]): Position of the pawn on the board.
  • notation_func (Callable): Callable function from notation module used to note the move.
color: str
position: tuple[int, int]
board
first_move: bool
moved_by_two: bool
can_en_passant: bool
move: int
notation_func: Callable
def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
138    def check_possible_moves(self, color: str, checking: bool=False) -> list[tuple[int, int]]:
139        """Function checking all possible moves for the Pawn.
140
141        Args:
142            color (str): Color of the pawn.
143            checking (bool, optional): Flag to know when player makes move and when algorithm checks possible moves. Defaults to False.
144
145        Returns:
146            list[tuple[int, int]]: Returns list of all legal positions to which pawn can move.
147        """
148        possible_moves: list[tuple[int, int]] = []
149        if self.check_turn(color):
150            return possible_moves
151        move = self.move
152        x: int = self.position[0]
153        y: int = self.position[1]
154        forward_one = (x + move, y)
155        forward_two = (x + move + move, y)
156        if not self.board.board[forward_one[0]][forward_one[1]].figure:
157            possible_moves.append(forward_one)
158            if self.first_move and not self.board.board[forward_two[0]][forward_two[1]].figure:
159                possible_moves.append(forward_two)
160        for offset in [-1, 1]:
161            if 0 <= y + offset < 8:
162                capture_position = (x + move, y + offset)
163                target_square = self.board.board[capture_position[0]][capture_position[1]]
164                if target_square.figure and target_square.figure.color != self.color:
165                    possible_moves.append(capture_position)
166                adjacent_pawn = self.board.board[x][y + offset].figure
167                if isinstance(adjacent_pawn, Pawn) and adjacent_pawn.color != self.color and adjacent_pawn.moved_by_two:
168                    possible_moves.append((x + move, y + offset))
169                    self.can_en_passant = True
170        return possible_moves

Function checking all possible moves for the Pawn.

Arguments:
  • color (str): Color of the pawn.
  • checking (bool, optional): Flag to know when player makes move and when algorithm checks possible moves. Defaults to False.
Returns:

list[tuple[int, int]]: Returns list of all legal positions to which pawn can move.

def choose_figure( self, event: Any, figure, choose_piece_menu: customtkinter.windows.widgets.ctk_label.CTkLabel, choose_piece_menu_1: customtkinter.windows.widgets.ctk_frame.CTkFrame) -> None:
172    def choose_figure(self, event: Any, figure, choose_piece_menu: ctk.CTkLabel, choose_piece_menu_1: ctk.CTkFrame) -> None:
173        """Function responsible of promoting the pawn to other figure.
174
175        Args:
176            event (Any): Event type. Doesn't matter but is required parameter by customtkinter.
177            figure : Figure chosen by the player.
178            choose_piece_menu (ctk.CTkLabel): Label widget.
179            choose_piece_menu_1 (ctk.CTkFrame): Frame widget.
180        """
181        x, y = self.position[0], self.position[1]
182        self.board.board[x][y].figure = figure(self.color, self.board, self.position)
183        self.board.board[x][y].update()
184        self.notation_func(self.board.board[x][y].figure.__class__.__name__)
185        choose_piece_menu.destroy()
186        choose_piece_menu_1.destroy()

Function responsible of promoting the pawn to other figure.

Arguments:
  • event (Any): Event type. Doesn't matter but is required parameter by customtkinter.
  • figure : Figure chosen by the player.
  • choose_piece_menu (ctk.CTkLabel): Label widget.
  • choose_piece_menu_1 (ctk.CTkFrame): Frame widget.
def create_button( self, choose_piece_menu: customtkinter.windows.widgets.ctk_label.CTkLabel, figure, choose_piece_menu_1: customtkinter.windows.widgets.ctk_frame.CTkFrame) -> None:
188    def create_button(self, choose_piece_menu: ctk.CTkLabel, figure, choose_piece_menu_1: ctk.CTkFrame) -> None:
189        """Function creating the button for one of the figure possible to choose from the menu.
190
191        Args:
192            choose_piece_menu (ctk.CTkLabel): Label in which button will be created.
193            figure : Figure that will be possible to choose by clicking the button.
194            choose_piece_menu_1 (ctk.CTkFrame): Frame widget.
195        """
196        piece_image: ctk.CTkImage | None = self.load_image(str(figure.__name__))
197        button_figure: ctk.CTkLabel = ctk.CTkLabel(
198            master        = choose_piece_menu,
199            text          = '',
200            image         = piece_image,
201            corner_radius = 0
202        )
203        button_figure.pack(side=ctk.LEFT, padx=10, pady=10)
204        button_figure.bind('<Button-1>', lambda e: self.choose_figure(e, figure, choose_piece_menu, choose_piece_menu_1))

Function creating the button for one of the figure possible to choose from the menu.

Arguments:
  • choose_piece_menu (ctk.CTkLabel): Label in which button will be created.
  • figure : Figure that will be possible to choose by clicking the button.
  • choose_piece_menu_1 (ctk.CTkFrame): Frame widget.
def promote(self) -> bool:
206    def promote(self) -> bool:
207        """Function checking if pawn is ont the end of the board. If so it force the player to choose the figure they want to promote to.
208
209        Returns:
210            bool: True if pawn was promoted. False otherwise.
211        """
212        if self.position[0] in {0, 7}:
213            choose_piece_menu_1: ctk.CTkFrame = ctk.CTkFrame(
214                master   = self.board, corner_radius=0,
215                fg_color = COLOR.BACKGROUND
216            )
217            choose_piece_menu_1.place(relx=0, rely=0, relwidth=1, relheight=1)
218            if platform.system() == 'Windows':
219                pywinstyles.set_opacity(choose_piece_menu_1, value=0.01, color="#000001")
220            choose_piece_menu: ctk.CTkFrame = ctk.CTkFrame(
221                master        = self.board,
222                fg_color      = COLOR.BACKGROUND,
223                corner_radius = 0,
224                border_color  = COLOR.DARK_TEXT,
225                border_width  = 4
226            )
227            choose_piece_menu.place(relx=0.5, rely=0.5, anchor=ctk.CENTER)
228            possible_figures = [Knight, Bishop, Rook, Queen]
229            for figure in possible_figures:
230                self.create_button(choose_piece_menu, figure, choose_piece_menu_1)
231            return True
232        return False

Function checking if pawn is ont the end of the board. If so it force the player to choose the figure they want to promote to.

Returns:

bool: True if pawn was promoted. False otherwise.

def notate( self, figure_name: Piece, moves_record, capture: bool, check: bool, checkmate: bool) -> None:
234    def notate(self, figure_name: Piece, moves_record, capture: bool, check: bool, checkmate: bool) -> None:
235        """Helper function to note the move of the pawn.
236
237        Args:
238            figure_name (Piece): Name of the figure.
239            moves_record : Object of MovesRecord. Cannot specify type due to circular imports.
240            capture (bool): Flag to check if capture occurred.
241            check (bool): Flag to check if after pawn move check occurred.
242            checkmate (bool): Flag to check if after pawn move checkmate occurred.
243        """
244        moves_record.record_move(figure_name,
245            capture=capture, castle=None, check=check, checkmate=checkmate,
246            promotion=f'{self.board.board[self.position[0]][self.position[1]].figure.__class__.__name__[0]}'
247        )

Helper function to note the move of the pawn.

Arguments:
  • figure_name (Piece): Name of the figure.
  • moves_record : Object of MovesRecord. Cannot specify type due to circular imports.
  • capture (bool): Flag to check if capture occurred.
  • check (bool): Flag to check if after pawn move check occurred.
  • checkmate (bool): Flag to check if after pawn move checkmate occurred.
class Knight(Piece):
249class Knight(Piece):
250    """Implementation of the Knight.
251
252    Args:
253        Piece : Inheritance from the master class Piece to access general functions of the figures.
254    """
255    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
256        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
257
258        Args:
259            color (str): Color of the Knight.
260            board : Board object on which figures will be placed.
261            position (tuple[int, int]): Position on the board.
262        """
263        super().__init__(color, board, position)
264        self.color: str = color
265        self.board = board
266        self.load_image()
267        self.moves: list[tuple[int, int]] = [   
268            (-2,-1), (-2, 1),
269            (-1,-2), (-1, 2),
270            ( 1,-2), ( 1, 2),
271            ( 2,-1), ( 2, 1)   
272        ]
273        self.special_cases: dict[tuple[int, int], list[int]] = {
274            (1, 1): [0, 1, 2, 4],
275            (1, 6): [0, 1, 3, 5],
276            (6, 6): [3, 5, 6, 7],
277            (6, 1): [2, 4, 3, 5],
278            (0, 0): [0, 1, 2, 3, 4, 6],
279            (0, 1): [0, 1, 2, 3, 4],
280            (1, 0): [0, 1, 2, 4, 7]
281        }
282
283    def check_moves(self, exceptions: list[int]) -> list[tuple[int, int]]:
284        """Function checking possible moves for the Knight.
285
286        Args:
287            exceptions (list[int]): List of position that are not legal.
288
289        Returns:
290            list[tuple[int, int]]: List of all legal moves.
291        """
292        possible_moves: list[tuple[int, int]] = []
293        for i, move in enumerate(self.moves):
294            if i in exceptions:
295                continue
296            new_position = (self.position[0] + move[0], self.position[1] + move[1])
297            if 0 <= new_position[0] <= 7 and 0 <= new_position[1] <= 7:
298                target_square = self.board.board[new_position[0]][new_position[1]]
299                if not target_square.figure or target_square.figure.color != self.color:
300                    possible_moves.append(new_position)
301        return possible_moves
302
303    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
304        """Function checking all possible moves for the Knight.
305
306        Args:
307            color (str): Color of the Knight.
308            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
309
310        Returns:
311            list[tuple[int, int]]: List of all legal position to which Knight can move.
312        """
313        if self.check_turn(color) and not checking:
314            return []
315        if 2 <= self.position[0] <= 5 and self.position[1] in {1, 6} and self.position not in self.special_cases:
316            if self.position[1] == 1:
317                return self.check_moves([2, 4])
318            if self.position[1] == 6:
319                return self.check_moves([3, 5])
320        if 2 <= self.position[1] <= 5 and self.position[0] in {1, 6} and self.position not in self.special_cases:
321            if self.position[0] == 1:
322                return self.check_moves([0, 1])
323            if self.position[0] == 6:
324                return self.check_moves([6, 7])
325        if self.position in self.special_cases:
326            return self.check_moves(self.special_cases[self.position])
327        return self.check_moves([])

Implementation of the Knight.

Arguments:
  • Piece : Inheritance from the master class Piece to access general functions of the figures.
Knight(color: str, board, position: tuple[int, int])
255    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
256        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
257
258        Args:
259            color (str): Color of the Knight.
260            board : Board object on which figures will be placed.
261            position (tuple[int, int]): Position on the board.
262        """
263        super().__init__(color, board, position)
264        self.color: str = color
265        self.board = board
266        self.load_image()
267        self.moves: list[tuple[int, int]] = [   
268            (-2,-1), (-2, 1),
269            (-1,-2), (-1, 2),
270            ( 1,-2), ( 1, 2),
271            ( 2,-1), ( 2, 1)   
272        ]
273        self.special_cases: dict[tuple[int, int], list[int]] = {
274            (1, 1): [0, 1, 2, 4],
275            (1, 6): [0, 1, 3, 5],
276            (6, 6): [3, 5, 6, 7],
277            (6, 1): [2, 4, 3, 5],
278            (0, 0): [0, 1, 2, 3, 4, 6],
279            (0, 1): [0, 1, 2, 3, 4],
280            (1, 0): [0, 1, 2, 4, 7]
281        }

Constructor is really basic. Setups master class and setups board, color and loads asset.

Arguments:
  • color (str): Color of the Knight.
  • board : Board object on which figures will be placed.
  • position (tuple[int, int]): Position on the board.
color: str
board
moves: list[tuple[int, int]]
special_cases: dict[tuple[int, int], list[int]]
def check_moves(self, exceptions: list[int]) -> list[tuple[int, int]]:
283    def check_moves(self, exceptions: list[int]) -> list[tuple[int, int]]:
284        """Function checking possible moves for the Knight.
285
286        Args:
287            exceptions (list[int]): List of position that are not legal.
288
289        Returns:
290            list[tuple[int, int]]: List of all legal moves.
291        """
292        possible_moves: list[tuple[int, int]] = []
293        for i, move in enumerate(self.moves):
294            if i in exceptions:
295                continue
296            new_position = (self.position[0] + move[0], self.position[1] + move[1])
297            if 0 <= new_position[0] <= 7 and 0 <= new_position[1] <= 7:
298                target_square = self.board.board[new_position[0]][new_position[1]]
299                if not target_square.figure or target_square.figure.color != self.color:
300                    possible_moves.append(new_position)
301        return possible_moves

Function checking possible moves for the Knight.

Arguments:
  • exceptions (list[int]): List of position that are not legal.
Returns:

list[tuple[int, int]]: List of all legal moves.

def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
303    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
304        """Function checking all possible moves for the Knight.
305
306        Args:
307            color (str): Color of the Knight.
308            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
309
310        Returns:
311            list[tuple[int, int]]: List of all legal position to which Knight can move.
312        """
313        if self.check_turn(color) and not checking:
314            return []
315        if 2 <= self.position[0] <= 5 and self.position[1] in {1, 6} and self.position not in self.special_cases:
316            if self.position[1] == 1:
317                return self.check_moves([2, 4])
318            if self.position[1] == 6:
319                return self.check_moves([3, 5])
320        if 2 <= self.position[1] <= 5 and self.position[0] in {1, 6} and self.position not in self.special_cases:
321            if self.position[0] == 1:
322                return self.check_moves([0, 1])
323            if self.position[0] == 6:
324                return self.check_moves([6, 7])
325        if self.position in self.special_cases:
326            return self.check_moves(self.special_cases[self.position])
327        return self.check_moves([])

Function checking all possible moves for the Knight.

Arguments:
  • color (str): Color of the Knight.
  • checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
Returns:

list[tuple[int, int]]: List of all legal position to which Knight can move.

class Bishop(Piece):
329class Bishop(Piece):
330    """Implementation of the Bishop.
331
332    Args:
333        Piece : Inheritance from the master class Piece to access general functions of the figures.
334    """
335    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
336        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
337
338        Args:
339            color (str): Color of the Bishop.
340            board : Board object on which figures will be placed.
341            position (tuple[int, int]): Position on the board.
342        """
343        super().__init__(color, board, position)
344        self.color: str = color
345        self.board = board
346        self.load_image()
347        self.moves_vec = [
348            (-1, -1), (-1, 1),
349            ( 1, -1), (1,  1)
350        ]
351
352    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
353        """Function checking all possible moves of the Bishop.
354
355        Args:
356            color (str): Color of the Bishop.
357            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
358
359        Returns:
360            list[tuple[int, int]]: List of all legal position to which Bishop can move.
361        """
362        possible_moves: list[tuple[int, int]] = []
363        if self.check_turn(color) and not checking:
364            return possible_moves
365        for move in self.moves_vec:
366            for i in range(1, 8):
367                multiplied_vec = tuple(x * i for x in move)
368                x = self.position[0] + multiplied_vec[0]
369                y = self.position[1] + multiplied_vec[1]
370                if 0 <= x <= 7 and 0 <= y <= 7:
371                    if not self.board.board[x][y].figure:
372                        possible_moves.append((x, y))
373                    elif self.board.board[x][y].figure.color != self.color:
374                        possible_moves.append((x, y))
375                        break
376                    else:
377                        break
378                else:
379                    break
380        return possible_moves

Implementation of the Bishop.

Arguments:
  • Piece : Inheritance from the master class Piece to access general functions of the figures.
Bishop(color: str, board, position: tuple[int, int])
335    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
336        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
337
338        Args:
339            color (str): Color of the Bishop.
340            board : Board object on which figures will be placed.
341            position (tuple[int, int]): Position on the board.
342        """
343        super().__init__(color, board, position)
344        self.color: str = color
345        self.board = board
346        self.load_image()
347        self.moves_vec = [
348            (-1, -1), (-1, 1),
349            ( 1, -1), (1,  1)
350        ]

Constructor is really basic. Setups master class and setups board, color and loads asset.

Arguments:
  • color (str): Color of the Bishop.
  • board : Board object on which figures will be placed.
  • position (tuple[int, int]): Position on the board.
color: str
board
moves_vec
def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
352    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
353        """Function checking all possible moves of the Bishop.
354
355        Args:
356            color (str): Color of the Bishop.
357            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
358
359        Returns:
360            list[tuple[int, int]]: List of all legal position to which Bishop can move.
361        """
362        possible_moves: list[tuple[int, int]] = []
363        if self.check_turn(color) and not checking:
364            return possible_moves
365        for move in self.moves_vec:
366            for i in range(1, 8):
367                multiplied_vec = tuple(x * i for x in move)
368                x = self.position[0] + multiplied_vec[0]
369                y = self.position[1] + multiplied_vec[1]
370                if 0 <= x <= 7 and 0 <= y <= 7:
371                    if not self.board.board[x][y].figure:
372                        possible_moves.append((x, y))
373                    elif self.board.board[x][y].figure.color != self.color:
374                        possible_moves.append((x, y))
375                        break
376                    else:
377                        break
378                else:
379                    break
380        return possible_moves

Function checking all possible moves of the Bishop.

Arguments:
  • color (str): Color of the Bishop.
  • checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
Returns:

list[tuple[int, int]]: List of all legal position to which Bishop can move.

class Rook(Piece):
382class Rook(Piece):
383    """Implementation of the Rook.
384
385    Args:
386        Piece : Inheritance from the master class Piece to access general functions of the figures.
387    """
388    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
389        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
390
391        Args:
392            color (str): Color of the Rook.
393            board : Board object on which figures will be placed.
394            position (tuple[int, int]): Position on the board.
395        """
396        super().__init__(color, board, position)
397        self.color: str = color
398        self.board = board
399        self.load_image()
400        self.first_move: bool = True
401        self.moves_vec = [
402            (-1, 0), (0,-1),
403            ( 1, 0), (0, 1)
404        ]
405
406    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
407        """Function checking all possible moves of the Rook.
408
409        Args:
410            color (str): Color of the Rook.
411            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
412
413        Returns:
414            list[tuple[int, int]]: List of all legal position to which Rook can move.
415        """
416        possible_moves: list[tuple[int, int]] = []
417        if self.check_turn(color) and not checking:
418            return possible_moves
419        for move in self.moves_vec:
420            for i in range(1, 8):
421                multiplied_vec = tuple(x * i for x in move)
422                x = self.position[0] + multiplied_vec[0]
423                y = self.position[1] + multiplied_vec[1]
424                if 0 <= x <= 7 and 0 <= y <= 7:
425                    if not self.board.board[x][y].figure:
426                        possible_moves.append((x, y))
427                    elif self.board.board[x][y].figure.color != self.color:
428                        possible_moves.append((x, y))
429                        break
430                    else:
431                        break
432                else:
433                    break
434        return possible_moves

Implementation of the Rook.

Arguments:
  • Piece : Inheritance from the master class Piece to access general functions of the figures.
Rook(color: str, board, position: tuple[int, int])
388    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
389        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
390
391        Args:
392            color (str): Color of the Rook.
393            board : Board object on which figures will be placed.
394            position (tuple[int, int]): Position on the board.
395        """
396        super().__init__(color, board, position)
397        self.color: str = color
398        self.board = board
399        self.load_image()
400        self.first_move: bool = True
401        self.moves_vec = [
402            (-1, 0), (0,-1),
403            ( 1, 0), (0, 1)
404        ]

Constructor is really basic. Setups master class and setups board, color and loads asset.

Arguments:
  • color (str): Color of the Rook.
  • board : Board object on which figures will be placed.
  • position (tuple[int, int]): Position on the board.
color: str
board
first_move: bool
moves_vec
def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
406    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
407        """Function checking all possible moves of the Rook.
408
409        Args:
410            color (str): Color of the Rook.
411            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
412
413        Returns:
414            list[tuple[int, int]]: List of all legal position to which Rook can move.
415        """
416        possible_moves: list[tuple[int, int]] = []
417        if self.check_turn(color) and not checking:
418            return possible_moves
419        for move in self.moves_vec:
420            for i in range(1, 8):
421                multiplied_vec = tuple(x * i for x in move)
422                x = self.position[0] + multiplied_vec[0]
423                y = self.position[1] + multiplied_vec[1]
424                if 0 <= x <= 7 and 0 <= y <= 7:
425                    if not self.board.board[x][y].figure:
426                        possible_moves.append((x, y))
427                    elif self.board.board[x][y].figure.color != self.color:
428                        possible_moves.append((x, y))
429                        break
430                    else:
431                        break
432                else:
433                    break
434        return possible_moves

Function checking all possible moves of the Rook.

Arguments:
  • color (str): Color of the Rook.
  • checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
Returns:

list[tuple[int, int]]: List of all legal position to which Rook can move.

class Queen(Piece):
436class Queen(Piece):
437    """Implementation of the Queen.
438
439    Args:
440        Piece : Inheritance from the master class Piece to access general functions of the figures.
441    """
442    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
443        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
444
445        Args:
446            color (str): Color of the Queen.
447            board : Board object on which figures will be placed.
448            position (tuple[int, int]): Position on the board.
449        """
450        super().__init__(color, board, position)
451        self.color: str = color
452        self.board = board
453        self.load_image()
454        self.moves_vec = [
455            (-1, 0), (0,-1),
456            ( 1, 0), (0, 1),
457            (-1, -1), (-1, 1),
458            ( 1, -1), (1,  1)
459        ]
460
461    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
462        """Function checking all possible moves of the Queen.
463
464        Args:
465            color (str): Color of the Queen.
466            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
467
468        Returns:
469            list[tuple[int, int]]: List of all legal position to which Queen can move.
470        """
471        possible_moves: list[tuple[int, int]] = []
472        if self.check_turn(color) and not checking:
473            return possible_moves
474        for move in self.moves_vec:
475            for i in range(1, 8):
476                multiplied_vec = tuple(x * i for x in move)
477                x = self.position[0] + multiplied_vec[0]
478                y = self.position[1] + multiplied_vec[1]
479                if 0 <= x <= 7 and 0 <= y <= 7:
480                    if not self.board.board[x][y].figure:
481                        possible_moves.append((x, y))
482                    elif self.board.board[x][y].figure.color != self.color:
483                        possible_moves.append((x, y))
484                        break
485                    else:
486                        break
487                else:
488                    break
489        return possible_moves

Implementation of the Queen.

Arguments:
  • Piece : Inheritance from the master class Piece to access general functions of the figures.
Queen(color: str, board, position: tuple[int, int])
442    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
443        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
444
445        Args:
446            color (str): Color of the Queen.
447            board : Board object on which figures will be placed.
448            position (tuple[int, int]): Position on the board.
449        """
450        super().__init__(color, board, position)
451        self.color: str = color
452        self.board = board
453        self.load_image()
454        self.moves_vec = [
455            (-1, 0), (0,-1),
456            ( 1, 0), (0, 1),
457            (-1, -1), (-1, 1),
458            ( 1, -1), (1,  1)
459        ]

Constructor is really basic. Setups master class and setups board, color and loads asset.

Arguments:
  • color (str): Color of the Queen.
  • board : Board object on which figures will be placed.
  • position (tuple[int, int]): Position on the board.
color: str
board
moves_vec
def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
461    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
462        """Function checking all possible moves of the Queen.
463
464        Args:
465            color (str): Color of the Queen.
466            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
467
468        Returns:
469            list[tuple[int, int]]: List of all legal position to which Queen can move.
470        """
471        possible_moves: list[tuple[int, int]] = []
472        if self.check_turn(color) and not checking:
473            return possible_moves
474        for move in self.moves_vec:
475            for i in range(1, 8):
476                multiplied_vec = tuple(x * i for x in move)
477                x = self.position[0] + multiplied_vec[0]
478                y = self.position[1] + multiplied_vec[1]
479                if 0 <= x <= 7 and 0 <= y <= 7:
480                    if not self.board.board[x][y].figure:
481                        possible_moves.append((x, y))
482                    elif self.board.board[x][y].figure.color != self.color:
483                        possible_moves.append((x, y))
484                        break
485                    else:
486                        break
487                else:
488                    break
489        return possible_moves

Function checking all possible moves of the Queen.

Arguments:
  • color (str): Color of the Queen.
  • checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
Returns:

list[tuple[int, int]]: List of all legal position to which Queen can move.

class King(Piece):
491class King(Piece):
492    """Implementation of the King.
493
494    Args:
495        Piece : Inheritance from the master class Piece to access general functions of the figures.
496    """
497    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
498        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
499
500        Args:
501            color (str): Color of the King.
502            board : Board object on which figures will be placed.
503            position (tuple[int, int]): Position on the board.
504        """
505        super().__init__(color, board, position)
506        self.color: str = color
507        self.board = board
508        self.load_image()
509        self.first_move: bool = True
510        self.can_castle: bool = False
511
512    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
513        """Function checking all possible moves of the King.
514
515        Args:
516            color (str): Color of the King.
517            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
518
519        Returns:
520            list[tuple[int, int]]: List of all legal position to which King can move.
521        """
522        possible_moves: list[tuple[int, int]] = []
523        if self.check_turn(color) and not checking:
524            return possible_moves
525        for i in range(max(0, self.position[0] - 1), min(8, self.position[0] + 2)):
526            for j in range(max(0, self.position[1] - 1), min(8, self.position[1] + 2)):
527                if not self.board.board[i][j].figure:
528                    possible_moves.append((i, j))
529                if self.board.board[i][j].figure and self.board.board[i][j].figure.color != self.color:
530                    possible_moves.append((i, j))
531        if self.first_move and not checking:
532            possible_moves.extend(self.get_castling_moves())
533        return possible_moves
534
535    def get_castling_moves(self) -> list[tuple[int, int]]:
536        """Function checking moves for castle.
537
538        Returns:
539            list[tuple[int, int]]: List of legal positions.
540        """
541        castling_moves = []
542        row = self.position[0]
543        if self.can_castle_kingside():
544            castling_moves.append((row, 6))
545        if self.can_castle_queenside():
546            castling_moves.append((row, 2))
547        return castling_moves
548
549    def can_castle_kingside(self) -> bool:
550        """Function checking if King can perform castle from King side.
551
552        Returns:
553            bool: True if King can castle. False otherwise.
554        """
555        row, col = self.position
556        if self.first_move and isinstance(self.board.board[row][7].figure, Rook) and self.board.board[row][7].figure.first_move:
557            for i in range(col + 1, 7):
558                if self.board.board[row][i].figure or self.board.is_under_attack((row, i), self.color):
559                    return False
560            if self.board.is_under_attack((row, 6), self.color) or self.board.is_under_attack((row, col), self.color):
561                return False
562            return True
563        return False
564
565    def can_castle_queenside(self) -> bool:
566        """Function checking if King can perform castle from Queen side.
567
568        Returns:
569            bool: True if King can castle. False otherwise.
570        """
571        row, col = self.position
572        if self.first_move and isinstance(self.board.board[row][0].figure, Rook) and self.board.board[row][0].figure.first_move:
573            for i in range(col - 1, 0, -1):
574                if self.board.board[row][i].figure or self.board.is_under_attack((row, i), self.color):
575                    return False
576            if self.board.is_under_attack((row, 2), self.color) or self.board.is_under_attack((row, col), self.color):
577                return False
578            return True
579        return False

Implementation of the King.

Arguments:
  • Piece : Inheritance from the master class Piece to access general functions of the figures.
King(color: str, board, position: tuple[int, int])
497    def __init__(self, color: str, board, position: tuple[int, int]) -> None:
498        """Constructor is really basic. Setups master class and setups board, color and loads asset. 
499
500        Args:
501            color (str): Color of the King.
502            board : Board object on which figures will be placed.
503            position (tuple[int, int]): Position on the board.
504        """
505        super().__init__(color, board, position)
506        self.color: str = color
507        self.board = board
508        self.load_image()
509        self.first_move: bool = True
510        self.can_castle: bool = False

Constructor is really basic. Setups master class and setups board, color and loads asset.

Arguments:
  • color (str): Color of the King.
  • board : Board object on which figures will be placed.
  • position (tuple[int, int]): Position on the board.
color: str
board
first_move: bool
can_castle: bool
def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
512    def check_possible_moves(self, color: str, checking: bool = False) -> list[tuple[int, int]]:
513        """Function checking all possible moves of the King.
514
515        Args:
516            color (str): Color of the King.
517            checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
518
519        Returns:
520            list[tuple[int, int]]: List of all legal position to which King can move.
521        """
522        possible_moves: list[tuple[int, int]] = []
523        if self.check_turn(color) and not checking:
524            return possible_moves
525        for i in range(max(0, self.position[0] - 1), min(8, self.position[0] + 2)):
526            for j in range(max(0, self.position[1] - 1), min(8, self.position[1] + 2)):
527                if not self.board.board[i][j].figure:
528                    possible_moves.append((i, j))
529                if self.board.board[i][j].figure and self.board.board[i][j].figure.color != self.color:
530                    possible_moves.append((i, j))
531        if self.first_move and not checking:
532            possible_moves.extend(self.get_castling_moves())
533        return possible_moves

Function checking all possible moves of the King.

Arguments:
  • color (str): Color of the King.
  • checking (bool, optional): Flag to recognize between human making a move and algorithm checking if after move check doesn't occurs. Defaults to False.
Returns:

list[tuple[int, int]]: List of all legal position to which King can move.

def get_castling_moves(self) -> list[tuple[int, int]]:
535    def get_castling_moves(self) -> list[tuple[int, int]]:
536        """Function checking moves for castle.
537
538        Returns:
539            list[tuple[int, int]]: List of legal positions.
540        """
541        castling_moves = []
542        row = self.position[0]
543        if self.can_castle_kingside():
544            castling_moves.append((row, 6))
545        if self.can_castle_queenside():
546            castling_moves.append((row, 2))
547        return castling_moves

Function checking moves for castle.

Returns:

list[tuple[int, int]]: List of legal positions.

def can_castle_kingside(self) -> bool:
549    def can_castle_kingside(self) -> bool:
550        """Function checking if King can perform castle from King side.
551
552        Returns:
553            bool: True if King can castle. False otherwise.
554        """
555        row, col = self.position
556        if self.first_move and isinstance(self.board.board[row][7].figure, Rook) and self.board.board[row][7].figure.first_move:
557            for i in range(col + 1, 7):
558                if self.board.board[row][i].figure or self.board.is_under_attack((row, i), self.color):
559                    return False
560            if self.board.is_under_attack((row, 6), self.color) or self.board.is_under_attack((row, col), self.color):
561                return False
562            return True
563        return False

Function checking if King can perform castle from King side.

Returns:

bool: True if King can castle. False otherwise.

def can_castle_queenside(self) -> bool:
565    def can_castle_queenside(self) -> bool:
566        """Function checking if King can perform castle from Queen side.
567
568        Returns:
569            bool: True if King can castle. False otherwise.
570        """
571        row, col = self.position
572        if self.first_move and isinstance(self.board.board[row][0].figure, Rook) and self.board.board[row][0].figure.first_move:
573            for i in range(col - 1, 0, -1):
574                if self.board.board[row][i].figure or self.board.is_under_attack((row, i), self.color):
575                    return False
576            if self.board.is_under_attack((row, 2), self.color) or self.board.is_under_attack((row, col), self.color):
577                return False
578            return True
579        return False

Function checking if King can perform castle from Queen side.

Returns:

bool: True if King can castle. False otherwise.