From 9161b1fcd3d2ad8cdf211e63ad7b4df890a88ae4 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 00:51:31 +0200 Subject: [PATCH 01/27] ADD: Pawn DocString --- python/engine/material.py | 120 ++++++++++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 12 deletions(-) diff --git a/python/engine/material.py b/python/engine/material.py index c1de74a..5795000 100644 --- a/python/engine/material.py +++ b/python/engine/material.py @@ -22,7 +22,7 @@ class Piece(object): """ def __init__(self, white, x, y): - """Initialization of piece. + """Initialization of the piece. Parameters ---------- @@ -40,6 +40,7 @@ def __init__(self, white, x, y): self.x = x self.y = y + @abstractmethod def piece_deepcopy(self): """Method to create an uncorrelated clone of the piece. @@ -86,10 +87,10 @@ def piece_move_authorized(self, start, end): Parameters ---------- - start: tuple - (x, y) coordinates of the starting square (current coordinates). - end: tuple - (x, y) coordinates of the landing square + start: engine.Cell + Starting cell for movement check (current cell of piece). + end: engine.Cell + Landing cell for movement check. Returns ------- @@ -166,15 +167,53 @@ def draw(self): class Pawn(Piece): + """Base class for the pawns pieces + + Implements the properties, attributes and functions specific to pawns. + + Attributes + ---------- + white : bool + Whether the piece is white or black. + x : int + x coordinate of piece on board + y : int + y coordinate of piece on board + killed: bool + Whether the piece has been killed by opponent or not. Initialized to False + has_moved: bool + Whether the piece has already moved during a game or not. Initialized to False + last_move_is_double: bool + Whether the piece previous move was a double advance or not. For future En Passant checks. Initialized to False. + """ type = "pawn" def __init__(self, *args, **kwargs): + """Initialization of the pawn. + + Parameters + ---------- + white : bool + Whether the piece is white or black. + x : int + initial x coordinate of piece on board + y : int + initial y coordinate of piece on board + + """ super().__init__(*args, **kwargs) self.has_moved = False # if the pawn has yet been moved or not to keep ? self.last_move_is_double = False # check for en passant, if last move was a double tap def piece_deepcopy(self): + """Method to create an uncorrelated clone of the piece. + + Returns + ------- + Pawn + Exact copy of self. + """ copied_piece = Pawn(self.white, self.x, self.y) copied_piece.killed = self.killed copied_piece.has_moved = self.has_moved @@ -182,12 +221,27 @@ def piece_deepcopy(self): return copied_piece def piece_move_authorized(self, start, end): + """Method to verify if is a move is authorized in terms of movements. + + Parameters + ---------- + start: engine.Cell + Starting cell for movement check (current cell). + end: engine.Cell + Landing cell for movement check. + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ + # Check if there is a piece on the landing cell if end.get_piece() is not None: + # check if there is not another piece of same color - ###print(end.get_piece(), start.get_piece()) if end.get_piece().is_white() == self.is_white(): - ###print("PAWN TAKING PIECE OF SAME COLOR") return False + else: # Pawn can only take an adversary piece in diagonal dx = end.get_x() - start.get_x() @@ -197,9 +251,9 @@ def piece_move_authorized(self, start, end): elif dx == -1 and abs(dy) == 1 and not self.is_white(): return True else: - ###print("PAWN NOT TAKING OTHER PIECE IN DIAGONAL") return False + # No piece on landing cell, just checking if movement is authorized. else: dx = end.get_x() - start.get_x() dy = end.get_y() - start.get_y() @@ -209,17 +263,31 @@ def piece_move_authorized(self, start, end): return True else: + # Initial move authorized to be two cells at once. Should check self.has_moved here ? if start.get_x() == 1 and dx == 2 and dy == 0 and self.is_white(): return True elif start.get_x() == 6 and dx == -2 and dy == 0 and not self.is_white(): return True else: - ###print("PAWN MOVE NOT AUTHORIZED WITH DX %i and DY %i" %(dx, dy)) return False def can_move(self, board, move): + """Method to verify if is a move is authorized in terms of movements. + + Parameters + ---------- + board: engine.Board + Board to which the piece belongs to and on which the movement is tested + move: engine.Move + Move object to be tested + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ authorized_move = self.piece_move_authorized(move.start, move.end) - ###print("move authorized ?", authorized_move) + if not authorized_move: """to remove ?""" crossed_cell = board.get_cell(move.start.get_x(), move.end.get_y()) @@ -230,15 +298,14 @@ def can_move(self, board, move): authorized_move = True move.complementary_passant = crossed_cell else: + # Checks that no piece (friend or foe) is blocking the cell(s) in front. dx = move.end.get_x() - move.start.get_x() if dx > 1: if board.get_cell(move.start.get_x()+1, move.start.get_y()).get_piece() is not None: - ###print('Pawn line of sight blocked') return False elif dx < -1: if board.get_cell(move.start.get_x()-1, move.start.get_y()).get_piece() is not None: - ###print('Pawn line of sight blocked') return False """ if move.end.get_x() == 7 and self.is_white(): @@ -249,16 +316,38 @@ def can_move(self, board, move): return authorized_move def get_potential_moves(self, x, y): + """Method to list all the possible moves from coordinates. Only uses authorized movements, no other pieces on a + board. + + Parameters + ---------- + x: int + x coordinate of the piece + y: int + y coordinate of the piece + + Returns + ------- + bool + List of authorized moves + """ + possible_moves = [] if self.is_white(): + # Front cell possible_moves.append((x + 1, y)) + + # Diagonal cells if y - 1 >= 0: possible_moves.append((x + 1, y - 1)) if y + 1 <= 7: possible_moves.append((x + 1, y + 1)) + # Double front cell if x == 1: possible_moves.append((x + 2, y)) + + # Symmetric for black pawns else: possible_moves.append((x - 1, y)) @@ -272,6 +361,13 @@ def get_potential_moves(self, x, y): return possible_moves def get_str(self): + """Method to represent the piece as a string. + + Returns + ------- + str + String representation of the piece + """ return ' P ' From 4a4692aeeb72f8a9173a2bc9c3660427ca7eecbd Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 00:59:42 +0200 Subject: [PATCH 02/27] ADD: material main classes descriptions --- python/engine/material.py | 81 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/python/engine/material.py b/python/engine/material.py index 5795000..32a4d82 100644 --- a/python/engine/material.py +++ b/python/engine/material.py @@ -372,6 +372,21 @@ def get_str(self): class Bishop(Piece): + """Base class for the bishop pieces + + Implements the properties, attributes and functions specific to bishops. + + Attributes + ---------- + white : bool + Whether the piece is white or black. + x : int + x coordinate of piece on board + y : int + y coordinate of piece on board + killed: bool + Whether the piece has been killed by opponent or not. Initialized to False. + """ type = "bishop" @@ -450,6 +465,23 @@ def get_str(self): class Rook(Piece): + """Base class for the rook pieces + + Implements the properties, attributes and functions specific to rooks. + + Attributes + ---------- + white : bool + Whether the piece is white or black. + x : int + x coordinate of piece on board + y : int + y coordinate of piece on board + killed: bool + Whether the piece has been killed by opponent or not. Initialized to False. + has_moved: bool + Whether the piece has already moved during a game or not. Initialized to False. + """ type = "rook" @@ -528,6 +560,21 @@ def get_str(self): class Knight(Piece): + """Base class for the knoght pieces + + Implements the properties, attributes and functions specific to knights. + + Attributes + ---------- + white : bool + Whether the piece is white or black. + x : int + x coordinate of piece on board + y : int + y coordinate of piece on board + killed: bool + Whether the piece has been killed by opponent or not. Initialized to False. + """ type = "knight" @@ -565,6 +612,21 @@ def get_potential_moves(self, x, y): class Queen(Piece): + """Base class for the queen pieces + + Implements the properties, attributes and functions specific to queens. + + Attributes + ---------- + white : bool + Whether the piece is white or black. + x : int + x coordinate of piece on board + y : int + y coordinate of piece on board + killed: bool + Whether the piece has been killed by opponent or not. Initialized to False. + """ type = "queen" @@ -676,6 +738,25 @@ def get_str(self): class King(Piece): + """Base class for the king pieces + + Implements the properties, attributes and functions specific to kings. + + Attributes + ---------- + white : bool + Whether the piece is white or black. + x : int + x coordinate of piece on board + y : int + y coordinate of piece on board + killed: bool + Whether the piece has been killed by opponent or not. Initialized to False + has_moved: bool + Whether the piece has already moved during a game or not. Initialized to False + castling_done: bool + Whether the piece has already realized castling. Initialized to False. + """ type = "king" From 8e28aff68751603548e4f2e5bc1a8bdf9fa53130 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 11:11:36 +0200 Subject: [PATCH 03/27] ADD: full docstring for material --- python/engine/material.py | 436 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 420 insertions(+), 16 deletions(-) diff --git a/python/engine/material.py b/python/engine/material.py index 32a4d82..b078689 100644 --- a/python/engine/material.py +++ b/python/engine/material.py @@ -328,7 +328,7 @@ def get_potential_moves(self, x, y): Returns ------- - bool + list List of authorized moves """ @@ -360,6 +360,9 @@ def get_potential_moves(self, x, y): return possible_moves + def promote(self, promote_into="Queen"): + raise NotImplementedError + def get_str(self): """Method to represent the piece as a string. @@ -391,20 +394,55 @@ class Bishop(Piece): type = "bishop" def __init__(self, *args, **kwargs): + """Initialization of the bishop. + + Parameters + ---------- + white : bool + Whether the piece is white or black. + x : int + initial x coordinate of piece on board + y : int + initial y coordinate of piece on board + + """ super().__init__(*args, **kwargs) def piece_deepcopy(self): + """Method to create an uncorrelated clone of the piece. + + Returns + ------- + Bishop + Exact copy of self. + """ copied_piece = Bishop(self.white, self.x, self.y) copied_piece.killed = self.killed return copied_piece def piece_move_authorized(self, start, end): + """Method to verify if is a move is authorized in terms of movements. + + Parameters + ---------- + start: engine.Cell + Starting cell for movement check (current cell). + end: engine.Cell + Landing cell for movement check. + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ if start.get_x() == end.get_x() and start.get_y() == end.get_y(): return False else: + # If material is already on the landing cell if end.get_piece() is not None: if end.get_piece().is_white() == self.is_white(): return False + # Checking movemement dx = end.get_x() - start.get_x() dy = end.get_y() - start.get_y() if abs(dx) == abs(dy): @@ -413,23 +451,55 @@ def piece_move_authorized(self, start, end): return False def can_move(self, board, move): + """Method to verify if a move is authorized within a board. + + Parameters + ---------- + board: engine.Board + Board to which the piece belongs to and on which the movement is tested + move: engine.Move + Move object to be tested + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ + # Checking if movement is alright authorized_move = self.piece_move_authorized(move.start, move.end) if authorized_move: dx = move.end.get_x() - move.start.get_x() dy = move.end.get_y() - move.start.get_y() + + # Checking that no material is blocking the trajectory for i in range(1, abs(dx)): x_trajectory = i * int(dx / abs(dx)) + move.start.get_x() y_trajectory = i * int(dy / abs(dy)) + move.start.get_y() if board.get_cell(x_trajectory, y_trajectory).get_piece() is not None: - ###print('Bishop line of sight blocked') return False return True else: return False def get_potential_moves(self, x, y): + """Method to list all the possible moves from coordinates. Only uses authorized movements, no other pieces on a + board. + + Parameters + ---------- + x: int + x coordinate of the piece + y: int + y coordinate of the piece + + Returns + ------- + list + List of authorized moves + """ possible_moves = [] + # Diagonal 1 nx = x - 1 ny = y - 1 while nx >= 0 and ny >= 0: @@ -437,6 +507,7 @@ def get_potential_moves(self, x, y): nx -= 1 ny -= 1 + # Diagonal 2 nx = x - 1 ny = y + 1 while nx >= 0 and ny <= 7: @@ -444,6 +515,7 @@ def get_potential_moves(self, x, y): nx -= 1 ny += 1 + # Diagonal 3 nx = x + 1 ny = y - 1 while nx <= 7 and ny >= 0: @@ -451,6 +523,7 @@ def get_potential_moves(self, x, y): nx += 1 ny -= 1 + # Diagonal 4 nx = x + 1 ny = y + 1 while nx <= 7 and ny <= 7: @@ -461,6 +534,13 @@ def get_potential_moves(self, x, y): return possible_moves def get_str(self): + """Method to represent the piece as a string. + + Returns + ------- + str + String representation of the piece + """ return ' B ' @@ -486,22 +566,58 @@ class Rook(Piece): type = "rook" def __init__(self, *args, **kwargs): + """Initialization of the rook. + + Parameters + ---------- + white : bool + Whether the piece is white or black. + x : int + initial x coordinate of piece on board + y : int + initial y coordinate of piece on board + + """ super().__init__(*args, **kwargs) self.has_moved = False def piece_deepcopy(self): + """Method to create an uncorrelated clone of the piece. + + Returns + ------- + Rook + Exact copy of self. + """ copied_piece = Rook(self.white, self.x, self.y) copied_piece.killed = self.killed copied_piece.has_moved = self.has_moved return copied_piece def piece_move_authorized(self, start, end): + """Method to verify if is a move is authorized in terms of movements. + + Parameters + ---------- + start: engine.Cell + Starting cell for movement check (current cell). + end: engine.Cell + Landing cell for movement check. + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ if start.get_x() == end.get_x() and start.get_y() == end.get_y(): return False else: + # Checking if material is already on the landing cell if end.get_piece() is not None: if end.get_piece().is_white() == self.is_white(): return False + + # Checking movement dx = end.get_x() - start.get_x() dy = end.get_y() - start.get_y() if dx == 0 or dy == 0: @@ -510,44 +626,80 @@ def piece_move_authorized(self, start, end): return False def can_move(self, board, move): + """Method to verify if a move is authorized within a board. + + Parameters + ---------- + board: engine.Board + Board to which the piece belongs to and on which the movement is tested + move: engine.Move + Move object to be tested + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ + + # Checking that movement is authorized authorized_move = self.piece_move_authorized(move.start, move.end) if authorized_move: dx = move.end.get_x() - move.start.get_x() dy = move.end.get_y() - move.start.get_y() + # Checking that no material in x axis is blocking the trajectory for i in range(1, abs(dx)): x_trajectory = i * int(dx / abs(dx)) + move.start.get_x() y_trajectory = move.start.get_y() if board.get_cell(x_trajectory, y_trajectory).get_piece() is not None: - ###print('Rook line of sight blocked') return False + + # Checking that no material in the y axis is blocking the trajectory for i in range(1, abs(dy)): x_trajectory = move.start.get_x() y_trajectory = i * int(dy / abs(dy)) + move.start.get_y() if board.get_cell(x_trajectory, y_trajectory).get_piece() is not None: - ###print('Rook line of sight blocked') return False return True else: return False def get_potential_moves(self, x, y): + """Method to list all the possible moves from coordinates. Only uses authorized movements, no other pieces on a + board. + + Parameters + ---------- + x: int + x coordinate of the piece + y: int + y coordinate of the piece + + Returns + ------- + list + List of authorized moves + """ possible_moves = [] + # X-axis left nx = x - 1 while nx >= 0: possible_moves.append((nx, y)) nx -= 1 + # X-axis right ny = y + 1 while ny <= 7: possible_moves.append((x, ny)) ny += 1 + # Y-axis top nx = x + 1 while nx <= 7: possible_moves.append((nx, y)) nx += 1 + # Y-axis down ny = y - 1 while ny >= 0: possible_moves.append((x, ny)) @@ -556,6 +708,13 @@ def get_potential_moves(self, x, y): return possible_moves def get_str(self): + """Method to represent the piece as a string. + + Returns + ------- + str + String representation of the piece + """ return ' R ' @@ -579,30 +738,102 @@ class Knight(Piece): type = "knight" def __init__(self, *args, **kwargs): + """Initialization of the knight. + + Parameters + ---------- + white : bool + Whether the piece is white or black. + x : int + initial x coordinate of piece on board + y : int + initial y coordinate of piece on board + + """ super().__init__(*args, **kwargs) def piece_deepcopy(self): + """Method to create an uncorrelated clone of the piece. + + Returns + ------- + Knight + Exact copy of self. + """ copied_piece = Knight(self.white, self.x, self.y) copied_piece.killed = self.killed return copied_piece def piece_move_authorized(self, start, end): + """Method to verify if is a move is authorized in terms of movements. + + Parameters + ---------- + start: engine.Cell + Starting cell for movement check (current cell). + end: engine.Cell + Landing cell for movement check. + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ if end.get_piece() is not None: if end.get_piece().is_white() == self.is_white(): return False + dx = start.get_x() - end.get_x() dy = start.get_y() - end.get_y() return abs(dx) * abs(dy) == 2 def can_move(self, board, move): + """Method to verify if a move is authorized within a board. + + Parameters + ---------- + board: engine.Board + Board to which the piece belongs to and on which the movement is tested + move: engine.Move + Move object to be tested + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ + # The knight is jumping, no need to verify blocking material return self.piece_move_authorized(move.start, move.end) def get_str(self): + """Method to represent the piece as a string. + + Returns + ------- + str + String representation of the piece + """ return ' N ' def get_potential_moves(self, x, y): + """Method to list all the possible moves from coordinates. Only uses authorized movements, no other pieces on a + board. + + Parameters + ---------- + x: int + x coordinate of the piece + y: int + y coordinate of the piece + + Returns + ------- + list + List of authorized moves + """ possible_moves = [] + # All difference position that a knight can move to combos = [(2, 1), (1, 2), (-2, 1), (2, -1), (-2, -1), (-1, 2), (1, -2), (-1, -2)] for nx, ny in combos: if 0 <= nx+x <= 7 and 0 <= ny+y <= 7: @@ -631,14 +862,47 @@ class Queen(Piece): type = "queen" def __init__(self, *args, **kwargs): + """Initialization of the queen. + + Parameters + ---------- + white : bool + Whether the piece is white or black. + x : int + initial x coordinate of piece on board + y : int + initial y coordinate of piece on board + + """ super().__init__(*args, **kwargs) def piece_deepcopy(self): + """Method to create an uncorrelated clone of the piece. + + Returns + ------- + Queen + Exact copy of self. + """ copied_piece = Queen(self.white, self.x, self.y) copied_piece.killed = self.killed return copied_piece def piece_move_authorized(self, start, end): + """Method to verify if is a move is authorized in terms of movements. + + Parameters + ---------- + start: engine.Cell + Starting cell for movement check (current cell). + end: engine.Cell + Landing cell for movement check. + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ if start.get_x() == end.get_x() and start.get_y() == end.get_y(): return False else: @@ -651,38 +915,74 @@ def piece_move_authorized(self, start, end): return (dx == 0) or (dy == 0) or (abs(dx) == abs(dy)) def can_move(self, board, move): + """Method to verify if a move is authorized within a board. + + Parameters + ---------- + board: engine.Board + Board to which the piece belongs to and on which the movement is tested + move: engine.Move + Move object to be tested + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ + # Checking if movement is authorized authorized_move = self.piece_move_authorized(move.start, move.end) + + # Checking that no material is blocking the trajectory if authorized_move: dx = move.end.get_x() - move.start.get_x() dy = move.end.get_y() - move.start.get_y() + + # Queen going along an axis if dx == 0 or dy == 0: + # Along X-axis for i in range(1, abs(dx)): x_trajectory = i * int(dx / abs(dx)) + move.start.get_x() y_trajectory = move.start.get_y() if board.get_cell(x_trajectory, y_trajectory).get_piece() is not None: - ###print('Queen line of sight blocked') return False + # Along Y-axis for i in range(1, abs(dy)): x_trajectory = move.start.get_x() y_trajectory = i * int(dy / abs(dy)) + move.start.get_y() if board.get_cell(x_trajectory, y_trajectory).get_piece() is not None: - ###print('Queen line of sight blocked') return False return True + + # Queen going in diagonal elif abs(dx) == abs(dy): for i in range(1, abs(dx)): x_trajectory = i * int(dx / abs(dx)) + move.start.get_x() y_trajectory = i * int(dy / abs(dy)) + move.start.get_y() if board.get_cell(x_trajectory, y_trajectory).get_piece() is not None: - ###print('Queen line of sight blocked') return False return True else: return False def get_potential_moves(self, x, y): + """Method to list all the possible moves from coordinates. Only uses authorized movements, no other pieces on a + board. + + Parameters + ---------- + x: int + x coordinate of the piece + y: int + y coordinate of the piece + + Returns + ------- + list + List of authorized moves + """ possible_moves = [] + # Diagonal 1 nx = x - 1 ny = y - 1 while nx >= 0 and ny >= 0: @@ -690,6 +990,7 @@ def get_potential_moves(self, x, y): nx -= 1 ny -= 1 + # Diagonal 2 nx = x - 1 ny = y + 1 while nx >= 0 and ny <= 7: @@ -697,6 +998,7 @@ def get_potential_moves(self, x, y): nx -= 1 ny += 1 + # Diagonal 3 nx = x + 1 ny = y - 1 while nx <= 7 and ny >= 0: @@ -704,6 +1006,7 @@ def get_potential_moves(self, x, y): nx += 1 ny -= 1 + # Diagonal 4 nx = x + 1 ny = y + 1 while nx <= 7 and ny <= 7: @@ -711,21 +1014,25 @@ def get_potential_moves(self, x, y): nx += 1 ny += 1 + # X-axis left nx = x - 1 while nx >= 0: possible_moves.append((nx, y)) nx -= 1 + # X-axis right nx = x + 1 while nx <= 7: possible_moves.append((nx, y)) nx += 1 + # Y-axis down ny = y - 1 while ny >= 0: possible_moves.append((x, ny)) ny -= 1 + # Y-axis top ny = y + 1 while ny <= 7: possible_moves.append((x, ny)) @@ -734,6 +1041,13 @@ def get_potential_moves(self, x, y): return possible_moves def get_str(self): + """Method to represent the piece as a string. + + Returns + ------- + str + String representation of the piece + """ return ' Q ' @@ -761,11 +1075,30 @@ class King(Piece): type = "king" def __init__(self, *args, **kwargs): + """Initialization of the king. + + Parameters + ---------- + white : bool + Whether the piece is white or black. + x : int + initial x coordinate of piece on board + y : int + initial y coordinate of piece on board + + """ super().__init__(*args, **kwargs) self.castling_done = False self.has_moved = False def piece_deepcopy(self): + """Method to create an uncorrelated clone of the piece. + + Returns + ------- + King + Exact copy of self. + """ copied_piece = King(self.white, self.x, self.y) copied_piece.killed = self.killed copied_piece.castling_done = self.castling_done @@ -776,6 +1109,20 @@ def set_castling_done(self, castling_done): self.castling_done = castling_done def piece_move_authorized(self, start, end): + """Method to verify if is a move is authorized in terms of movements. + + Parameters + ---------- + start: engine.Cell + Starting cell for movement check (current cell). + end: engine.Cell + Landing cell for movement check. + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ if start.get_x() == end.get_x() and start.get_y() == end.get_y(): return False if end.get_piece() is not None: @@ -790,48 +1137,70 @@ def piece_move_authorized(self, start, end): return False def can_move(self, board, move): + """Method to verify if a move is authorized within a board. + + Parameters + ---------- + board: engine.Board + Board to which the piece belongs to and on which the movement is tested + move: engine.Move + Move object to be tested + + Returns + ------- + bool + Whether the movement is authorized by the piece possibilities or not. + """ + # Checking if movement is authorized authorized_move = self.piece_move_authorized(move.start, move.end) if authorized_move: + # Verifying that the landing cell is not threatened by some adversary material if move.end.is_threatened(board, self.is_white()): - ###print('King cannot move to a threatened cell') return False else: return True + # If move is not authorized it could mean that player is trying to do a special move, i.e. castling else: - ###print('King moving, not threatened in new cell but cannot move toward it, move not authorized') + # Checking castling conditions on the right then on the left if not self.castling_done and not self.has_moved and (move.end.y == 6 or move.end.y == 2): if move.end.y == 6: # Roque vers la droite + + # Getting the rook for castling rook_to_move = board.get_cell(move.start.x, 7).get_piece() rook_starting_coordinates = (move.start.x, 7) rook_ending_coordinates = (move.start.x, 5) + + # Listing cells that must not have material on if isinstance(rook_to_move, Rook): must_be_empty_cells = [board.get_cell(move.start.x, 5), board.get_cell(move.start.x, 6)] must_not_be_threatened_cells = [board.get_cell(move.start.x, 4), board.get_cell(move.start.x, 5), board.get_cell(move.start.x, 6)] else: - ###print('Rook has moved cannot do castling', rook_to_move) return False elif move.end.y == 2: # Roque vers la gauche rook_to_move = board.get_cell(move.start.x, 0).get_piece() rook_starting_coordinates = (move.start.x, 0) rook_ending_coordinates = (move.start.x, 3) + + # Getting the rook if isinstance(rook_to_move, Rook): + + # Listing cells that must not have material on must_be_empty_cells = [board.get_cell(move.start.x, 1), board.get_cell(move.start.x, 2), board.get_cell(move.start.x, 3)] must_not_be_threatened_cells = [board.get_cell(move.start.x, 2), board.get_cell(move.start.x, 3), board.get_cell(move.start.x, 4)] else: - ###print('Rook to move issue', rook_to_move) return False else: - ###print('Weird move ordinate', move.end.x, move.end.y) return False + # Verifying conditions for listed cells empty_cells_check = True not_threatened_cells = True for cll in must_be_empty_cells: @@ -841,6 +1210,8 @@ def can_move(self, board, move): if cll.is_threatened(board, self.is_white()): not_threatened_cells = False + # Verify that all conditions are met and completes the move so that it has the full castling information + # to operate all the movements conditions_to_castling = [not rook_to_move.has_moved, empty_cells_check, not_threatened_cells] if all(conditions_to_castling): move.complementary_castling = rook_to_move, board.get_cell(rook_starting_coordinates[0], @@ -848,22 +1219,35 @@ def can_move(self, board, move): board.get_cell(rook_ending_coordinates[0], rook_ending_coordinates[1]) return True else: - ###print('Conditions for castling:') - ###print('Rook has moved:', rook_to_move.has_moved) - ###print('Cells in between empty:', empty_cells_check) - ###print('Cells in between not threatened:', not_threatened_cells) return False return False def get_potential_moves(self, x, y): + """Method to list all the possible moves from coordinates. Only uses authorized movements, no other pieces on a + board. + + Parameters + ---------- + x: int + x coordinate of the piece + y: int + y coordinate of the piece + + Returns + ------- + list + List of authorized moves + """ possible_moves = [] + # All possible moves combos = [(1, 0), (1, 1), (-1, 1), (1, -1), (-1, -1), (0, 1), (0, -1), (-1, 0)] for nx, ny in combos: if 0 <= x+nx <= 7 and 0 <= y+ny <= 7: possible_moves.append((nx+x, ny+y)) + # Add castling as potential moves if not done yet if not self.has_moved: possible_moves.append((x, 1)) possible_moves.append((x, 6)) @@ -871,9 +1255,29 @@ def get_potential_moves(self, x, y): return possible_moves def get_str(self): + """Method to represent the piece as a string. + + Returns + ------- + str + String representation of the piece + """ return ' K ' def is_checked(self, board): + """Method to verify that the king at its current position is not threatened / checked by opponent material. + + Parameters + ---------- + board: engine.Board + Board to which the piece belongs to + + Returns + ------- + bool + Whether the king is checked or not. + """ + return board.get_cell(self.x, self.y).is_threatened(board, self.white) # def is_checked_mate(self, board): From 6ce7e540b86954ffee4f757ab72d9eace2634f58 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 09:12:18 +0000 Subject: [PATCH 04/27] :art: Format Python code with psf/black --- python/engine/color.py | 2 +- python/engine/engine.py | 237 ++++++++++++++++++++------------ python/engine/material.py | 138 +++++++++++++------ python/engine/move.py | 76 ++++++---- python/interface/interface.py | 157 +++++++++++++-------- python/player/ai_player.py | 94 +++++++++---- python/player/my_player.py | 54 ++++---- python/player/player.py | 29 ++-- python/utils/images_creation.py | 12 +- python/utils/profile_game.py | 8 +- python/utils/training_ai.py | 1 - run_app.py | 3 +- tests/engine_test.py | 50 ++++--- 13 files changed, 545 insertions(+), 316 deletions(-) diff --git a/python/engine/color.py b/python/engine/color.py index b9941f4..4b2c455 100644 --- a/python/engine/color.py +++ b/python/engine/color.py @@ -1,4 +1,4 @@ class Color: GREEN = "\x1b[32m" - WHITE = '\033[0m' + WHITE = "\033[0m" RED = "\x1b[31m" diff --git a/python/engine/engine.py b/python/engine/engine.py index 7d91d1a..e9b0fea 100644 --- a/python/engine/engine.py +++ b/python/engine/engine.py @@ -16,7 +16,7 @@ class Color: GREEN = "\x1b[32m" - WHITE = '\033[0m' + WHITE = "\033[0m" RED = "\x1b[31m" @@ -39,7 +39,6 @@ def set_piece(self, piece): self.piece.x = self.x self.piece.y = self.y - def get_piece(self): return self.piece @@ -49,9 +48,20 @@ def get_x(self): def get_y(self): return self.y - def is_threatened(self, board, threaten_color): # change threaten_color par #white_threatened + def is_threatened( + self, board, threaten_color + ): # change threaten_color par #white_threatened # Check Knights threatening - for i, j in [(2, 1), (-2, 1), (2, -1), (-2, -1), (1, 2), (1, -2), (-1, 2), (-1, -2)]: + for i, j in [ + (2, 1), + (-2, 1), + (2, -1), + (-2, -1), + (1, 2), + (1, -2), + (-1, 2), + (-1, -2), + ]: x_to_check = self.x + i y_to_check = self.y + j @@ -72,8 +82,11 @@ def is_threatened(self, board, threaten_color): # change threaten_color par #wh cell_to_check = board.get_cell(x_to_check, y_to_check) piece_to_check = cell_to_check.get_piece() - if isinstance(piece_to_check, material.King) or isinstance(piece_to_check, material.Rook) or isinstance(piece_to_check, - material.Queen): + if ( + isinstance(piece_to_check, material.King) + or isinstance(piece_to_check, material.Rook) + or isinstance(piece_to_check, material.Queen) + ): if piece_to_check.is_white() != threaten_color: return True @@ -85,7 +98,9 @@ def is_threatened(self, board, threaten_color): # change threaten_color par #wh cell_to_check = board.get_cell(x_to_check, y_to_check) piece_to_check = cell_to_check.get_piece() - if isinstance(piece_to_check, material.Rook) or isinstance(piece_to_check, material.Queen): + if isinstance(piece_to_check, material.Rook) or isinstance( + piece_to_check, material.Queen + ): keep_going = False if piece_to_check.is_white() != threaten_color: return True @@ -100,7 +115,9 @@ def is_threatened(self, board, threaten_color): # change threaten_color par #wh cell_to_check = board.get_cell(x_to_check, y_to_check) piece_to_check = cell_to_check.get_piece() - if isinstance(piece_to_check, material.Rook) or isinstance(piece_to_check, material.Queen): + if isinstance(piece_to_check, material.Rook) or isinstance( + piece_to_check, material.Queen + ): keep_going = False if piece_to_check.is_white() != threaten_color: return True @@ -115,7 +132,9 @@ def is_threatened(self, board, threaten_color): # change threaten_color par #wh cell_to_check = board.get_cell(x_to_check, y_to_check) piece_to_check = cell_to_check.get_piece() - if isinstance(piece_to_check, material.Rook) or isinstance(piece_to_check, material.Queen): + if isinstance(piece_to_check, material.Rook) or isinstance( + piece_to_check, material.Queen + ): keep_going = False if piece_to_check.is_white() != threaten_color: return True @@ -130,7 +149,9 @@ def is_threatened(self, board, threaten_color): # change threaten_color par #wh cell_to_check = board.get_cell(x_to_check, y_to_check) piece_to_check = cell_to_check.get_piece() - if isinstance(piece_to_check, material.Rook) or isinstance(piece_to_check, material.Queen): + if isinstance(piece_to_check, material.Rook) or isinstance( + piece_to_check, material.Queen + ): keep_going = False if piece_to_check.is_white() != threaten_color: return True @@ -147,14 +168,25 @@ def is_threatened(self, board, threaten_color): # change threaten_color par #wh cell_to_check = board.get_cell(x_to_check, y_to_check) piece_to_check = cell_to_check.get_piece() - if isinstance(piece_to_check, material.King) or isinstance(piece_to_check, material.Bishop) or isinstance(piece_to_check, - material.Queen): + if ( + isinstance(piece_to_check, material.King) + or isinstance(piece_to_check, material.Bishop) + or isinstance(piece_to_check, material.Queen) + ): if piece_to_check.is_white() != threaten_color: return True - elif i > 0 and threaten_color and isinstance(piece_to_check, material.Pawn): + elif ( + i > 0 + and threaten_color + and isinstance(piece_to_check, material.Pawn) + ): if piece_to_check.is_white() != threaten_color: return True - elif i < 0 and not threaten_color and isinstance(piece_to_check, material.Pawn): + elif ( + i < 0 + and not threaten_color + and isinstance(piece_to_check, material.Pawn) + ): if piece_to_check.is_white() != threaten_color: return True @@ -166,7 +198,9 @@ def is_threatened(self, board, threaten_color): # change threaten_color par #wh cell_to_check = board.get_cell(x_to_check, y_to_check) piece_to_check = cell_to_check.get_piece() - if isinstance(piece_to_check, material.Bishop) or isinstance(piece_to_check, material.Queen): + if isinstance(piece_to_check, material.Bishop) or isinstance( + piece_to_check, material.Queen + ): keep_going = False if piece_to_check.is_white() != threaten_color: return True @@ -182,7 +216,9 @@ def is_threatened(self, board, threaten_color): # change threaten_color par #wh cell_to_check = board.get_cell(x_to_check, y_to_check) piece_to_check = cell_to_check.get_piece() - if isinstance(piece_to_check, material.Bishop) or isinstance(piece_to_check, material.Queen): + if isinstance(piece_to_check, material.Bishop) or isinstance( + piece_to_check, material.Queen + ): keep_going = False if piece_to_check.is_white() != threaten_color: return True @@ -198,7 +234,9 @@ def is_threatened(self, board, threaten_color): # change threaten_color par #wh cell_to_check = board.get_cell(x_to_check, y_to_check) piece_to_check = cell_to_check.get_piece() - if isinstance(piece_to_check, material.Bishop) or isinstance(piece_to_check, material.Queen): + if isinstance(piece_to_check, material.Bishop) or isinstance( + piece_to_check, material.Queen + ): keep_going = False if piece_to_check.is_white() != threaten_color: return True @@ -214,7 +252,9 @@ def is_threatened(self, board, threaten_color): # change threaten_color par #wh cell_to_check = board.get_cell(x_to_check, y_to_check) piece_to_check = cell_to_check.get_piece() - if isinstance(piece_to_check, material.Bishop) or isinstance(piece_to_check, material.Queen): + if isinstance(piece_to_check, material.Bishop) or isinstance( + piece_to_check, material.Queen + ): keep_going = False if piece_to_check.is_white() != threaten_color: return True @@ -227,7 +267,6 @@ def is_threatened(self, board, threaten_color): # change threaten_color par #wh class Board: - def __init__(self, empty_init=False): if not empty_init: self.board = None @@ -238,15 +277,15 @@ def deepcopy(self, memodict={}): board = [[Cell(i, j, None) for j in range(8)] for i in range(8)] copied_object.board = board copied_material = self.deep_copy_material() - white_king = copied_material['white']['alive']['king'][0] - black_king = copied_material['black']['alive']['king'][0] + white_king = copied_material["white"]["alive"]["king"][0] + black_king = copied_material["black"]["alive"]["king"][0] copied_object.all_material = copied_material copied_object.white_king = white_king copied_object.black_king = black_king - for piece_list in copied_material['white']['alive'].values(): + for piece_list in copied_material["white"]["alive"].values(): for piece in piece_list: copied_object.get_cell(piece.x, piece.y).set_piece(piece) - for piece_list in copied_material['black']['alive'].values(): + for piece_list in copied_material["black"]["alive"].values(): for piece in piece_list: copied_object.get_cell(piece.x, piece.y).set_piece(piece) @@ -261,7 +300,7 @@ def deep_copy_material(self): "bishop": [], "rook": [], "queen": [], - "king": [] + "king": [], }, "killed": { "pawn": [], @@ -269,8 +308,8 @@ def deep_copy_material(self): "bishop": [], "rook": [], "queen": [], - "king": [] - } + "king": [], + }, }, "black": { "alive": { @@ -279,7 +318,7 @@ def deep_copy_material(self): "bishop": [], "rook": [], "queen": [], - "king": [] + "king": [], }, "killed": { "pawn": [], @@ -287,16 +326,18 @@ def deep_copy_material(self): "bishop": [], "rook": [], "queen": [], - "king": [] - } - } + "king": [], + }, + }, } - for color in ['white', 'black']: - for status in ['alive', 'killed']: - for piece_type in ['pawn', 'knight', 'bishop', 'rook', 'queen', 'king']: + for color in ["white", "black"]: + for status in ["alive", "killed"]: + for piece_type in ["pawn", "knight", "bishop", "rook", "queen", "king"]: for piece in self.all_material[color][status][piece_type]: - material[color][status][piece_type].append(piece.piece_deepcopy()) + material[color][status][piece_type].append( + piece.piece_deepcopy() + ) return material def __deepcopy__(self, memodict={}): @@ -305,15 +346,15 @@ def __deepcopy__(self, memodict={}): copied_object.board = board copied_material = self.deep_copy_material() - white_king = copied_material['white']['alive']['king'][0] - black_king = copied_material['black']['alive']['king'][0] + white_king = copied_material["white"]["alive"]["king"][0] + black_king = copied_material["black"]["alive"]["king"][0] copied_object.all_material = copied_material copied_object.white_king = white_king copied_object.black_king = black_king - for piece_list in copied_material['white']['alive'].values(): + for piece_list in copied_material["white"]["alive"].values(): for piece in piece_list: copied_object.get_cell(piece.x, piece.y).set_piece(piece) - for piece_list in copied_material['black']['alive'].values(): + for piece_list in copied_material["black"]["alive"].values(): for piece in piece_list: copied_object.get_cell(piece.x, piece.y).set_piece(piece) @@ -331,7 +372,7 @@ def to_fen(self): if no_piece_count > 0: fen += str(no_piece_count) no_piece_count = 0 - letter = piece.get_str().replace(' ', '') + letter = piece.get_str().replace(" ", "") if piece.is_white(): letter = letter.lower() fen += letter @@ -347,7 +388,7 @@ def one_hot_encode(self, white_side=True): "knight": [0, 0, 1, 0, 0, 0], "rook": [0, 0, 0, 1, 0, 0], "queen": [0, 0, 0, 0, 1, 0], - "king": [0, 0, 0, 0, 0, 1] + "king": [0, 0, 0, 0, 0, 1], } one_hot_board = [] for line in self.board: @@ -355,11 +396,11 @@ def one_hot_encode(self, white_side=True): for cell in line: piece = cell.get_piece() if piece is None: - one_hot_line.append([0]*6) + one_hot_line.append([0] * 6) else: one_hot_piece = material_to_one_hot[piece.type] if piece.is_white() != white_side: - one_hot_piece = [-1*val for val in one_hot_piece] + one_hot_piece = [-1 * val for val in one_hot_piece] one_hot_line.append(one_hot_piece) one_hot_board.append(one_hot_line) return one_hot_board @@ -387,7 +428,7 @@ def _reset_board(self): "bishop": [], "rook": [], "queen": [], - "king": [] + "king": [], }, "killed": { "pawn": [], @@ -395,8 +436,8 @@ def _reset_board(self): "bishop": [], "rook": [], "queen": [], - "king": [] - } + "king": [], + }, }, "black": { "alive": { @@ -405,7 +446,7 @@ def _reset_board(self): "bishop": [], "rook": [], "queen": [], - "king": [] + "king": [], }, "killed": { "pawn": [], @@ -413,9 +454,9 @@ def _reset_board(self): "bishop": [], "rook": [], "queen": [], - "king": [] - } - } + "king": [], + }, + }, } white_king = material.King(True, 0, 4) @@ -441,9 +482,16 @@ def _reset_board(self): w_queen = material.Queen(True, 0, 3) pieces["white"]["alive"]["queen"].append(w_queen) - line = [Cell(0, 0, w_rook_1), Cell(0, 1, w_knight_1), Cell(0, 2, w_bishop_1), - Cell(0, 3, w_queen), Cell(0, 4, white_king), Cell(0, 5, w_bishop_2), - Cell(0, 6, w_knight_2), Cell(0, 7, w_rook_2)] + line = [ + Cell(0, 0, w_rook_1), + Cell(0, 1, w_knight_1), + Cell(0, 2, w_bishop_1), + Cell(0, 3, w_queen), + Cell(0, 4, white_king), + Cell(0, 5, w_bishop_2), + Cell(0, 6, w_knight_2), + Cell(0, 7, w_rook_2), + ] board.append(line) line = [] @@ -456,7 +504,7 @@ def _reset_board(self): for i in range(4): line = [] for j in range(8): - line.append(Cell(i+2, j, None)) + line.append(Cell(i + 2, j, None)) board.append(line) line = [] @@ -484,9 +532,16 @@ def _reset_board(self): b_queen = material.Queen(False, 7, 3) pieces["black"]["alive"]["queen"].append(b_queen) - line = [Cell(7, 0, b_rook_1), Cell(7, 1, b_knight_1), Cell(7, 2, b_bishop_1), - Cell(7, 3, b_queen), Cell(7, 4, black_king), Cell(7, 5, b_bishop_2), - Cell(7, 6, b_knight_2), Cell(7, 7, b_rook_2)] + line = [ + Cell(7, 0, b_rook_1), + Cell(7, 1, b_knight_1), + Cell(7, 2, b_bishop_1), + Cell(7, 3, b_queen), + Cell(7, 4, black_king), + Cell(7, 5, b_bishop_2), + Cell(7, 6, b_knight_2), + Cell(7, 7, b_rook_2), + ] board.append(line) self.board = board @@ -531,33 +586,32 @@ def transform_pawn(self, coordinates): self.all_material[color]["alive"]["queen"].append(new_queen) def draw(self, printing=True): - whole_text = ' | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |' + whole_text = " | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |" # ###print(' | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |') - boarder_line = '+---+-----+-----+-----+-----+-----+-----+-----+-----+' + boarder_line = "+---+-----+-----+-----+-----+-----+-----+-----+-----+" # ###print(boarder_line) - whole_text += '\n' + whole_text += "\n" whole_text += boarder_line for i in range(8): - current_line = ' ' + str(i) + ' |' + current_line = " " + str(i) + " |" for j in range(8): cell = self.get_cell(i, j) if cell.get_piece() is None: - current_line += ' ' + current_line += " " else: current_line += cell.get_piece().draw() - current_line += '|' - whole_text += '\n' + current_line += "|" + whole_text += "\n" # ###print(current_line) whole_text += current_line # ###print(boarder_line) - whole_text += '\n' + whole_text += "\n" whole_text += boarder_line print(whole_text) return whole_text class Game: - game_status = [] def __init__(self, automatic_draw=True, ai=False): @@ -572,7 +626,7 @@ def __init__(self, automatic_draw=True, ai=False): self.to_play_player = self.player1 self.board = Board() - self.status = 'ACTIVE' + self.status = "ACTIVE" self.played_moves = [] self.automatic_draw = automatic_draw @@ -584,11 +638,11 @@ def reset_game(self): def to_fen(self): pieces, castling = self.board.to_fen() - color_playing = 'w' if self.to_play_player.is_white_side() else "b" + color_playing = "w" if self.to_play_player.is_white_side() else "b" return pieces + " " + color_playing + " " + castling + " - 0 1" def is_finished(self): - return self.status != 'ACTIVE' + return self.status != "ACTIVE" def move_from_coordinates(self, player, start_x, start_y, end_x, end_y): start_cell = self.board.get_cell(start_x, start_y) @@ -608,12 +662,15 @@ def can_player_move(self, player): selected_piece = self.board.get_cell(i, j).get_piece() if selected_piece is not None: if selected_piece.is_white() == player.is_white_side(): - possible_moves = selected_piece.get_potential_moves(i, j) for k in range(len(possible_moves)): selected_move = possible_moves[k] - selected_move = Move(player, self.board, self.board.get_cell(i, j), - self.board.get_cell(selected_move[0], selected_move[1])) + selected_move = Move( + player, + self.board, + self.board.get_cell(i, j), + self.board.get_cell(selected_move[0], selected_move[1]), + ) verified_move = selected_move.is_possible_move() if verified_move: @@ -632,14 +689,15 @@ def check_pat_mat(self, player): king = self.board.white_king else: king = self.board.black_king - is_mat = self.board.get_cell(king.x, king.y).is_threatened(self.board, not player.is_white_side) + is_mat = self.board.get_cell(king.x, king.y).is_threatened( + self.board, not player.is_white_side + ) if is_mat: return 2 else: return 1 def move(self, move, player): - moved_piece = move.moved_piece # List of checks @@ -679,11 +737,11 @@ def move(self, move, player): self.board.draw() # self.save() if self.board.white_king.is_killed(): - print('END OF THE GAME, BLACK HAS WON') - return False, 'black' + print("END OF THE GAME, BLACK HAS WON") + return False, "black" elif self.board.black_king.is_killed(): - print('END OF THE GAME, WHITE HAS WON') - return False, 'white' + print("END OF THE GAME, WHITE HAS WON") + return False, "white" ###print('PLAYER TO PLAY:', self.to_play_player) @@ -696,31 +754,34 @@ def update_status(self): game_status = self.check_pat_mat(self.player1) if game_status == 1: ###print('PAT, white & black do not differentiate each other') - return False, 'black&white' + return False, "black&white" elif game_status == 2: ###print('END OF THE GAME, MAT DETECTED, BLACK HAS WON') - return False, 'black' + return False, "black" else: game_status = self.check_pat_mat(self.player2) if game_status == 1: ###print('PAT, white & black do not differentiate each other') - return False, 'black&white' + return False, "black&white" elif game_status == 2: ###print('END OF THE GAME, MAT DETECTED WHITE HAS WON') - return False, 'white' + return False, "white" else: ###print('Game keeps going') - return True, '' + return True, "" - def save(self, directory='debug_files'): + def save(self, directory="debug_files"): draw_text = self.draw_board() draw_text = draw_text.replace("\x1b[32m", "") - draw_text = draw_text.replace('\033[0m', "") + draw_text = draw_text.replace("\033[0m", "") draw_text = draw_text.replace("\x1b[31m", "") import os import matplotlib.pyplot as plt - plt.rc('figure', figsize=(12, 7)) - plt.text(0.01, 0.05, str(draw_text), {"fontsize": 10}, fontproperties='monospace') - plt.axis('off') + + plt.rc("figure", figsize=(12, 7)) + plt.text( + 0.01, 0.05, str(draw_text), {"fontsize": 10}, fontproperties="monospace" + ) + plt.axis("off") plt.tight_layout() - plt.savefig(os.path.join(directory, str(len(self.played_moves))+'.png')) + plt.savefig(os.path.join(directory, str(len(self.played_moves)) + ".png")) diff --git a/python/engine/material.py b/python/engine/material.py index b078689..5f3b8f4 100644 --- a/python/engine/material.py +++ b/python/engine/material.py @@ -77,8 +77,7 @@ def is_killed(self): return self.killed def set_killed(self): - """Sets the piece status to killed. - """ + """Sets the piece status to killed.""" self.killed = True @abstractmethod @@ -149,7 +148,7 @@ def get_str(self): str String representation of the piece """ - return ' ' + return " " def draw(self): """Method to represent the piece as a colored string in order to draw a board. @@ -204,7 +203,9 @@ def __init__(self, *args, **kwargs): """ super().__init__(*args, **kwargs) self.has_moved = False # if the pawn has yet been moved or not to keep ? - self.last_move_is_double = False # check for en passant, if last move was a double tap + self.last_move_is_double = ( + False # check for en passant, if last move was a double tap + ) def piece_deepcopy(self): """Method to create an uncorrelated clone of the piece. @@ -237,7 +238,6 @@ def piece_move_authorized(self, start, end): """ # Check if there is a piece on the landing cell if end.get_piece() is not None: - # check if there is not another piece of same color if end.get_piece().is_white() == self.is_white(): return False @@ -266,7 +266,9 @@ def piece_move_authorized(self, start, end): # Initial move authorized to be two cells at once. Should check self.has_moved here ? if start.get_x() == 1 and dx == 2 and dy == 0 and self.is_white(): return True - elif start.get_x() == 6 and dx == -2 and dy == 0 and not self.is_white(): + elif ( + start.get_x() == 6 and dx == -2 and dy == 0 and not self.is_white() + ): return True else: return False @@ -293,7 +295,10 @@ def can_move(self, board, move): crossed_cell = board.get_cell(move.start.get_x(), move.end.get_y()) crossed_piece = crossed_cell.get_piece() if isinstance(crossed_piece, Pawn): - if crossed_piece.last_move_is_double and crossed_piece.is_white() != self.is_white(): + if ( + crossed_piece.last_move_is_double + and crossed_piece.is_white() != self.is_white() + ): # Revoir comment on update cet attribut last_move_is_double authorized_move = True move.complementary_passant = crossed_cell @@ -302,10 +307,20 @@ def can_move(self, board, move): dx = move.end.get_x() - move.start.get_x() if dx > 1: - if board.get_cell(move.start.get_x()+1, move.start.get_y()).get_piece() is not None: + if ( + board.get_cell( + move.start.get_x() + 1, move.start.get_y() + ).get_piece() + is not None + ): return False elif dx < -1: - if board.get_cell(move.start.get_x()-1, move.start.get_y()).get_piece() is not None: + if ( + board.get_cell( + move.start.get_x() - 1, move.start.get_y() + ).get_piece() + is not None + ): return False """ if move.end.get_x() == 7 and self.is_white(): @@ -371,7 +386,7 @@ def get_str(self): str String representation of the piece """ - return ' P ' + return " P " class Bishop(Piece): @@ -541,7 +556,7 @@ def get_str(self): str String representation of the piece """ - return ' B ' + return " B " class Rook(Piece): @@ -715,7 +730,7 @@ def get_str(self): str String representation of the piece """ - return ' R ' + return " R " class Knight(Piece): @@ -813,7 +828,7 @@ def get_str(self): str String representation of the piece """ - return ' N ' + return " N " def get_potential_moves(self, x, y): """Method to list all the possible moves from coordinates. Only uses authorized movements, no other pieces on a @@ -834,10 +849,19 @@ def get_potential_moves(self, x, y): possible_moves = [] # All difference position that a knight can move to - combos = [(2, 1), (1, 2), (-2, 1), (2, -1), (-2, -1), (-1, 2), (1, -2), (-1, -2)] + combos = [ + (2, 1), + (1, 2), + (-2, 1), + (2, -1), + (-2, -1), + (-1, 2), + (1, -2), + (-1, -2), + ] for nx, ny in combos: - if 0 <= nx+x <= 7 and 0 <= ny+y <= 7: - possible_moves.append((x+nx, y+ny)) + if 0 <= nx + x <= 7 and 0 <= ny + y <= 7: + possible_moves.append((x + nx, y + ny)) return possible_moves @@ -943,13 +967,19 @@ def can_move(self, board, move): for i in range(1, abs(dx)): x_trajectory = i * int(dx / abs(dx)) + move.start.get_x() y_trajectory = move.start.get_y() - if board.get_cell(x_trajectory, y_trajectory).get_piece() is not None: + if ( + board.get_cell(x_trajectory, y_trajectory).get_piece() + is not None + ): return False # Along Y-axis for i in range(1, abs(dy)): x_trajectory = move.start.get_x() y_trajectory = i * int(dy / abs(dy)) + move.start.get_y() - if board.get_cell(x_trajectory, y_trajectory).get_piece() is not None: + if ( + board.get_cell(x_trajectory, y_trajectory).get_piece() + is not None + ): return False return True @@ -958,7 +988,10 @@ def can_move(self, board, move): for i in range(1, abs(dx)): x_trajectory = i * int(dx / abs(dx)) + move.start.get_x() y_trajectory = i * int(dy / abs(dy)) + move.start.get_y() - if board.get_cell(x_trajectory, y_trajectory).get_piece() is not None: + if ( + board.get_cell(x_trajectory, y_trajectory).get_piece() + is not None + ): return False return True else: @@ -1048,7 +1081,7 @@ def get_str(self): str String representation of the piece """ - return ' Q ' + return " Q " class King(Piece): @@ -1161,11 +1194,13 @@ def can_move(self, board, move): return True # If move is not authorized it could mean that player is trying to do a special move, i.e. castling else: - # Checking castling conditions on the right then on the left - if not self.castling_done and not self.has_moved and (move.end.y == 6 or move.end.y == 2): + if ( + not self.castling_done + and not self.has_moved + and (move.end.y == 6 or move.end.y == 2) + ): if move.end.y == 6: # Roque vers la droite - # Getting the rook for castling rook_to_move = board.get_cell(move.start.x, 7).get_piece() rook_starting_coordinates = (move.start.x, 7) @@ -1173,10 +1208,15 @@ def can_move(self, board, move): # Listing cells that must not have material on if isinstance(rook_to_move, Rook): - must_be_empty_cells = [board.get_cell(move.start.x, 5), board.get_cell(move.start.x, 6)] - must_not_be_threatened_cells = [board.get_cell(move.start.x, 4), - board.get_cell(move.start.x, 5), - board.get_cell(move.start.x, 6)] + must_be_empty_cells = [ + board.get_cell(move.start.x, 5), + board.get_cell(move.start.x, 6), + ] + must_not_be_threatened_cells = [ + board.get_cell(move.start.x, 4), + board.get_cell(move.start.x, 5), + board.get_cell(move.start.x, 6), + ] else: return False @@ -1187,13 +1227,17 @@ def can_move(self, board, move): # Getting the rook if isinstance(rook_to_move, Rook): - - # Listing cells that must not have material on - must_be_empty_cells = [board.get_cell(move.start.x, 1), board.get_cell(move.start.x, 2), - board.get_cell(move.start.x, 3)] - must_not_be_threatened_cells = [board.get_cell(move.start.x, 2), - board.get_cell(move.start.x, 3), - board.get_cell(move.start.x, 4)] + # Listing cells that must not have material on + must_be_empty_cells = [ + board.get_cell(move.start.x, 1), + board.get_cell(move.start.x, 2), + board.get_cell(move.start.x, 3), + ] + must_not_be_threatened_cells = [ + board.get_cell(move.start.x, 2), + board.get_cell(move.start.x, 3), + board.get_cell(move.start.x, 4), + ] else: return False @@ -1212,17 +1256,26 @@ def can_move(self, board, move): # Verify that all conditions are met and completes the move so that it has the full castling information # to operate all the movements - conditions_to_castling = [not rook_to_move.has_moved, empty_cells_check, not_threatened_cells] + conditions_to_castling = [ + not rook_to_move.has_moved, + empty_cells_check, + not_threatened_cells, + ] if all(conditions_to_castling): - move.complementary_castling = rook_to_move, board.get_cell(rook_starting_coordinates[0], - rook_starting_coordinates[1]), \ - board.get_cell(rook_ending_coordinates[0], rook_ending_coordinates[1]) + move.complementary_castling = ( + rook_to_move, + board.get_cell( + rook_starting_coordinates[0], rook_starting_coordinates[1] + ), + board.get_cell( + rook_ending_coordinates[0], rook_ending_coordinates[1] + ), + ) return True else: return False return False - def get_potential_moves(self, x, y): """Method to list all the possible moves from coordinates. Only uses authorized movements, no other pieces on a board. @@ -1244,8 +1297,8 @@ def get_potential_moves(self, x, y): # All possible moves combos = [(1, 0), (1, 1), (-1, 1), (1, -1), (-1, -1), (0, 1), (0, -1), (-1, 0)] for nx, ny in combos: - if 0 <= x+nx <= 7 and 0 <= y+ny <= 7: - possible_moves.append((nx+x, ny+y)) + if 0 <= x + nx <= 7 and 0 <= y + ny <= 7: + possible_moves.append((nx + x, ny + y)) # Add castling as potential moves if not done yet if not self.has_moved: @@ -1262,7 +1315,7 @@ def get_str(self): str String representation of the piece """ - return ' K ' + return " K " def is_checked(self, board): """Method to verify that the king at its current position is not threatened / checked by opponent material. @@ -1296,4 +1349,3 @@ def is_checked(self, board): # # if verified_move: # copied_board = board.copy() - diff --git a/python/engine/move.py b/python/engine/move.py index 99e05ce..2613ffb 100644 --- a/python/engine/move.py +++ b/python/engine/move.py @@ -22,8 +22,12 @@ def __init__(self, player, board, start, end): def deepcopy(self): copied_board = self.board.deepcopy() - copied_move = Move(self.player, copied_board, copied_board.get_cell(self.start.x, self.start.y), - copied_board.get_cell(self.end.x, self.end.y)) + copied_move = Move( + self.player, + copied_board, + copied_board.get_cell(self.start.x, self.start.y), + copied_board.get_cell(self.end.x, self.end.y), + ) copied_move.is_castling = self.is_castling copied_move.complementary_castling = self.complementary_castling copied_move.en_passant = self.en_passant @@ -31,11 +35,11 @@ def deepcopy(self): return copied_move def _set_moved_attribute(self): - if hasattr(self.moved_piece, 'has_moved'): + if hasattr(self.moved_piece, "has_moved"): self.moved_piece.has_moved = True ###print('PIECE', self.moved_piece.is_white(), self.moved_piece, "set to moved") ###print(self.start.x, self.start.y, self.end.x, self.end.y) - if hasattr(self.moved_piece, 'last_move_is_double'): + if hasattr(self.moved_piece, "last_move_is_double"): if abs(self.start.get_x() - self.end.get_x()) > 1: self.moved_piece.last_move_is_double = True else: @@ -67,11 +71,15 @@ def _is_castling(self): else: rook_starting_coordinates = (self.start.x, 7) rook_ending_coordinates = (self.start.x, 5) - must_be_empty_cells = [self.board.get_cell(self.start.x, 5), - self.board.get_cell(self.start.x, 6)] - must_not_be_threatened_cells = [self.board.get_cell(self.start.x, 4), - self.board.get_cell(self.start.x, 5), - self.board.get_cell(self.start.x, 6)] + must_be_empty_cells = [ + self.board.get_cell(self.start.x, 5), + self.board.get_cell(self.start.x, 6), + ] + must_not_be_threatened_cells = [ + self.board.get_cell(self.start.x, 4), + self.board.get_cell(self.start.x, 5), + self.board.get_cell(self.start.x, 6), + ] elif self.end.y == 2: # Castling on the left rook_to_move = self.board.get_cell(self.start.x, 0).get_piece() @@ -84,12 +92,16 @@ def _is_castling(self): else: rook_starting_coordinates = (self.start.x, 0) rook_ending_coordinates = (self.start.x, 3) - must_be_empty_cells = [self.board.get_cell(self.start.x, 1), - self.board.get_cell(self.start.x, 2), - self.board.get_cell(self.start.x, 3)] - must_not_be_threatened_cells = [self.board.get_cell(self.start.x, 2), - self.board.get_cell(self.start.x, 3), - self.board.get_cell(self.start.x, 4)] + must_be_empty_cells = [ + self.board.get_cell(self.start.x, 1), + self.board.get_cell(self.start.x, 2), + self.board.get_cell(self.start.x, 3), + ] + must_not_be_threatened_cells = [ + self.board.get_cell(self.start.x, 2), + self.board.get_cell(self.start.x, 3), + self.board.get_cell(self.start.x, 4), + ] else: ###print('king did not move to a castling position') return False @@ -106,11 +118,15 @@ def _is_castling(self): conditions_to_castling = [empty_cells_check, not_threatened_cells] if all(conditions_to_castling): - self.complementary_castling = rook_to_move, \ - self.board.get_cell(rook_starting_coordinates[0], - rook_starting_coordinates[1]), \ - self.board.get_cell(rook_ending_coordinates[0], - rook_ending_coordinates[1]) + self.complementary_castling = ( + rook_to_move, + self.board.get_cell( + rook_starting_coordinates[0], rook_starting_coordinates[1] + ), + self.board.get_cell( + rook_ending_coordinates[0], rook_ending_coordinates[1] + ), + ) return True else: @@ -122,7 +138,6 @@ def _is_castling(self): def _is_en_passant(self): if isinstance(self.moved_piece, material.Pawn): - dx = self.start.get_x() - self.end.get_x() dy = self.start.get_y() - self.end.get_y() if dy == 0 or self.killed_piece is not None: @@ -132,7 +147,10 @@ def _is_en_passant(self): crossed_cell = self.board.get_cell(self.start.get_x(), self.end.get_y()) crossed_piece = crossed_cell.get_piece() if isinstance(crossed_piece, material.Pawn): - if crossed_piece.last_move_is_double and crossed_piece.is_white() != self.moved_piece.is_white(): + if ( + crossed_piece.last_move_is_double + and crossed_piece.is_white() != self.moved_piece.is_white() + ): # Revoir comment on update cet attribut last_move_is_double self.killed_piece = crossed_piece self.en_passant = True @@ -167,12 +185,16 @@ def move_pieces(self): if self.killed_piece is not None: self.board.kill_piece_from_coordinates((self.end.x, self.end.y)) - self.board.move_piece_from_coordinates((self.start.x, self.start.y), (self.end.x, self.end.y)) + self.board.move_piece_from_coordinates( + (self.start.x, self.start.y), (self.end.x, self.end.y) + ) # ADD CASTLING if self.complementary_castling is not None and self.is_castling: castling_rook, rook_start, rook_end = self.complementary_castling - self.board.move_piece_from_coordinates((rook_start.x, rook_start.y), (rook_end.x, rook_end.y)) + self.board.move_piece_from_coordinates( + (rook_start.x, rook_start.y), (rook_end.x, rook_end.y) + ) ###print("CASTLING DETECTED PPPPPPPPP") self._set_castling_done() @@ -180,7 +202,7 @@ def move_pieces(self): self._transform_pawn() self._set_moved_attribute() - def is_possible_move(self): # REFONDRE + def is_possible_move(self): # REFONDRE # To be implemented # Should be kept ? @@ -289,4 +311,6 @@ def _work_future_to_check_chess(self): king = move.board.white_king else: king = move.board.black_king - return move.board.get_cell(king.x, king.y).is_threatened(move.board, king.is_white()) \ No newline at end of file + return move.board.get_cell(king.x, king.y).is_threatened( + move.board, king.is_white() + ) diff --git a/python/interface/interface.py b/python/interface/interface.py index 0a71766..cf99006 100644 --- a/python/interface/interface.py +++ b/python/interface/interface.py @@ -15,20 +15,18 @@ from engine.engine import Game - class LoginScreen(GridLayout): - def __init__(self, **kwargs): super(LoginScreen, self).__init__(**kwargs) self.cols = 8 - self.add_widget(Label(text='')) + self.add_widget(Label(text="")) self.username = TextInput(multiline=False) self.add_widget(self.username) - self.add_widget(Label(text='password')) + self.add_widget(Label(text="password")) self.password = TextInput(password=True, multiline=False) self.add_widget(self.password) - self.add_widget(Label(text='password2')) + self.add_widget(Label(text="password2")) self.password2 = TextInput(password=True, multiline=False) self.add_widget(self.password2) @@ -36,7 +34,6 @@ def __init__(self, **kwargs): class DisplayableCell(Button): - def __init__(self, row, column, **kwargs): super(DisplayableCell, self).__init__(**kwargs) self.row = row @@ -44,10 +41,9 @@ def __init__(self, row, column, **kwargs): class TableScreen(GridLayout): - def __init__(self, game, **kwargs): super(TableScreen, self).__init__(**kwargs) - self.path_to_illustrations = 'illustrations' + self.path_to_illustrations = "illustrations" self.game = game if game.ai: @@ -69,53 +65,70 @@ def __init__(self, game, **kwargs): for j in range(8): if (i % 2 == 0 and j % 2 == 0) or (i % 2 == 1 and j % 2 == 1): color = (0.4, 0.4, 0.8, 1) - c_img = 'b' + c_img = "b" else: color = (0.4, 0.8, 0.4, 1) - c_img = 'w' + c_img = "w" # self.add_widget(Button(text='Button %i %i' % (i, j), background_color=color, # background_down='illustrations/white_pawn.png', # background_normal='illustrations/white_queen.png')) piece = game.board.get_cell(i, j).get_piece() if piece is not None: - path_to_img = c_img if piece.is_white(): piece_color = (1, 1, 1, 1) - path_to_img += 'w' + path_to_img += "w" else: piece_color = (0, 0, 0, 1) - path_to_img += 'b' - path_to_img += ('_' + piece.get_str().replace(' ', '') + '.png') - path_to_down_img = 'down_' + path_to_img + path_to_img += "b" + path_to_img += "_" + piece.get_str().replace(" ", "") + ".png" + path_to_down_img = "down_" + path_to_img path_to_img = os.path.join(self.path_to_illustrations, path_to_img) - path_to_down_img = os.path.join(self.path_to_illustrations, path_to_down_img) + path_to_down_img = os.path.join( + self.path_to_illustrations, path_to_down_img + ) piece = piece.get_str() - button = DisplayableCell(text=piece, on_press=self.click_cell, row=i, column=j, - color=piece_color, background_normal=path_to_img, border=(0, 0, 0, 0), - background_down=path_to_down_img) + button = DisplayableCell( + text=piece, + on_press=self.click_cell, + row=i, + column=j, + color=piece_color, + background_normal=path_to_img, + border=(0, 0, 0, 0), + background_down=path_to_down_img, + ) else: - piece = '' + piece = "" piece_color = (1, 1, 1, 1) - path_to_img = c_img + '.png' - path_to_down_img = 'down_' + path_to_img + path_to_img = c_img + ".png" + path_to_down_img = "down_" + path_to_img path_to_img = os.path.join(self.path_to_illustrations, path_to_img) - path_to_down_img = os.path.join(self.path_to_illustrations, path_to_down_img) - - button = DisplayableCell(text=piece, background_normal=path_to_img, on_press=self.click_cell, row=i, - column=j, color=piece_color, border=(0, 0, 0, 0), - background_down=path_to_down_img) + path_to_down_img = os.path.join( + self.path_to_illustrations, path_to_down_img + ) + + button = DisplayableCell( + text=piece, + background_normal=path_to_img, + on_press=self.click_cell, + row=i, + column=j, + color=piece_color, + border=(0, 0, 0, 0), + background_down=path_to_down_img, + ) self.add_widget(button) line.append(button) self.cells.append(line) def reset_game(self, button): - print('On click, Reset', button) + print("On click, Reset", button) self.game.reset_game() self.update() @@ -124,41 +137,43 @@ def update(self): for i in range(8): for j in range(8): if (i % 2 == 0 and j % 2 == 0) or (i % 2 == 1 and j % 2 == 1): - c_img = 'b' + c_img = "b" else: - c_img = 'w' + c_img = "w" piece = board.get_cell(i, j).get_piece() if piece is not None: path_to_img = c_img if piece.is_white(): piece_color = (1, 1, 1, 1) - path_to_img += 'w' + path_to_img += "w" else: piece_color = (0, 0, 0, 1) - path_to_img += 'b' + path_to_img += "b" piece = piece.get_str() - path_to_img += ('_' + piece.replace(' ', '') + '.png') + path_to_img += "_" + piece.replace(" ", "") + ".png" else: - piece = '' + piece = "" piece_color = (1, 1, 1, 1) - path_to_img = c_img + '.png' + path_to_img = c_img + ".png" - path_to_down_img = 'down_' + path_to_img + path_to_down_img = "down_" + path_to_img path_to_img = os.path.join(self.path_to_illustrations, path_to_img) - path_to_down_img = os.path.join(self.path_to_illustrations, path_to_down_img) + path_to_down_img = os.path.join( + self.path_to_illustrations, path_to_down_img + ) self.cells[i][j].text = piece self.cells[i][j].color = piece_color self.cells[i][j].background_normal = path_to_img self.cells[i][j].background_down = path_to_down_img def finish_game(self, winner): - popup = Popup(title='Game finished', auto_dismiss=False) + popup = Popup(title="Game finished", auto_dismiss=False) popup.bind(on_dismiss=self.reset_game) box = BoxLayout() - box.add_widget(Label(text='Congratulations %s has won' % winner)) - restart_button = Button(text='Restart a game!') + box.add_widget(Label(text="Congratulations %s has won" % winner)) + restart_button = Button(text="Restart a game!") restart_button.bind(on_press=popup.dismiss) box.add_widget(restart_button) @@ -167,12 +182,20 @@ def finish_game(self, winner): popup.open() def click_cell(self, event): - self.cells[event.row][event.column].background_normal, self.cells[event.row][event.column].background_down = \ - self.cells[event.row][event.column].background_down, self.cells[event.row][event.column].background_normal + ( + self.cells[event.row][event.column].background_normal, + self.cells[event.row][event.column].background_down, + ) = ( + self.cells[event.row][event.column].background_down, + self.cells[event.row][event.column].background_normal, + ) if self.first_cell_clicked is None: self.first_cell_clicked = (event.row, event.column) - elif self.first_cell_clicked[0] == event.row and self.first_cell_clicked[1] == event.column: - print('Selection Aborted') + elif ( + self.first_cell_clicked[0] == event.row + and self.first_cell_clicked[1] == event.column + ): + print("Selection Aborted") self.first_cell_clicked = None else: start_x = self.first_cell_clicked[0] @@ -181,15 +204,25 @@ def click_cell(self, event): end_y = event.column print(self.game.player1, self.game.to_play_player) - validated_move, winner = self.game.move_from_coordinates(self.game.to_play_player, start_x, start_y, end_x, end_y) - print('Validated move ?', validated_move, self.game.to_play_player, start_x, start_y, end_x, end_y, winner) + validated_move, winner = self.game.move_from_coordinates( + self.game.to_play_player, start_x, start_y, end_x, end_y + ) + print( + "Validated move ?", + validated_move, + self.game.to_play_player, + start_x, + start_y, + end_x, + end_y, + winner, + ) if validated_move: self.update() if self.ai_playing: - - print('Time for AI') + print("Time for AI") ai_move = self.game.player2.time_to_play(self.game.board) self.game.board.draw() game_is_on = self.game.move(ai_move, self.game.player2) @@ -203,28 +236,34 @@ def click_cell(self, event): pass elif isinstance(winner, str): - print('WINNER', winner) + print("WINNER", winner) self.finish_game(winner) return None row, col = self.first_cell_clicked - self.cells[row][col].background_normal, self.cells[row][col].background_down = \ - self.cells[row][event.column].background_down, self.cells[event.row][col].background_normal + ( + self.cells[row][col].background_normal, + self.cells[row][col].background_down, + ) = ( + self.cells[row][event.column].background_down, + self.cells[event.row][col].background_normal, + ) self.first_cell_clicked = None - self.cells[event.row][event.column].background_normal, self.cells[event.row][event.column].background_down = \ - self.cells[event.row][event.column].background_down, self.cells[event.row][event.column].background_normal - + ( + self.cells[event.row][event.column].background_normal, + self.cells[event.row][event.column].background_down, + ) = ( + self.cells[event.row][event.column].background_down, + self.cells[event.row][event.column].background_normal, + ) class MyApp(App): - def __init__(self, play_with_ai=False, **kwargs): super().__init__(**kwargs) self.play_with_ai = play_with_ai - + def build(self): game = Game(automatic_draw=False, ai=self.play_with_ai) - print('game created') + print("game created") return TableScreen(game) - - diff --git a/python/player/ai_player.py b/python/player/ai_player.py index 6d2031e..076819e 100644 --- a/python/player/ai_player.py +++ b/python/player/ai_player.py @@ -8,25 +8,24 @@ class EasyAIPlayer(Player): - piece_weights = { "pawn": 10, "knight": 30, "bishop": 30, "rook": 50, "queen": 90, - "king": 900 + "king": 900, } piece_positions_weights = { "pawn": [ - [0.]*8, - [5.0]*8, + [0.0] * 8, + [5.0] * 8, [1.0, 1.0, 2.0, 3.0, 3.0, 2.0, 1.0, 1.0], [0.5, 0.5, 1.0, 2.5, 2.5, 1.0, 0.5, 0.5], [0.0, 0.0, 0.0, 2.0, 2.0, 0.0, 0.0, 0.0], [0.5, -0.5, -1.0, 0.0, 0.0, -1.0, -0.5, 0.5], [0.5, 1.0, 1.0, -2.0, -2.0, 1.0, 1.0, 0.5], - [0.]*8 + [0.0] * 8, ], "bishop": [ [-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0], @@ -36,7 +35,7 @@ class EasyAIPlayer(Player): [-1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, -1.0], [-1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0], [-1.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, -1.0], - [-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0] + [-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0], ], "knight": [ [-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0], @@ -46,7 +45,7 @@ class EasyAIPlayer(Player): [-3.0, 0.0, 1.5, 2.0, 2.0, 1.5, 0.0, -3.0], [-3.0, 0.5, 1.0, 1.5, 1.5, 1.0, 0.5, -3.0], [-4.0, -2.0, 0.0, 0.5, 0.5, 0.0, -2.0, -4.0], - [-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0] + [-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0], ], "rook": [ [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], @@ -56,7 +55,7 @@ class EasyAIPlayer(Player): [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5], [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5], [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5], - [0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0] + [0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0], ], "queen": [ [-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0], @@ -66,7 +65,7 @@ class EasyAIPlayer(Player): [0.0, 0.0, 0.5, 0.5, 0.5, 0.5, 0.0, 0.0], [-1.0, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -1.0], [-1.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0], - [-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0] + [-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0], ], "king": [ [-3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0], @@ -76,8 +75,8 @@ class EasyAIPlayer(Player): [-2.0, -3.0, -3.0, -4.0, -4.0, -3.0, -3.0, -2.0], [-1.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -1.0], [2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 2.0], - [2.0, 3.0, 1.0, 0.0, 0.0, 1.0, 3.0, 2.0] - ] + [2.0, 3.0, 1.0, 0.0, 0.0, 1.0, 3.0, 2.0], + ], } def __init__(self, *args, **kwargs): @@ -91,13 +90,14 @@ def __init__(self, *args, **kwargs): self.piece_positions_weights[key] = new_values def __str__(self): - return 'EasyAIPlayer' + return "EasyAIPlayer" def _get_possible_moves(self, board, is_white=None): if is_white is None: is_white = self.white_side() if self.white_side != is_white: + class TempPlayer: def __init__(self): self.white_side = is_white @@ -119,8 +119,12 @@ def is_white_side(self): if isinstance(piece, material.Pawn): ###print("POSSIBLE MOVES FOR PAWN", piece_available_moves) pass - selected_move = move.Move(player, board, board.get_cell(piece.x, piece.y), - board.get_cell(mv[0], mv[1])) + selected_move = move.Move( + player, + board, + board.get_cell(piece.x, piece.y), + board.get_cell(mv[0], mv[1]), + ) if selected_move.is_possible_move(): possible_moves.append(selected_move) ###print("possible move +1") @@ -166,7 +170,9 @@ def _select_move_from_score(self, moves, method="max"): def _search_tree(self, init_board, depth=2, method="max"): possible_moves = self._get_possible_moves(init_board) if depth == 1: - best_move, best_score = self._select_move_from_score(possible_moves, method=method) + best_move, best_score = self._select_move_from_score( + possible_moves, method=method + ) return best_move, best_score else: new_method = {"max": "min", "min": "max"} @@ -176,7 +182,9 @@ def _search_tree(self, init_board, depth=2, method="max"): # p_mv = pickle.loads(pickle.dumps(p_mv, -1)) # p_mv = copy.deepcopy(p_mv) p_mv.move_pieces() - _, score = self._search_tree(p_mv.board, depth=depth-1, method=new_method[method]) + _, score = self._search_tree( + p_mv.board, depth=depth - 1, method=new_method[method] + ) scores.append(score) if method == "max": @@ -184,12 +192,11 @@ def _search_tree(self, init_board, depth=2, method="max"): else: best_score = np.min(score) - best_indexes = np.where(np.array(scores)==best_score)[0] + best_indexes = np.where(np.array(scores) == best_score)[0] final_index = best_indexes[np.random.permutation(len(best_indexes))[0]] ###print(final_index) return possible_moves[int(final_index)], best_score - def _score_move(self, move): all_scores = {} mv_ = move.deepcopy() @@ -200,7 +207,15 @@ def _score_move(self, move): return move, score - def _alpha_beta(self, init_board, init_move=None, depth=2, alpha=-10000, beta=10000, is_white=None): + def _alpha_beta( + self, + init_board, + init_move=None, + depth=2, + alpha=-10000, + beta=10000, + is_white=None, + ): if is_white is None: is_white = self.white_side ###print('ALPHA BETA FOR BOARD:', "with depth", depth) @@ -224,8 +239,14 @@ def _alpha_beta(self, init_board, init_move=None, depth=2, alpha=-10000, beta=10 # p_mv_ = copy.deepcopy(p_mv) p_mv_ = p_mv.deepcopy() p_mv_.move_pieces() - score, _ = self._alpha_beta(p_mv_.board, init_move=p_mv_, depth=depth-1, alpha=alpha, beta=beta, - is_white=not is_white) + score, _ = self._alpha_beta( + p_mv_.board, + init_move=p_mv_, + depth=depth - 1, + alpha=alpha, + beta=beta, + is_white=not is_white, + ) ###print(score, p_mv.start.x, p_mv.start.y, p_mv.end.x, p_mv.end.y) best_move = [best_move, p_mv][np.argmax([best_score, score])] best_score = np.max([best_score, score]) @@ -246,8 +267,14 @@ def _alpha_beta(self, init_board, init_move=None, depth=2, alpha=-10000, beta=10 # p_mv_ = pickle.loads(pickle.dumps(p_mv, -1)) p_mv_ = p_mv.deepcopy() p_mv_.move_pieces() - score, _ = self._alpha_beta(p_mv_.board, init_move=p_mv_, depth=depth-1, alpha=alpha, beta=beta, - is_white=is_white) + score, _ = self._alpha_beta( + p_mv_.board, + init_move=p_mv_, + depth=depth - 1, + alpha=alpha, + beta=beta, + is_white=is_white, + ) ###print(score, p_mv.start.x, p_mv.start.y, p_mv.end.x, p_mv.end.y) best_move = [best_move, p_mv][np.argmin([best_score, score])] best_score = np.min([best_score, score]) @@ -264,7 +291,10 @@ def random_move(self, board): for i in np.random.permutation(8): for j in np.random.permutation(8): if board.get_cell(i, j).get_piece() is not None: - if board.get_cell(i, j).get_piece().is_white() == self.is_white_side(): + if ( + board.get_cell(i, j).get_piece().is_white() + == self.is_white_side() + ): selected_piece = board.get_cell(i, j).get_piece() ###print('AI Selected Piece', selected_piece) possible_moves = selected_piece.get_potential_moves(i, j) @@ -275,8 +305,12 @@ def random_move(self, board): ###print('Verifying Moves,', len(possible_moves), 'Moves Possibles') while not verified_move and index < len(random_move): selected_move = possible_moves[random_move[index]] - selected_move = move.Move(self, board, board.get_cell(i, j), - board.get_cell(selected_move[0], selected_move[1])) + selected_move = move.Move( + self, + board, + board.get_cell(i, j), + board.get_cell(selected_move[0], selected_move[1]), + ) verified_move = selected_move.is_possible_move() index += 1 @@ -303,16 +337,16 @@ def time_to_play(self, board, depth=3): def _score_board(self, board): score = 0 - for piece_type in board.all_material[self.color]['alive'].keys(): - for piece in board.all_material[self.color]['alive'][piece_type]: + for piece_type in board.all_material[self.color]["alive"].keys(): + for piece in board.all_material[self.color]["alive"][piece_type]: score += self.piece_weights[piece_type] ###print(piece_type, piece.x, piece.y) score += self.piece_positions_weights[piece_type][piece.x][piece.y] adv_color = "white" if self.color == "black" else "black" - for piece_type in board.all_material[adv_color]['alive'].keys(): - for piece in board.all_material[adv_color]['alive'][piece_type]: + for piece_type in board.all_material[adv_color]["alive"].keys(): + for piece in board.all_material[adv_color]["alive"][piece_type]: score -= self.piece_weights[piece_type] score -= self.piece_positions_weights[piece_type][piece.x][piece.y] return score diff --git a/python/player/my_player.py b/python/player/my_player.py index 675d9d1..6eb582c 100644 --- a/python/player/my_player.py +++ b/python/player/my_player.py @@ -15,7 +15,7 @@ def __init__(self, max_memory=200): def remember(self, m): self.memory.append(m) - self.memory = self.memory[max(len(self.memory)- self.max_memory, 0):] + self.memory = self.memory[max(len(self.memory) - self.max_memory, 0) :] def random_access(self): rn = np.random.randint(0, max(len(self.memory), 1)) @@ -23,7 +23,6 @@ def random_access(self): class MyPlayer(Player): - def __init__(self, path_to_model="", epsilon_explorer=0.15, *args, **kwargs): super().__init__(*args, **kwargs) self.color = "white" if self.white_side else "black" @@ -45,11 +44,11 @@ def __init__(self, path_to_model="", epsilon_explorer=0.15, *args, **kwargs): if not os.path.exists(path_to_model): self.model = tf.keras.Model(inputs=in_, outputs=out) - self.model.compile(loss='mse', optimizer="Adam") + self.model.compile(loss="mse", optimizer="Adam") else: print("Loading Model") self.model = tf.keras.models.load_model(path_to_model) - self.model.compile(loss='mse', optimizer="Adam") + self.model.compile(loss="mse", optimizer="Adam") print("Model Loaded") print(self.model.summary()) self.memory = Memory() @@ -63,10 +62,11 @@ def _score_board(self, board, white_side=None): if self.white_side != white_side: one_hot_encode_board = np.flip(one_hot_encode_board) - score = self.model.predict(np.expand_dims(one_hot_encode_board, 0).astype('float32')) + score = self.model.predict( + np.expand_dims(one_hot_encode_board, 0).astype("float32") + ) return score[0][0] - def _get_possible_moves(self, board, is_white=None): if is_white is None: is_white = self.white_side() @@ -77,16 +77,19 @@ def _get_possible_moves(self, board, is_white=None): for piece in board.all_material[color]["alive"][type_piece]: piece_available_moves = piece.get_potential_moves(piece.x, piece.y) for mv in piece_available_moves: - selected_move = move.Move(self, board, board.get_cell(piece.x, piece.y), - board.get_cell(mv[0], mv[1])) + selected_move = move.Move( + self, + board, + board.get_cell(piece.x, piece.y), + board.get_cell(mv[0], mv[1]), + ) if selected_move.is_possible_move(): possible_moves.append(selected_move) return possible_moves def _select_move_from_score(self, moves, train=True): - if train: - do_random_move = (np.random.randint(100) <= self.epsilon_explorer * 100) + do_random_move = np.random.randint(100) <= self.epsilon_explorer * 100 if do_random_move: print("RANDOM MOVE SELECTED") index_random_move = np.random.randint(len(moves)) @@ -119,18 +122,18 @@ def _base_scoring(self, board, game_is_won="", white_side=None): "bishop": 30, "rook": 50, "queen": 90, - "king": 900 + "king": 900, } piece_positions_weights = { "pawn": [ - [0.]*8, - [5.0]*8, + [0.0] * 8, + [5.0] * 8, [1.0, 1.0, 2.0, 3.0, 3.0, 2.0, 1.0, 1.0], [0.5, 0.5, 1.0, 2.5, 2.5, 1.0, 0.5, 0.5], [0.0, 0.0, 0.0, 2.0, 2.0, 0.0, 0.0, 0.0], [0.5, -0.5, -1.0, 0.0, 0.0, -1.0, -0.5, 0.5], [0.5, 1.0, 1.0, -2.0, -2.0, 1.0, 1.0, 0.5], - [0.]*8 + [0.0] * 8, ], "bishop": [ [-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0], @@ -140,7 +143,7 @@ def _base_scoring(self, board, game_is_won="", white_side=None): [-1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, -1.0], [-1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0], [-1.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, -1.0], - [-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0] + [-2.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -2.0], ], "knight": [ [-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0], @@ -150,7 +153,7 @@ def _base_scoring(self, board, game_is_won="", white_side=None): [-3.0, 0.0, 1.5, 2.0, 2.0, 1.5, 0.0, -3.0], [-3.0, 0.5, 1.0, 1.5, 1.5, 1.0, 0.5, -3.0], [-4.0, -2.0, 0.0, 0.5, 0.5, 0.0, -2.0, -4.0], - [-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0] + [-5.0, -4.0, -3.0, -3.0, -3.0, -3.0, -4.0, -5.0], ], "rook": [ [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], @@ -160,7 +163,7 @@ def _base_scoring(self, board, game_is_won="", white_side=None): [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5], [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5], [-0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5], - [0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0] + [0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0], ], "queen": [ [-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0], @@ -170,7 +173,7 @@ def _base_scoring(self, board, game_is_won="", white_side=None): [0.0, 0.0, 0.5, 0.5, 0.5, 0.5, 0.0, 0.0], [-1.0, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -1.0], [-1.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0], - [-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0] + [-2.0, -1.0, -1.0, -0.5, -0.5, -1.0, -1.0, -2.0], ], "king": [ [-3.0, -4.0, -4.0, -5.0, -5.0, -4.0, -4.0, -3.0], @@ -180,18 +183,18 @@ def _base_scoring(self, board, game_is_won="", white_side=None): [-2.0, -3.0, -3.0, -4.0, -4.0, -3.0, -3.0, -2.0], [-1.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -1.0], [2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 2.0], - [2.0, 3.0, 1.0, 0.0, 0.0, 1.0, 3.0, 2.0] - ] + [2.0, 3.0, 1.0, 0.0, 0.0, 1.0, 3.0, 2.0], + ], } score = 0 - for piece_type in board.all_material["white"]['alive'].keys(): - for piece in board.all_material["white"]['alive'][piece_type]: + for piece_type in board.all_material["white"]["alive"].keys(): + for piece in board.all_material["white"]["alive"][piece_type]: score += piece_weights[piece_type] score += np.flip(piece_positions_weights[piece_type])[piece.x][piece.y] - for piece_type in board.all_material["black"]['alive'].keys(): - for piece in board.all_material["black"]['alive'][piece_type]: + for piece_type in board.all_material["black"]["alive"].keys(): + for piece in board.all_material["black"]["alive"][piece_type]: score -= piece_weights[piece_type] score -= piece_positions_weights[piece_type][piece.x][piece.y] if not white_side: @@ -220,9 +223,8 @@ def _reinforce(self, board, best_next_score, batch_size=12, white_side=None): self.model.train_on_batch(input_states, target_q) self.model.save(self.path_to_model) - def __str__(self): - return 'MyAIPlayer' + return "MyAIPlayer" def time_to_play(self, board, white_side=None): if white_side is None: diff --git a/python/player/player.py b/python/player/player.py index c037588..c5f7542 100644 --- a/python/player/player.py +++ b/python/player/player.py @@ -9,7 +9,7 @@ def __init__(self, white_side): self.random_number = np.random.randint(0, 1000, 1) def __str__(self): - return 'NormalPlayer%i' % self.random_number + return "NormalPlayer%i" % self.random_number def is_white_side(self): return self.white_side @@ -19,32 +19,39 @@ def time_to_play(self, board): class AIRandomPlayer(Player): - def __str__(self): - return 'AIRandomPlayer' + return "AIRandomPlayer" def time_to_play(self, board): - for i in np.random.permutation(8): for j in np.random.permutation(8): if board.get_cell(i, j).get_piece() is not None: - if board.get_cell(i, j).get_piece().is_white() == self.is_white_side(): + if ( + board.get_cell(i, j).get_piece().is_white() + == self.is_white_side() + ): selected_piece = board.get_cell(i, j).get_piece() - print('AI Selected Piece', selected_piece) + print("AI Selected Piece", selected_piece) possible_moves = selected_piece.get_potential_moves(i, j) verified_move = False random_move = np.random.permutation(len(possible_moves)) index = 0 - print('Verifying Moves,', len(possible_moves), 'Moves Possibles') + print( + "Verifying Moves,", len(possible_moves), "Moves Possibles" + ) while not verified_move and index < len(random_move): selected_move = possible_moves[random_move[index]] - selected_move = Move(self, board, board.get_cell(i, j), - board.get_cell(selected_move[0], selected_move[1])) + selected_move = Move( + self, + board, + board.get_cell(i, j), + board.get_cell(selected_move[0], selected_move[1]), + ) verified_move = selected_move.is_possible_move() index += 1 if verified_move: - print('Move is verified, ') + print("Move is verified, ") return selected_move - print('No moved found, aborting...') + print("No moved found, aborting...") diff --git a/python/utils/images_creation.py b/python/utils/images_creation.py index 2850bda..8d33854 100644 --- a/python/utils/images_creation.py +++ b/python/utils/images_creation.py @@ -5,17 +5,17 @@ from PIL import Image -black_pawn = np.array(Image.open('own_illustrations/queen_grey.png')) +black_pawn = np.array(Image.open("own_illustrations/queen_grey.png")) -black_back = (np.array([0.4, 0.4, 0.8]) * 255).astype('uint8') -white_back = (np.array([0.4, 0.8, 0.4]) * 255).astype('uint8') +black_back = (np.array([0.4, 0.4, 0.8]) * 255).astype("uint8") +white_back = (np.array([0.4, 0.8, 0.4]) * 255).astype("uint8") changed = [] for i in range(3): channel = np.copy(black_pawn[:, :, i]) - channel[black_pawn[:, :, i]<137] = 100 * i - channel[black_pawn[:, :, i]==0] = 200 / (i+1) - channel[black_pawn[:, :, i]>137] = 100 * i - 50 + channel[black_pawn[:, :, i] < 137] = 100 * i + channel[black_pawn[:, :, i] == 0] = 200 / (i + 1) + channel[black_pawn[:, :, i] > 137] = 100 * i - 50 changed.append(channel) plt.figure() diff --git a/python/utils/profile_game.py b/python/utils/profile_game.py index 41121f7..26223aa 100644 --- a/python/utils/profile_game.py +++ b/python/utils/profile_game.py @@ -4,9 +4,10 @@ sys.path.append("../") import engine.engine as engine -#import move -#import time -#import ai_player + +# import move +# import time +# import ai_player game = engine.Game(automatic_draw=False, ai=True) print(game.board.one_hot_encode()) @@ -25,4 +26,3 @@ game_is_on = game.move(ai_move, game.player2) score = my_player._score_board(game.board) print(my_player.model.summary()) - diff --git a/python/utils/training_ai.py b/python/utils/training_ai.py index 1feacd9..e492ac5 100644 --- a/python/utils/training_ai.py +++ b/python/utils/training_ai.py @@ -23,4 +23,3 @@ if not game_is_on[0]: print(game_is_on) break - diff --git a/run_app.py b/run_app.py index 9bd80fb..5557084 100644 --- a/run_app.py +++ b/run_app.py @@ -4,6 +4,5 @@ from interface.interface import MyApp -if __name__ == '__main__': +if __name__ == "__main__": MyApp(play_with_ai=True).run() - diff --git a/tests/engine_test.py b/tests/engine_test.py index c74d790..d2acf79 100644 --- a/tests/engine_test.py +++ b/tests/engine_test.py @@ -1,44 +1,47 @@ import sys + sys.path.append("../python") import engine.engine as engine import importlib import engine.move as move import time + importlib.reload(engine) import player.ai_player as ai_player + def test_working_castling(): game = engine.Game() game.move_from_coordinates(game.player1, 1, 4, 3, 4) - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) game.move_from_coordinates(game.player2, 6, 4, 4, 4) - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) game.move_from_coordinates(game.player1, 0, 5, 3, 2) - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) game.move_from_coordinates(game.player2, 7, 3, 4, 6) - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) game.move_from_coordinates(game.player1, 0, 6, 2, 5) - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) game.move_from_coordinates(game.player2, 7, 1, 5, 2) - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) # small castling move game.move_from_coordinates(game.player1, 0, 4, 0, 6) - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) game.move_from_coordinates(game.player2, 6, 3, 5, 3) - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) game.move_from_coordinates(game.player1, 0, 1, 2, 2) - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) game.move_from_coordinates(game.player2, 7, 2, 6, 3) - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) game.move_from_coordinates(game.player1, 0, 3, 1, 4) - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) # big castling move - print('big castling') - print("///", game.board.all_material['black']['alive']['king'][0].has_moved) + print("big castling") + print("///", game.board.all_material["black"]["alive"]["king"][0].has_moved) game.move_from_coordinates(game.player2, 7, 4, 7, 2) game.draw_board() @@ -99,6 +102,7 @@ def test_pawn_transformation(): game.move_from_coordinates(game.player1, 1, 5, 2, 5) game.move_from_coordinates(game.player2, 1, 4, 0, 4) + def check_unchecking(): game = engine.Game() game.move_from_coordinates(game.player1, 1, 4, 3, 4) @@ -106,6 +110,7 @@ def check_unchecking(): game.move_from_coordinates(game.player1, 0, 3, 4, 7) game.move_from_coordinates(game.player2, 6, 6, 5, 6) + def test_blocked_double_pawn(): game = engine.Game() game.move_from_coordinates(game.player1, 1, 4, 3, 4) @@ -115,6 +120,7 @@ def test_blocked_double_pawn(): game.move_from_coordinates(game.player1, 4, 4, 5, 4) game.move_from_coordinates(game.player2, 6, 4, 4, 4) + def test_king_taking_queen(): game = engine.Game() game.move_from_coordinates(game.player1, 1, 4, 3, 4) @@ -130,6 +136,7 @@ def test_king_taking_queen(): game.move_from_coordinates(game.player1, 7, 6, 7, 5) game.move_from_coordinates(game.player2, 7, 4, 7, 5) + def specific_test(): game = engine.Game() game.move_from_coordinates(game.player1, 1, 4, 3, 4) @@ -169,12 +176,18 @@ def possible_moves(): print(piece_available_moves) for mv in piece_available_moves: print(mv) - selected_move = move.Move(game.player1, game.board, game.board.get_cell(piece.x, piece.y), - game.board.get_cell(mv[0], mv[1])) + selected_move = move.Move( + game.player1, + game.board, + game.board.get_cell(piece.x, piece.y), + game.board.get_cell(mv[0], mv[1]), + ) if selected_move.is_possible_move(): print("move ok") else: print("move not ok") + + def test_player(): player = ai_player.EasyAIPlayer(False) game = engine.Game() @@ -189,9 +202,7 @@ def test_player(): print(score) - - -if __name__ == '__main__': +if __name__ == "__main__": check_unchecking() test_working_castling() test_failing_castling() @@ -204,7 +215,8 @@ def test_player(): specific_test() possible_moves() test_player() - print('Tests finished') + print("Tests finished") import sys + print(sys.executable) From e4f4c8c5309e37b6f81a122df5b5b9d3fc4f25fc Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 12:27:39 +0200 Subject: [PATCH 05/27] ADD: better test CI organization --- .github/workflows/ci.yaml | 40 +++++++++++++++++++ tests/{engine_test.py => prev_engine_test.py} | 1 + tests/unit_test/engine_test.py | 17 ++++++++ 3 files changed, 58 insertions(+) create mode 100644 .github/workflows/ci.yaml rename tests/{engine_test.py => prev_engine_test.py} (99%) create mode 100644 tests/unit_test/engine_test.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..81235cf --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,40 @@ +name: CI + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + pull_request: + branches: [master] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + build: + strategy: + matrix: + python-version: [3.8.6] + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Switch to Current Branch + run: git checkout ${{ env.BRANCH }} + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + cd key_generator/source + pip install -r requirements-dev.txt + pip install -e . + + - name: Run unit tests + run: python -m pytest --import-mode=append tests/ diff --git a/tests/engine_test.py b/tests/prev_engine_test.py similarity index 99% rename from tests/engine_test.py rename to tests/prev_engine_test.py index d2acf79..e641acd 100644 --- a/tests/engine_test.py +++ b/tests/prev_engine_test.py @@ -1,6 +1,7 @@ import sys sys.path.append("../python") +sys.path.append("python") import engine.engine as engine import importlib diff --git a/tests/unit_test/engine_test.py b/tests/unit_test/engine_test.py new file mode 100644 index 0000000..4ad5803 --- /dev/null +++ b/tests/unit_test/engine_test.py @@ -0,0 +1,17 @@ +import sys + +sys.path.append("../../python") +sys.path.append("python") + +import engine.engine as engine + +def test_blocked_moves(): + """ + Test that a blocked move does not happen. + """ + print("ok") + game = engine.Game(automatic_draw=False) + game.move_from_coordinates(game.player1, 1, 4, 3, 4) + _, winner = game.move_from_coordinates(game.player2, 7, 0, 5, 0) + print("win", winner) + assert winner == 0 From c7b3110e08553b489944c2c273af919eca162197 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 10:28:18 +0000 Subject: [PATCH 06/27] :art: Format Python code with psf/black --- tests/unit_test/engine_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit_test/engine_test.py b/tests/unit_test/engine_test.py index 4ad5803..87986c2 100644 --- a/tests/unit_test/engine_test.py +++ b/tests/unit_test/engine_test.py @@ -5,6 +5,7 @@ import engine.engine as engine + def test_blocked_moves(): """ Test that a blocked move does not happen. From c54023faf77064a60f30eb93148cec785e34ddac Mon Sep 17 00:00:00 2001 From: Vincent Auriau Date: Sat, 21 Oct 2023 12:46:36 +0200 Subject: [PATCH 07/27] Update ci.yaml --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 81235cf..939ee14 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,7 +11,7 @@ jobs: build: strategy: matrix: - python-version: [3.8.6] + python-version: [3.9.x] runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job From e43e53fef44dbf8aec48bd7985f169135a3dc50b Mon Sep 17 00:00:00 2001 From: Vincent Auriau Date: Sat, 21 Oct 2023 12:50:36 +0200 Subject: [PATCH 08/27] Update ci.yaml --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 939ee14..ad0436f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -32,8 +32,8 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - cd key_generator/source - pip install -r requirements-dev.txt + cd envs + pip install -r environment.txt pip install -e . - name: Run unit tests From b49b4ace5271d27f4280fadc23730b4bb0a43df3 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 13:33:31 +0200 Subject: [PATCH 09/27] ADD: dev specific env --- .github/workflows/ci.yaml | 2 +- envs/requirements-dev.txt | 0 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 envs/requirements-dev.txt diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ad0436f..4dce88c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -33,7 +33,7 @@ jobs: run: | python -m pip install --upgrade pip cd envs - pip install -r environment.txt + pip install -r requirements-dev.txt pip install -e . - name: Run unit tests diff --git a/envs/requirements-dev.txt b/envs/requirements-dev.txt new file mode 100644 index 0000000..e69de29 From dba0f424386d6a015f234b80f45a69ab7248f497 Mon Sep 17 00:00:00 2001 From: Vincent Auriau Date: Sat, 21 Oct 2023 13:37:25 +0200 Subject: [PATCH 10/27] Update ci.yaml --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4dce88c..1cd6f0d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -32,7 +32,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - cd envs + cd envs/ pip install -r requirements-dev.txt pip install -e . From 69c899fdc353c499ab4a32a59b9edd733506f7a4 Mon Sep 17 00:00:00 2001 From: Vincent Auriau Date: Sat, 21 Oct 2023 13:41:24 +0200 Subject: [PATCH 11/27] Create pyproject.toml --- pyproject.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f492d70 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "pyalapin" +version = "0.0.1" +authors = [ + { name = "Vincent Auriau"}, +] +description = "Custom Chess Engine" +readme = "README.md" +requires-python = ">=3.8" From 112b3042d203a942d697008a17acac6707402370 Mon Sep 17 00:00:00 2001 From: Vincent Auriau Date: Sat, 21 Oct 2023 13:44:30 +0200 Subject: [PATCH 12/27] Update ci.yaml --- .github/workflows/ci.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1cd6f0d..542dfda 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -32,8 +32,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - cd envs/ - pip install -r requirements-dev.txt + pip install -r envs.requirements-dev.txt pip install -e . - name: Run unit tests From 2da7ef48e18e138afa747f2d1557cb50b299d1d6 Mon Sep 17 00:00:00 2001 From: Vincent Auriau Date: Sat, 21 Oct 2023 13:44:59 +0200 Subject: [PATCH 13/27] Update ci.yaml --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 542dfda..a978a8f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -32,7 +32,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r envs.requirements-dev.txt + pip install -r envs/requirements-dev.txt pip install -e . - name: Run unit tests From 3bd822bdab957b7544ae72cfd2572a7f67facf59 Mon Sep 17 00:00:00 2001 From: Vincent Auriau Date: Sat, 21 Oct 2023 13:47:39 +0200 Subject: [PATCH 14/27] Update pyproject.toml --- pyproject.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index f492d70..8af7450 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,3 +10,11 @@ authors = [ description = "Custom Chess Engine" readme = "README.md" requires-python = ">=3.8" + +[tool.setuptools] +packages = [ + "lib", + "config", + "tests", + "python" +] From 7175eca562620bcd56d5cd68040bf232cec80ff8 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 13:48:28 +0200 Subject: [PATCH 15/27] FIX: missing dev packages --- envs/requirements-dev.txt | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/envs/requirements-dev.txt b/envs/requirements-dev.txt index e69de29..50db694 100644 --- a/envs/requirements-dev.txt +++ b/envs/requirements-dev.txt @@ -0,0 +1,50 @@ +absl-py==1.0.0 +astunparse==1.6.3 +cachetools==5.0.0 +certifi==2021.10.8 +charset-normalizer==2.0.12 +cycler==0.11.0 +docopt==0.6.2 +docutils==0.18.1 +flatbuffers==2.0 +fonttools==4.33.3 +gast==0.5.3 +grpcio==1.46.1 +h5py==3.6.0 +idna==3.3 +importlib-metadata==4.11.3 +kiwisolver==1.4.2 +libclang==14.0.1 +Markdown==3.3.7 +matplotlib==3.5.1 +numpy==1.22.3 +oauthlib==3.2.0 +opt-einsum==3.3.0 +packaging==21.3 +palanteer==0.6 +panda==0.3.1 +pipreqs==0.4.11 +protobuf==3.20.1 +pyasn1==0.4.8 +pyasn1-modules==0.2.8 +pycallgraph2==1.1.3 +Pygments==2.12.0 +pyinstrument==4.1.1 +pyparsing==3.0.8 +pypiwin32==223 +python-dateutil==2.8.2 +pytz==2022.7 +pywin32==303 +requests==2.27.1 +requests-oauthlib==1.3.1 +rsa==4.8 +scipy==1.10.0 +six==1.16.0 +termcolor==1.1.0 +threadpoolctl==3.1.0 +typing_extensions==4.2.0 +urllib3==1.26.9 +Werkzeug==2.1.2 +wrapt==1.14.1 +yarg==0.1.9 +zipp==3.8.0 From c54d4bb7624901f5972ea103b93e18fd5674358c Mon Sep 17 00:00:00 2001 From: Vincent Auriau Date: Sat, 21 Oct 2023 13:52:17 +0200 Subject: [PATCH 16/27] Update requirements-dev.txt --- envs/requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/envs/requirements-dev.txt b/envs/requirements-dev.txt index 50db694..b54a870 100644 --- a/envs/requirements-dev.txt +++ b/envs/requirements-dev.txt @@ -34,7 +34,7 @@ pyparsing==3.0.8 pypiwin32==223 python-dateutil==2.8.2 pytz==2022.7 -pywin32==303 +pywin32==306 requests==2.27.1 requests-oauthlib==1.3.1 rsa==4.8 From 2460835d7e0549e9f929f5fac41c90061a4b2200 Mon Sep 17 00:00:00 2001 From: Vincent Auriau Date: Sat, 21 Oct 2023 13:55:23 +0200 Subject: [PATCH 17/27] Update requirements-dev.txt --- envs/requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/envs/requirements-dev.txt b/envs/requirements-dev.txt index b54a870..46a7f4d 100644 --- a/envs/requirements-dev.txt +++ b/envs/requirements-dev.txt @@ -34,7 +34,7 @@ pyparsing==3.0.8 pypiwin32==223 python-dateutil==2.8.2 pytz==2022.7 -pywin32==306 +pywin32==305 requests==2.27.1 requests-oauthlib==1.3.1 rsa==4.8 From 99e3d1113543c39bd709301cfe713a6de1f6d6b2 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 14:12:48 +0200 Subject: [PATCH 18/27] edit CI CI now runs on python 3.9.2 --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a978a8f..924c233 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,7 +11,7 @@ jobs: build: strategy: matrix: - python-version: [3.9.x] + python-version: [3.9.2] runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job From 578b59e35f4db3b5cfdc74b07875c04e31189171 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 14:21:17 +0200 Subject: [PATCH 19/27] removed pywin32 version in requirements --- .github/workflows/ci.yaml | 2 +- envs/requirements-dev.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 924c233..a978a8f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,7 +11,7 @@ jobs: build: strategy: matrix: - python-version: [3.9.2] + python-version: [3.9.x] runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job diff --git a/envs/requirements-dev.txt b/envs/requirements-dev.txt index 46a7f4d..76a68df 100644 --- a/envs/requirements-dev.txt +++ b/envs/requirements-dev.txt @@ -34,7 +34,7 @@ pyparsing==3.0.8 pypiwin32==223 python-dateutil==2.8.2 pytz==2022.7 -pywin32==305 +pywin32 requests==2.27.1 requests-oauthlib==1.3.1 rsa==4.8 From fae407b872f8c7cbd4f6c07b13e556b1632ad505 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 14:22:53 +0200 Subject: [PATCH 20/27] rm pywin32 from requirements --- envs/requirements-dev.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/envs/requirements-dev.txt b/envs/requirements-dev.txt index 76a68df..c8558c9 100644 --- a/envs/requirements-dev.txt +++ b/envs/requirements-dev.txt @@ -34,7 +34,6 @@ pyparsing==3.0.8 pypiwin32==223 python-dateutil==2.8.2 pytz==2022.7 -pywin32 requests==2.27.1 requests-oauthlib==1.3.1 rsa==4.8 From 426b4d23697d42e1c8a9f1c0c1d22c6656cc450e Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 14:26:12 +0200 Subject: [PATCH 21/27] rm pypiwin32 --- envs/requirements-dev.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/envs/requirements-dev.txt b/envs/requirements-dev.txt index c8558c9..6c1e85e 100644 --- a/envs/requirements-dev.txt +++ b/envs/requirements-dev.txt @@ -31,7 +31,6 @@ pycallgraph2==1.1.3 Pygments==2.12.0 pyinstrument==4.1.1 pyparsing==3.0.8 -pypiwin32==223 python-dateutil==2.8.2 pytz==2022.7 requests==2.27.1 From 4e8976a5e24235fe5f4d2c36b206f2061d185bfa Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 14:30:46 +0200 Subject: [PATCH 22/27] fix .toml --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8af7450..4a9b9ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,8 +13,6 @@ requires-python = ">=3.8" [tool.setuptools] packages = [ - "lib", - "config", "tests", "python" ] From 225aac7bfec58fb048c3bf6c5de54c13f5721ba8 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 14:35:35 +0200 Subject: [PATCH 23/27] add pytest to requirements --- envs/requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/envs/requirements-dev.txt b/envs/requirements-dev.txt index 6c1e85e..e81e124 100644 --- a/envs/requirements-dev.txt +++ b/envs/requirements-dev.txt @@ -31,6 +31,7 @@ pycallgraph2==1.1.3 Pygments==2.12.0 pyinstrument==4.1.1 pyparsing==3.0.8 +pytest python-dateutil==2.8.2 pytz==2022.7 requests==2.27.1 From 9c18493bbb0e73a040bef8aada65346bb4e15fd2 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sat, 21 Oct 2023 14:39:26 +0200 Subject: [PATCH 24/27] rm tensorflow import --- python/player/my_player.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/player/my_player.py b/python/player/my_player.py index 6eb582c..b77e6ae 100644 --- a/python/player/my_player.py +++ b/python/player/my_player.py @@ -1,7 +1,7 @@ import os import numpy as np -import tensorflow as tf +# import tensorflow as tf from player.player import Player import engine.material as material From acbc65bfa00fa504db95f93f681177281d0355cb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:55:05 +0200 Subject: [PATCH 25/27] :art: Format Python code with psf/black (#10) Co-authored-by: VincentAuriau --- python/player/my_player.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/player/my_player.py b/python/player/my_player.py index b77e6ae..7aafbe6 100644 --- a/python/player/my_player.py +++ b/python/player/my_player.py @@ -1,6 +1,7 @@ import os import numpy as np + # import tensorflow as tf from player.player import Player From 464fb6c833833e4e60186dd066908012425dcc9c Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sun, 22 Oct 2023 17:34:31 +0200 Subject: [PATCH 26/27] ENH: restructure of pawn promotion --- python/engine/engine.py | 24 ++++++++++++++---------- python/engine/material.py | 32 +++++++++++++++++++++++++++++++- python/engine/move.py | 21 ++++++++++++--------- 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/python/engine/engine.py b/python/engine/engine.py index e9b0fea..2115abd 100644 --- a/python/engine/engine.py +++ b/python/engine/engine.py @@ -575,7 +575,6 @@ def kill_piece_from_coordinates(self, coordinates): def transform_pawn(self, coordinates): pawn = self.get_cell(coordinates[0], coordinates[1]).get_piece() if not isinstance(pawn, material.Pawn): - ###print(pawn) raise ValueError("Transforming piece that is not a Pawn") else: color = "white" if pawn.is_white() else "black" @@ -585,6 +584,18 @@ def transform_pawn(self, coordinates): self.get_cell(pawn.x, pawn.y).set_piece(new_queen) self.all_material[color]["alive"]["queen"].append(new_queen) + def promote_pawn(self, coordinates, promote_into="Queen"): + pawn = self.get_cell(coordinates[0], coordinates[1]).get_piece() + if not isinstance(pawn, material.Pawn): + raise ValueError("Transforming piece that is not a Pawn") + else: + color = "white" if pawn.is_white() else "black" + self.all_material[color]["alive"][pawn.type].remove(pawn) + + new_piece = pawn.promote(promote_into=promote_into) + self.get_cell(pawn.x, pawn.y).set_piece(new_piece) + self.all_material[color]["alive"]["queen"].append(new_piece) + def draw(self, printing=True): whole_text = " | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |" # ###print(' | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |') @@ -644,11 +655,11 @@ def to_fen(self): def is_finished(self): return self.status != "ACTIVE" - def move_from_coordinates(self, player, start_x, start_y, end_x, end_y): + def move_from_coordinates(self, player, start_x, start_y, end_x, end_y, extras={}): start_cell = self.board.get_cell(start_x, start_y) end_cell = self.board.get_cell(end_x, end_y) - move = Move(player, self.board, start_cell, end_cell) + move = Move(player, self.board, start_cell, end_cell, extras=extras) return self.move(move, player) @@ -703,22 +714,17 @@ def move(self, move, player): # List of checks # To change if castling or en passant move if moved_piece is None: - ###print('There is no moved piece, move is aborted') return False, 0 assert moved_piece is not None if player != self.to_play_player: - ###print('The wrong player has played, move is aborted') - ###print(self.to_play_player, 'supposed to play and', player, 'has played') return False, 0 assert player == self.to_play_player allowed_move = move.is_possible_move() if not allowed_move: - ###print('Move method checking legality of move rejected it') return False, 0 elif moved_piece.is_white() != player.is_white_side(): - ###print("Engine detected player to move other side piece") return False, 0 else: @@ -743,8 +749,6 @@ def move(self, move, player): print("END OF THE GAME, WHITE HAS WON") return False, "white" - ###print('PLAYER TO PLAY:', self.to_play_player) - # Checking for PAT & MAT check_status, winner = self.update_status() diff --git a/python/engine/material.py b/python/engine/material.py index 5f3b8f4..807a9eb 100644 --- a/python/engine/material.py +++ b/python/engine/material.py @@ -376,7 +376,37 @@ def get_potential_moves(self, x, y): return possible_moves def promote(self, promote_into="Queen"): - raise NotImplementedError + """Method to promote a pawn to other material type. Only happens if the pawn reaches the other side of the board. + The player can choose which type of material he wants its pawn to be promoted into. + + Parameters + ---------- + promote_into: str + Type of material to promote the pawn into. + + Returns + ------- + Piece + New piece with right promotion and same coordinates of current pawn. + """ + # Should we verify color and coordinates of Pawn ? + if self.is_white(): + assert self.x == 7, "Pawn has not reached other side of the board" + else: + assert self.x == 0, "Pawn has not reached the other side of the board" + + if promote_into.lower() == "queen": + return Queen(white=self.is_white(), x=self.x, y=self.y) + elif promote_into.lower() == "rook": + rook = Rook(white=self.is_white(), x=self.x, y=self.y) + rook.has_moved = True + return rook + elif promote_into == "knight": + return Knight(white=self.is_white(), x=self.x, y=self.y) + elif promote_into == "bishop": + return Bishop(white=self.is_white(), x=self.x, y=self.y) + else: + raise ValueError(f"Cannot promote piece into, {promote_into}, piece unknown") def get_str(self): """Method to represent the piece as a string. diff --git a/python/engine/move.py b/python/engine/move.py index 2613ffb..1bcf377 100644 --- a/python/engine/move.py +++ b/python/engine/move.py @@ -5,11 +5,13 @@ class Move: - def __init__(self, player, board, start, end): + def __init__(self, player, board, start, end, extras={}): self.player = player self.board = board self.start = start self.end = end + self.extras = extras + self.moved_piece = start.get_piece() if self.moved_piece is None: ###print("Empty cell selected as start of a move") @@ -18,7 +20,6 @@ def __init__(self, player, board, start, end): self.is_castling = False self.complementary_castling = None self.en_passant = False - self.transform_pawn = False def deepcopy(self): copied_board = self.board.deepcopy() @@ -31,7 +32,6 @@ def deepcopy(self): copied_move.is_castling = self.is_castling copied_move.complementary_castling = self.complementary_castling copied_move.en_passant = self.en_passant - copied_move.transform_pawn = self.transform_pawn return copied_move def _set_moved_attribute(self): @@ -162,21 +162,24 @@ def _is_en_passant(self): else: return False - def _is_pawn_transformation(self): + def _is_pawn_promotion(self): if not isinstance(self.moved_piece, material.Pawn): return False else: if self.end.get_x() == 7 and self.moved_piece.is_white(): + self.promote_into = self.extras.get("promote_into", "queen") + print(self.extras) return True elif self.end.get_x() == 0 and not self.moved_piece.is_white(): + self.promote_into = self.extras.get("promote_into", "queen") return True else: return False - def _transform_pawn(self): - ###print("TRANSFORM PAWN 2", self.moved_piece, self.moved_piece.is_white()) + def _promote_pawn(self): coordinates = (self.end.get_x(), self.end.get_y()) - self.board.transform_pawn(coordinates) + print("promote into", self.promote_into) + self.board.promote_pawn(coordinates=coordinates, promote_into=self.promote_into) def move_pieces(self): """ @@ -198,8 +201,8 @@ def move_pieces(self): ###print("CASTLING DETECTED PPPPPPPPP") self._set_castling_done() - if self._is_pawn_transformation(): - self._transform_pawn() + if self._is_pawn_promotion(): + self._promote_pawn() self._set_moved_attribute() def is_possible_move(self): # REFONDRE From d222051cb169e93361529039d4ebbab5f06f3ce9 Mon Sep 17 00:00:00 2001 From: VincentAuriau Date: Sun, 22 Oct 2023 17:34:46 +0200 Subject: [PATCH 27/27] ADD: tests for pawn promotion --- tests/unit_test/engine_test.py | 38 ++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/tests/unit_test/engine_test.py b/tests/unit_test/engine_test.py index 87986c2..a806c0e 100644 --- a/tests/unit_test/engine_test.py +++ b/tests/unit_test/engine_test.py @@ -10,9 +10,43 @@ def test_blocked_moves(): """ Test that a blocked move does not happen. """ - print("ok") game = engine.Game(automatic_draw=False) game.move_from_coordinates(game.player1, 1, 4, 3, 4) _, winner = game.move_from_coordinates(game.player2, 7, 0, 5, 0) - print("win", winner) assert winner == 0 + +def test_promotion_to_rook(): + """ + Test that the promotion works well + """ + game = engine.Game(automatic_draw=False) + game.move_from_coordinates(game.player1, 1, 4, 3, 4) + game.move_from_coordinates(game.player2, 6, 5, 4, 5) + game.move_from_coordinates(game.player1, 3, 4, 4, 5) + game.move_from_coordinates(game.player2, 7, 6, 5, 7) + game.move_from_coordinates(game.player1, 4, 5, 5, 5) + game.move_from_coordinates(game.player2, 6, 0, 5, 0) + game.move_from_coordinates(game.player1, 5, 5, 6, 6) + game.move_from_coordinates(game.player2, 5, 0, 4, 0) + game.move_from_coordinates(game.player1, 6, 6, 7, 6, extras={"promote_into": "rook"}) + assert game.board.to_fen()[0] == "rnbqkbnr/pppp1ppp/8/8/P7/7N/1PPPP2P/RNBQKBrR" + + +def test_default_promotion(): + """ + Test that the promotion works well + """ + game = engine.Game(automatic_draw=False) + game.move_from_coordinates(game.player1, 1, 4, 3, 4) + game.move_from_coordinates(game.player2, 6, 5, 4, 5) + game.move_from_coordinates(game.player1, 3, 4, 4, 5) + game.move_from_coordinates(game.player2, 7, 6, 5, 7) + game.move_from_coordinates(game.player1, 4, 5, 5, 5) + game.move_from_coordinates(game.player2, 6, 0, 5, 0) + game.move_from_coordinates(game.player1, 5, 5, 6, 6) + game.move_from_coordinates(game.player2, 5, 0, 4, 0) + game.move_from_coordinates(game.player1, 6, 6, 7, 6) + assert game.board.to_fen()[0] == "rnbqkbnr/pppp1ppp/8/8/P7/7N/1PPPP2P/RNBQKBqR" + +if __name__ == "__main__": + pass \ No newline at end of file