def test_pawn_enpassant(): # Advance pawn by two spaces conducted_move_history = [] board = get_enpassant_board() board, move_history_element = conduct_move( board, Move(MoveType.NORMAL, [1, 5], [3, 5]), "black") conducted_move_history.append(move_history_element) # Assert white pawn can only do regular attack, one space advance AND enpassant en_passant_move = Move.en_passant([3, 6], [2, 5]) assert_contains( board[3][6].get_attack_set(board, [3, 6], conducted_move_history), [en_passant_move]) # Actually execute en passant move board, move_history_element = conduct_move(board, en_passant_move, "white") conducted_move_history.append(move_history_element) __ = BlankPiece() p2 = Pawn("black") wp = Pawn("white") # 0 1 2 3 4 5 6 7 expected_board = [ [__, __, __, __, __, __, __, __], # 0 [__, __, __, __, __, __, __, p2], # 1 [__, __, __, __, __, wp, __, __], # 2 [__, __, __, __, __, __, __, __], # 3 [__, __, __, __, __, __, __, __], # 4 [__, __, __, __, __, __, __, __], # 5 [__, __, __, __, __, __, __, __], # 6 [__, __, __, __, __, __, __, __] # 7 ] assert_row_contain_same_type_elements(expected_board[1], board[1]) assert_row_contain_same_type_elements(expected_board[2], board[2]) assert_row_contain_same_type_elements(expected_board[3], board[3])
def test_board_state_without_pawn_enpassant(): conducted_move_history = [] board = get_enpassant_board() board, move_history_element = conduct_move(board, Move(MoveType.NORMAL, [1, 5], [2, 5]), "black") conducted_move_history.append(move_history_element) # Assert white pawn can only do regular attack and one space advance assert_contains( board[3][6].get_attack_set(board, [3, 6], conducted_move_history), [ Move(MoveType.NORMAL, [3, 6], [2, 5]) ] ) assert_contains( board[3][6].get_move_set(board, [3, 6], conducted_move_history), [Move(MoveType.NORMAL, [3, 6], [2, 6])] ) # Advance the black pawn one more space board, move_history_element = conduct_move(board, Move(MoveType.NORMAL, [2, 5], [3, 5]), "black") conducted_move_history.append(move_history_element) # Assert white pawn can only do do a one space advance assert_length(board[3][6].get_attack_set(board, [3, 6], []), 0) assert_contains( board[3][6].get_move_set(board, [3, 6], []), [ Move(MoveType.NORMAL, [3, 6], [2, 6]) ] )
def make_move(self, player_color, selected_move): if player_color != self.player_turn: return False chosen_move = Move.create_from_dict(selected_move) update_move_history(self.game_board, self.potential_moveset_history, self.player_turn, self.conducted_move_history) self.game_board, move_history_element = conduct_move(self.game_board, chosen_move, self.player_turn) self.conducted_move_history.append(move_history_element) if not self.game_board: print("Illegal move not caught by game logic") if is_being_checked(self.game_board, opposite_col(self.player_turn)): if can_player_leave_check_state(self.game_board, opposite_col(self.player_turn), self.conducted_move_history): self.progress_state = ProgressState.CHECK else: print_board(self.game_board) self.progress_state = ProgressState.CHECKMATE else: self.progress_state = ProgressState.IN_PROGRESS self.player_turn = opposite_col(self.player_turn) if is_stalemate(self.game_board, self.player_turn, self.conducted_move_history): self.progress_state = ProgressState.DRAW_STALEMATE elif is_impossible_to_reach_checkmate(self.game_board): self.progress_state = ProgressState.DRAW_INSUFFICIENT_MATERIALS elif is_threefold_repetition_stalemate(self.potential_moveset_history): self.progress_state = self.progress_state.DRAW_THREEFOLD_REPETITION elif is_fifty_move_rule_draw(self.conducted_move_history): self.progress_state = self.progress_state.DRAW_FIFTY_MOVE_RULE print(self.player_turn + 's turn. Waiting for selection') return True
def test_conducting_col_h_castle_move(): board = get_castling_board() col_h_castle = Move(MoveType.CASTLING, [7, 4], [7, 6]) board, move_history_element = conduct_move(board, col_h_castle, "white") _ = BlankPiece() expected_row = [Rook("white"), _, _, _, _, Rook("white"), King("white"), _] actual_row = board[7] assert_row_contain_same_type_elements(expected_row, actual_row)
def test_fifty_move_rule(): # Test moving white King to cause a 50-move rule draw board, conducted_move_history = get_fifty_move_rule_board() board, move_history_element = conduct_move(board, Move(MoveType.NORMAL, [7, 3], [7, 4]), "white") conducted_move_history.append(move_history_element) assert_true("Should be 50-move rule draw", is_fifty_move_rule_draw(conducted_move_history)) # Black rook captures white pawn to prevent 50-move rule draw (via piece-capture code-path) board, conducted_move_history = get_fifty_move_rule_board() board, move_history_element = conduct_move(board, Move(MoveType.NORMAL, [3, 1], [6, 1]), "black") conducted_move_history.append(move_history_element) assert_false("Should NOT be draw", is_fifty_move_rule_draw(conducted_move_history)) # Advance a pawn to prevent 50-move rule draw (via Pawn-type movement code-path) board, conducted_move_history = get_fifty_move_rule_board() board, move_history_element = conduct_move(board, Move(MoveType.NORMAL, [6, 1], [5, 2]), "white") conducted_move_history.append(move_history_element) assert_false("Should NOT be draw", is_fifty_move_rule_draw(conducted_move_history))
def get_fifty_move_rule_board(): """ Helper function to run through the first 3 moves in a "four move rule" (use a smaller deque for sanity here -- 50 is a lot), """ # conducted_move_history = deque([], 4) __ = BlankPiece() wk = King("white") wp = Pawn("white") br = Rook("black") bk = King("black") # 0 1 2 3 4 5 6 7 board = [ [__, __, __, bk, __, __, __, __], # 0 [__, __, __, __, __, __, __, __], # 1 [__, __, __, __, __, __, __, __], # 2 [__, br, __, __, __, __, __, __], # 3 [__, __, __, __, __, __, __, __], # 4 [__, __, __, __, __, __, __, __], # 5 [__, wp, __, __, __, __, __, __], # 6 [__, __, __, __, wk, __, __, __] # 7 ] msg = "Should NOT be 50-move rule draw" board, move_history_element = conduct_move( board, Move(MoveType.NORMAL, [7, 4], [7, 1]), "white") conducted_move_history.append(move_history_element) assert_false(msg, is_fifty_move_rule_draw(conducted_move_history)) board, move_history_element = conduct_move( board, Move(MoveType.NORMAL, [7, 1], [7, 2]), "white") conducted_move_history.append(move_history_element) assert_false(msg, is_fifty_move_rule_draw(conducted_move_history)) board, move_history_element = conduct_move( board, Move(MoveType.NORMAL, [7, 2], [7, 3]), "white") conducted_move_history.append(move_history_element) assert_false(msg, is_fifty_move_rule_draw(conducted_move_history)) return board, conducted_move_history
def get_fifty_move_rule_board(): """ Helper function to run through the first 3 moves in a "four move rule" (use a smaller deque for sanity here -- 50 is a lot), """ # conducted_move_history = deque([], 4) __ = BlankPiece() wk = King("white") wp = Pawn("white") br = Rook("black") bk = King("black") # 0 1 2 3 4 5 6 7 board = [ [__, __, __, bk, __, __, __, __], # 0 [__, __, __, __, __, __, __, __], # 1 [__, __, __, __, __, __, __, __], # 2 [__, br, __, __, __, __, __, __], # 3 [__, __, __, __, __, __, __, __], # 4 [__, __, __, __, __, __, __, __], # 5 [__, wp, __, __, __, __, __, __], # 6 [__, __, __, __, wk, __, __, __] # 7 ] msg = "Should NOT be 50-move rule draw" board, move_history_element = conduct_move(board, Move(MoveType.NORMAL, [7, 4], [7, 1]), "white") conducted_move_history.append(move_history_element) assert_false(msg, is_fifty_move_rule_draw(conducted_move_history)) board, move_history_element = conduct_move(board, Move(MoveType.NORMAL, [7, 1], [7, 2]), "white") conducted_move_history.append(move_history_element) assert_false(msg, is_fifty_move_rule_draw(conducted_move_history)) board, move_history_element = conduct_move(board, Move(MoveType.NORMAL, [7, 2], [7, 3]), "white") conducted_move_history.append(move_history_element) assert_false(msg, is_fifty_move_rule_draw(conducted_move_history)) return board, conducted_move_history
def test_pawn_enpassant(): # Advance pawn by two spaces conducted_move_history = [] board = get_enpassant_board() board, move_history_element = conduct_move(board, Move(MoveType.NORMAL, [1, 5], [3, 5]), "black") conducted_move_history.append(move_history_element) # Assert white pawn can only do regular attack, one space advance AND enpassant en_passant_move = Move.en_passant([3, 6], [2, 5]) assert_contains( board[3][6].get_attack_set(board, [3, 6], conducted_move_history), [ en_passant_move ] ) # Actually execute en passant move board, move_history_element = conduct_move(board, en_passant_move, "white") conducted_move_history.append(move_history_element) __ = BlankPiece() p2 = Pawn("black") wp = Pawn("white") # 0 1 2 3 4 5 6 7 expected_board = [ [__, __, __, __, __, __, __, __], # 0 [__, __, __, __, __, __, __, p2], # 1 [__, __, __, __, __, wp, __, __], # 2 [__, __, __, __, __, __, __, __], # 3 [__, __, __, __, __, __, __, __], # 4 [__, __, __, __, __, __, __, __], # 5 [__, __, __, __, __, __, __, __], # 6 [__, __, __, __, __, __, __, __] # 7 ] assert_row_contain_same_type_elements(expected_board[1], board[1]) assert_row_contain_same_type_elements(expected_board[2], board[2]) assert_row_contain_same_type_elements(expected_board[3], board[3])
def test_board_state_without_pawn_enpassant(): conducted_move_history = [] board = get_enpassant_board() board, move_history_element = conduct_move( board, Move(MoveType.NORMAL, [1, 5], [2, 5]), "black") conducted_move_history.append(move_history_element) # Assert white pawn can only do regular attack and one space advance assert_contains( board[3][6].get_attack_set(board, [3, 6], conducted_move_history), [Move(MoveType.NORMAL, [3, 6], [2, 5])]) assert_contains( board[3][6].get_move_set(board, [3, 6], conducted_move_history), [Move(MoveType.NORMAL, [3, 6], [2, 6])]) # Advance the black pawn one more space board, move_history_element = conduct_move( board, Move(MoveType.NORMAL, [2, 5], [3, 5]), "black") conducted_move_history.append(move_history_element) # Assert white pawn can only do do a one space advance assert_length(board[3][6].get_attack_set(board, [3, 6], []), 0) assert_contains(board[3][6].get_move_set(board, [3, 6], []), [Move(MoveType.NORMAL, [3, 6], [2, 6])])
def test_fifty_move_rule(): # Test moving white King to cause a 50-move rule draw board, conducted_move_history = get_fifty_move_rule_board() board, move_history_element = conduct_move( board, Move(MoveType.NORMAL, [7, 3], [7, 4]), "white") conducted_move_history.append(move_history_element) assert_true("Should be 50-move rule draw", is_fifty_move_rule_draw(conducted_move_history)) # Black rook captures white pawn to prevent 50-move rule draw (via piece-capture code-path) board, conducted_move_history = get_fifty_move_rule_board() board, move_history_element = conduct_move( board, Move(MoveType.NORMAL, [3, 1], [6, 1]), "black") conducted_move_history.append(move_history_element) assert_false("Should NOT be draw", is_fifty_move_rule_draw(conducted_move_history)) # Advance a pawn to prevent 50-move rule draw (via Pawn-type movement code-path) board, conducted_move_history = get_fifty_move_rule_board() board, move_history_element = conduct_move( board, Move(MoveType.NORMAL, [6, 1], [5, 2]), "white") conducted_move_history.append(move_history_element) assert_false("Should NOT be draw", is_fifty_move_rule_draw(conducted_move_history))
def make_move(self, player_color, selected_move): if player_color != self.player_turn: return False chosen_move = Move.create_from_dict(selected_move) update_move_history(self.game_board, self.potential_moveset_history, self.player_turn, self.conducted_move_history) self.game_board, move_history_element = conduct_move( self.game_board, chosen_move, self.player_turn) self.conducted_move_history.append(move_history_element) if not self.game_board: print("Illegal move not caught by game logic") if is_being_checked(self.game_board, opposite_col(self.player_turn)): if can_player_leave_check_state(self.game_board, opposite_col(self.player_turn), self.conducted_move_history): self.progress_state = ProgressState.CHECK else: print_board(self.game_board) self.progress_state = ProgressState.CHECKMATE else: self.progress_state = ProgressState.IN_PROGRESS self.player_turn = opposite_col(self.player_turn) if is_stalemate(self.game_board, self.player_turn, self.conducted_move_history): self.progress_state = ProgressState.DRAW_STALEMATE elif is_impossible_to_reach_checkmate(self.game_board): self.progress_state = ProgressState.DRAW_INSUFFICIENT_MATERIALS elif is_threefold_repetition_stalemate(self.potential_moveset_history): self.progress_state = self.progress_state.DRAW_THREEFOLD_REPETITION elif is_fifty_move_rule_draw(self.conducted_move_history): self.progress_state = self.progress_state.DRAW_FIFTY_MOVE_RULE print(self.player_turn + 's turn. Waiting for selection') return True
def main(): player_turn = "white" game_board = create() # Maintain complete history of all potential game moves (for the three-fold repetition rule) potential_moveset_history = [] # The Wikipedia page for the 50-move rule suggests that for the purposes the rule, a move is a player's turn # followed by opponents turn. history_length = 50 * 2 # Maintain complete history of actually completed moves (for the 50-move rule, and en passant) conducted_move_history = deque([], history_length) while 1: print_board(game_board) print(player_turn + 's turn. Select piece') valid_selection = False while not valid_selection: coords = request_user_move(' Select piece to move. Example: e2 \n') if not coords: continue if selected_piece(game_board, coords).is_blank_piece: print('...Error invalid piece. Choose another piece\n') continue if selected_piece(game_board, coords).col != player_turn: print( '...Error selected opponents\' piece. Choose another piece\n' ) continue piece_total_move_set = selected_piece(game_board, coords).get_move_set( game_board, coords, conducted_move_history) piece_total_move_set += selected_piece(game_board, coords).get_attack_set( game_board, coords, conducted_move_history) piece_legal_move_set = filter_self_checking_moves( game_board, piece_total_move_set, player_turn, conducted_move_history) if len(piece_legal_move_set) == 0: print( '...Error no legal moves available. Choose another piece\n' ) continue valid_selection = True print( 'Selected: \'' + selected_piece(game_board, coords).type + '\'.', ) print('Possible moves: ', ) for move in piece_legal_move_set: print(move, ": ", py_to_a1_convert(move.end_coords)) print() # Choose end location: chosen_move = False while not chosen_move: user_end_coords = request_user_move( ' Select location to move piece to: ') if not user_end_coords: continue for move in piece_legal_move_set: if move.end_coords[0] == user_end_coords[ 0] and move.end_coords[1] == user_end_coords[1]: chosen_move = move if not chosen_move: continue if chosen_move.move_type == MoveType.PROMOTION: # If we chose one a promotion end coordinates, we need to find the exact promotion choice we desire chosen_move = request_promotion_type(piece_legal_move_set) if not chosen_move: print('Invalid move\n') continue update_move_history(game_board, potential_moveset_history, player_turn, conducted_move_history) game_board, move_history_element = conduct_move( game_board, chosen_move, player_turn) conducted_move_history.append(move_history_element) if not game_board: print("Illegal move not caught by game logic") if is_being_checked(game_board, opposite_col(player_turn)): if can_player_leave_check_state(game_board, opposite_col(player_turn), conducted_move_history): print('CHECK\n') else: print_board(game_board) print('CHECKMATE. ' + player_turn + ' wins!\n') sys.exit(0) player_turn = opposite_col(player_turn) if is_stalemate(game_board, player_turn, conducted_move_history): print('DRAW (STALEMATE)\n') sys.exit(0) if is_impossible_to_reach_checkmate(game_board): print('DRAW (INSUFFICIENT MATERIALS)\n') sys.exit(0) if is_threefold_repetition_stalemate(potential_moveset_history): print('DRAW (THREEFOLD REPETITION)\n') sys.exit(0) if is_fifty_move_rule_draw(conducted_move_history): print('DRAW (FIFTY-MOVE RULE)\n') sys.exit(0)