diff --git a/lab2/main.py b/lab2/main.py index 3dc9263b..1216e581 100644 --- a/lab2/main.py +++ b/lab2/main.py @@ -11,9 +11,9 @@ class Game: self.black_positions = self.initialize_black() print(self.white_positions) print(self.black_positions) - self.check_move_piece_capable(1, 0, 1, 0, True) def initialize_white(self): + """Initialize white pieces""" white_positions = [] for y_coordinate in range(3): for x_coordinate in range(self.board_size): @@ -26,6 +26,7 @@ class Game: return white_positions def initialize_black(self): + """Initialize black pieces""" black_positions = [] for y_coordinate in range(self.board_size - 3, self.board_size): for x_coordinate in range(self.board_size): @@ -37,122 +38,106 @@ class Game: black_positions.append((x_coordinate, y_coordinate, False)) return black_positions - def print_move_info(self, piece_x_coord, piece_y_coord, x_final, y_final): - print("(piece_x_coordinate, piece_y_coordinate): " + (piece_x_coord, piece_y_coord)) - print("(x_final_coordinate, y_final_coordinate): " + (x_final, y_final)) - - def check_move_out_of_bounds(self, piece_x_coord, piece_y_coord, x_final, y_final): - error = False - if x_final < 0: - print("Illegal move! final x coordinate can't be below 0!") - print_move_info(piece_x_coord, piece_y_coord, x_final, y_final) - error = True - if (x_final > self.board_size): - print(f"Illegal move! final x coordinate can't be above board size! board_size: ${self.board_size}") - print_move_info(piece_x_coord, piece_y_coord, x_final, y_final) - error = True - if y_final < 0: - print("Illegal move! final y coordinate can't be below 0!") - print_move_info(piece_x_coord, piece_y_coord, x_final, y_final) - error = True - if (y_final > self.board_size): - print(f"Illegal move! final y coordinate can't be above board size! board_size: ${self.size}") - print_move_info(piece_x_coord, piece_y_coord, x_final, y_final) - error = True - return error - - def check_spot_taken(self, piece_x_coord, piece_y_coord, x_final, y_final, player_color): - error = False - if player_color: - if (x_final, y_final) in self.white_positions or (x_final, y_final) in self.white_positions: - print("Illegal move! can't move into position taken by any other piece") - print_move_info(piece_x_coord, piece_y_coord, x_final, y_final) - error = True - return error - - def check_piece_exists(self, piece_x_coord, piece_y_coord, player_color): - error = False - if player_color: - if not (piece_x_coord, piece_y_coord) in self.white_positions: - print("Illegal move! There is no piece on those coordinates or piece does not belong to white player") - error = True - elif not (piece_x_coord, piece_y_coord) in self.black_positons: - print("Illegal move! There is no piece on those coordinates or piece does not belong to black player") - error = True - return error - - def check_move_diagonally(self, from, to, player_color, king): - if not king: - if player_color: - if to[0] == from[0] - 1 or to[0] == from[0] + 1: - if to[1] == from[1] + 1: - return False - elif x_final == piece_x_coord - 1 or x_final == piece_x_coord + 1: - if y_final == piece_y_coord - 1: - return False - if king: - if player_color: - if x_final == piece_x_coord - 1 or x_final == piece_x_coord + 1: - return False - elif x_final == piece_x_coord - 1 or x_final == piece_x_coord + 1: - return False - return True - - def check_piece_king(self, from, player_color): - if player_color == "white": - if (from[0], from[1], True) in self.white_positions: - return True - elif (from[0], from[1], True) in self.black_positions: + def check_move_out_of_bounds(self, from_, to): + """Check if the move destination is out of the bounds of the board""" + if to[0] < 0 or to[0] >= self.board_size: + print(f"Illegal move! Final x coordinate must be between 0 and {self.board_size}!") + if to[1] < 0 or to[1] >= self.board_size: + print(f"Illegal move! Final y coordinate must be between 0 and {self.board_size}!") return True return False - # https://stackoverflow.com/a/2191707 - def check_move_piece_capable(self, from, to, player_color): - error = True - king = self.check_piece_king(from, player_color) - error = self.check_move_diagonally(from, to, player_color, king) - return error + def check_piece_exists(self, coords, color): + """Check if a piece of given color exists at a given spot""" + if color == "white": + if (*coords, False) | (*coords, False) in self.white_positions: + return True + else: + if (*coords, False) | (*coords, False) in self.black_positons: + return True + return False - def check_capture(self, from, to, player_color): - capture = False - if player_color == "white": - if (to[0] - 1, to[1] - 1) in self.black_positions or (t[0] + 1, to[1] - 1) in self.black_positions - capture = True - elif (to[0], to[1] - 1) in self.white_positions or (to[0] + 1, t0[1] - 1) in self.white_positions - capture = True + def check_piece_king(self, coords, color): + """Check if a piece of in a given spot and of a given color is a king. Return false if no piece is found""" + if color == "white": + return (*coords, True) in self.white_positions + else: + return (*coords, True) in self.black_positions + + # https://stackoverflow.com/a/2191707 + def check_move_piece_capable(self, from_, to, color): + """Check if the move is exactly one square diagonally""" + if abs(to[0] - from_[0]) == 1: + if self.check_piece_king(from_, color): + return True + else: + if color == "white": + return to[1] == from_[1] + 1 + elif color == "black": + return to[1] == from_[1] - 1 + return False + + def check_capture(self, from_, to, color): + """Check if a piece was captured for a given move. Return captured piece coordinates or None""" + # captures can only happen if the player moved twice-diagonally + if(abs(to[0]-from_[0])!=2 or abs(to[1]-from_[1]!=2)): + return None + + middle = (abs(to[0]-from_[0])//2, abs(to[1]-from_[1])//2) + + if color == "white" and self.check_piece_exists(middle, "black"): + return middle + elif color == "black" and self.check_piece_exists(middle, "white"): + return middle + return None + + def check_move_legal(self, from_, to, color): + """Check if a move is legal. Return a boolean or coordinates of captured piece""" + if self.check_move_out_of_bounds(from_, to): + print("Illegal move! Final position is out of the bounds of the board") + return False + if not self.check_piece_exists(from_, color): + print("Illegal move! There is no piece on the starting position that belongs to the player") + return False + if self.check_piece_exists(to, "white") or check_piece_exists(to, "black"): + print("Illegal move! Cannot move to position taken by another piece") + return False + capture = self.check_capture(from_, to, color) + if capture == None: + return self.check_move_piece_capable(from_, to, color) return capture - - def check_move_legal(self, from, to, player_color): - if self.check_move_out_of_bounds(from, to): - return False - if self.check_piece_exists(from, to, player_color): - return False - if self.check_spot_taken(from, to, player_color): - return False - capture = self.check_capture(from, to, player_color) - if not capture: - return self.check_move_piece_capable(from, to, player_color): - - return True - def make_move(self, from, to, color): + def make_move(self, from_, to, color): + """Move a piece on the board and remove any captured pieces""" + move_legal = self.check_move_legal(from_, to, color) + if move_legal == False: + return False + capture = move_legal if type(move_legal) is tuple else None + king = self.check_piece_king(from_, color) + if(color == "white"): - self.white_pieces.remove(from) - self.white_pieces.append(to) + self.white_pieces.remove(*from_, king) + self.white_pieces.append(*to, king) + if(capture): + captured_king = self.check_piece_king(capture, "black") + self.black_pieces.remove(*move_legal, captured_king) + else: - self.black_pieces.remove(from) - self.black_pieces.append(to) - if not check_move_legal(piece_x_coord, piece_y_coord, x_final, y_final, player_color): - return False + self.black_pieces.remove(*from_, king) + self.black_pieces.append(*to, king) + if(capture): + captured_king = self.check_piece_king(capture, "white") + self.white_pieces.remove(*move_legal, captured_king) return True def print_board(self): + """Print the board in the console""" print(" ", end="") for x in range(self.board_size): - print(" " + chr(97 + x), end=" ") + print(f" {chr(ord('a')+x)} ", end="") print(" ") line = 0 for y in range(self.board_size*4): @@ -178,23 +163,92 @@ class Game: case 0: print("+---", end="") case 1 | 3: - print("|" + 3*bg, end="") + print(f"|{3*bg}", end="") case 2: - print("|" + bg + checker + bg, end="") + print(f"|{bg}{checker}{bg}", end="") if(line%4==0): print("+") else: - print("|", end=f"{y//4}\n" if line%4==2 else "\n") + print(f"|{y//4}" if line%4==2 else "|") line+=1 print(" ", end="") for x in range(self.board_size): print("+---", end="") print("+\n ", end="") for x in range(self.board_size): - print(" " + chr(97 + x), end=" ") + print(f" {chr(ord('a')+x)}", end=" ") print() # Ran first in the code + + def get_possible_moves(color): + # toDo: get possible moves for single piece, then iterate through all pieces + #if color == "white": + # for s + pass + + def alpha_beta(self, depth, alpha, beta, player): + if depth == 0: + return self.evaluate(), None + + if player == 'x': + max_eval = float('-inf') + best_move = None + + for move in state.get_possible_moves(player): + new_state = copy.deepcopy(state) + new_state.move(*move) + eval, _ = alpha_beta(new_state, depth-1, alpha, beta, 'o') + + if eval > max_eval: + max_eval = eval + best_move = move + + alpha = max(alpha, eval) + + if alpha >= beta: + break + + return max_eval, best_move + + else: + min_eval = float('inf') + best_move = None + + for move in state.get_possible_moves(player): + new_state = copy.deepcopy(state) + new_state.move(*move) + eval, _ = alpha_beta(new_state, depth-1, alpha, beta, 'x') + + if eval < min_eval: + min_eval = eval + best_move = move + + beta = min(beta, eval) + + if beta <= alpha: + break + + return min_eval, best_move + + def evaluate(self): + white_score = 0 + black_score = 0 + + for white_position in self.white_positions: + if white_position[2]: + white_score += 2 + else: + white_score += 2 + + for black_position in self.black_positions: + if black_position[2]: + black_score += 2 + else: + black_score += 2 + + return x_score - o_score + if __name__ == "__main__": game = Game(8) game.print_board() diff --git a/lab2/reportLink.txt b/lab2/reportLink.txt new file mode 100644 index 00000000..ec785e50 --- /dev/null +++ b/lab2/reportLink.txt @@ -0,0 +1 @@ +https://www.overleaf.com/8592159577skmysmsphtkr \ No newline at end of file