def test_en_passant_moves(self): """Test if conditions are met en passant moves are given in proper format""" black_pawn_position = Position(1, 3) white_pawn_position = Position(0, 3) black_pawn = Pawn(None, BLACK, black_pawn_position) white_pawn = Pawn(None, WHITE, white_pawn_position) test_board = { 'white_pieces': [white_pawn], 'black_pieces': [black_pawn] } board = set_up_test_board(test_board=test_board) self.assertEqual(len(black_pawn.en_passant_moves), 1) test_en_passant_move = { TYPE: EN_PASSANT, CAPTURE_PIECE: white_pawn } en_passant_move = black_pawn.en_passant_moves position_key = (0, 2) self.assertIn(position_key, en_passant_move.keys()) for key in test_en_passant_move.keys(): self.assertEqual(en_passant_move[position_key][key], test_en_passant_move[key] )
def test_add_position_success(self): """Test the __add__ and __radd__ methods for position class""" position = Position(1, 1) other_position = Position(2, 2) self.assertEqual(Position(3, 3), position + other_position) self.assertEqual(Position(2, 2), position + (1, 1)) self.assertEqual(Position(2, 2), (1, 1) + position)
def test_player_move_fails_not_in_progress(self): """Test exception is thrown if the game is over and a player tries to move""" game = Game() game._status = constants.CHECKMATE with pytest.raises(InvalidMoveError): game.player_move(Position(0, 1), Position(0, 2))
def test_iadd_position_success(self): """Test Position classes __iadd__ method""" position = Position(0, 0) position += (1, 1) self.assertEqual(Position(1, 1), position) position = Position(0, 0) position += Position(2, 2) self.assertEqual(Position(2, 2), position)
def test_castling_moves_both_sides(self): """Test king castling moves returns correct moves when castling is open on both sides""" king_side_rook = Rook(None, WHITE, Position(0, 0)) queen_side_rook = Rook(None, WHITE, Position(7, 0)) test_board = {'white_pieces': [king_side_rook, queen_side_rook]} board = set_up_test_board(new_game=False, test_board=test_board) white_king = board.get_piece(Position(4, 0)) self.assertEqual(len(white_king.castling_moves), 2)
def test_is_open(self): """Test is open returns true if square is open and false otherwise""" pawn = Pawn(None, WHITE, Position(1, 4)) test_board = { 'white_pieces': [pawn] } board = set_up_test_board(new_game=False, test_board=test_board) self.assertTrue(board.is_open(Position(1, 1))) self.assertFalse(board.is_open(Position(1, 4)))
def load_move(self, move): """ Load a move into a game """ # TODO # for now just call player_move move_from = Position(move.from_position_x, move.from_position_y) move_to = Position(move.to_position_x, move.to_position_y) colour = move.colour self.player_move(move_from, move_to, colour)
def test_white_pawn_moves_from_start(self): """Test pawn moves returns the correct moves when a pawn is in the starting position""" pawn = Pawn(None, WHITE, Position(1, 1)) test_board = { 'white_pieces': [pawn] } board = set_up_test_board(new_game=False, test_board=test_board) self.assertEqual(len(pawn.moves), 2) self.assertIn(Position(1, 2), pawn.moves) self.assertIn(Position(1, 3), pawn.moves)
def test_player_move_castle_success(self): """Test player move castling is successful when it is a valid move""" white_rook_position = Position(0, 0) white_rook = Rook(None, constants.WHITE, white_rook_position) test_board = {'white_pieces': [white_rook]} board = set_up_test_board(new_game=False, test_board=test_board) game = set_up_test_game(board) game.player_move(Position(4, 0), Position(2, 0)) self.assertEqual(white_rook.position, Position(3, 0)) self.assertTrue(isinstance(board.get_piece(Position(2, 0)), King))
def test_add_position_out_of_bounds(self): """Test if result of add is out of bounds exception is raised""" position = Position(0, 0) with pytest.raises(PositionOutOfBounds): result = position + (-1, 0) with pytest.raises(PositionOutOfBounds): result = position + Position(-1, 0) with pytest.raises(PositionOutOfBounds): result = position + (8, 0) with pytest.raises(PositionOutOfBounds): result = position + Position(8, 0)
def test_try_move_without_capture(self, board): rook_position = Position(4, 4) rook = Rook(board, constants.WHITE, rook_position) board.in_check.return_value = False game = Game() game._board = board self.assertTrue(game.try_move(rook, Position(5, 4))) self.assertEqual(rook.position, rook_position) board.in_check.return_value = True self.assertFalse(game.try_move(rook, Position(5, 4))) self.assertEqual(rook.position, rook_position)
def test_get_moves_no_special(self, board, try_move): invalid_position = Position(3, 3) def my_side_effect(*args): if args[1] == invalid_position: return False return True try_move.side_effect = my_side_effect game = Game() game._board = board rook = Rook(board, constants.WHITE, Position(4, 4)) bishop = Bishop(board, constants.WHITE, Position(1, 1)) knight = Knight(board, constants.WHITE, Position(7, 0)) mocked_board_moves = { rook: { constants.MOVES: [Position(5, 4), Position(3, 4)], constants.SPECIAL_MOVES: {} }, bishop: { constants.MOVES: [Position(2, 2), invalid_position], constants.SPECIAL_MOVES: {} }, knight: { constants.MOVES: [], constants.SPECIAL_MOVES: {} } } valid_moves = { rook: { constants.MOVES: [Position(5, 4), Position(3, 4)], constants.SPECIAL_MOVES: {} }, bishop: { constants.MOVES: [Position(2, 2)], constants.SPECIAL_MOVES: {} }, knight: { constants.MOVES: [], constants.SPECIAL_MOVES: {} } } board.get_moves.return_value = mocked_board_moves all_game_moves = game.get_moves(constants.WHITE) self.assertEqual(len(mocked_board_moves), len(all_game_moves)) for key in valid_moves.keys(): self.assertIn(key, all_game_moves) self.assertEqual(len(valid_moves[key]), len(all_game_moves[key])) for move in mocked_board_moves[key]: self.assertIn(move, all_game_moves[key]) self.assertEqual(game.status, constants.IN_PROGRESS)
def test_white_pawn_moves_one_enemy(self): """Test pawn moves returns correct moves with one enemy on a diagonal square""" pawn = Pawn(None, WHITE, Position(1, 1)) enemy = Bishop(None, BLACK, Position(2, 2)) test_board = { 'white_pieces': [pawn], 'black_pieces': [enemy] } board = set_up_test_board(new_game=False, test_board=test_board) self.assertEqual(len(pawn.moves), 3) self.assertIn(Position(2, 2), pawn.moves)
def test_castling_moves_empty_when_attacked(self): """Test castling moves is empty when an enemy is attacking a square in between king and rook""" black_knight = Knight(None, BLACK, Position(2, 2)) white_rook = Rook(None, WHITE, Position(0, 0)) white_rook2 = Rook(None, WHITE, Position(7, 0)) test_board = { 'white_pieces': [white_rook, white_rook2], 'black_pieces': [black_knight] } board = set_up_test_board(new_game=False, test_board=test_board) white_king = board.get_piece(Position(4, 0)) self.assertEqual(len(white_king.castling_moves), 1)
def test_en_passant_moves_empty(self): """Test en passant moves empty if piece in attacked position is not a pawn""" black_pawn_position = Position(1, 3) white_knight_position = Position(0, 3) black_pawn = Pawn(None, BLACK, black_pawn_position) white_knight = Knight(None, WHITE, white_knight_position) test_board = { 'white_pieces': [white_knight], 'black_pieces': [black_pawn] } board = set_up_test_board(test_board=test_board) self.assertEqual(0, len(black_pawn.en_passant_moves))
def test_player_move_fails_into_check(self): """Test trying to make a move into check fails""" white_rook_position = Position(4, 1) black_rook = Rook(None, constants.BLACK, Position(4, 5)) white_rook = Rook(None, constants.WHITE, white_rook_position) test_board = { 'white_pieces': [white_rook], 'black_pieces': [black_rook] } board = set_up_test_board(new_game=False, test_board=test_board) game = set_up_test_game(board) with pytest.raises(InvalidMoveError): game.player_move(white_rook_position, Position(2, 1))
def test_is_enemy_pawn(self): """Test is_enemy_pawn returns True if enemy pawn and False otherwise""" black_pawn_position = Position(1, 4) black_rook_position = Position(2, 4) black_pawn = Pawn(None, BLACK, black_pawn_position) black_rook = Rook(None, BLACK, black_rook_position) test_board = { 'black_pieces': [black_rook, black_pawn] } board = set_up_test_board(new_game=False, test_board=test_board) self.assertTrue(board.is_enemy_pawn(black_pawn_position, WHITE)) self.assertFalse(board.is_enemy_pawn(black_rook_position, WHITE))
def test_iadd_raises_out_of_bounds_error(self): """Test iadd raises error when result is out of the boards bounds""" position = Position(0, 0) with pytest.raises(PositionOutOfBounds): position += (8, 0) self.assertEqual(position, Position(0, 0)) with pytest.raises(PositionOutOfBounds): position += Position(8, 0) self.assertEqual(position, Position(0, 0)) with pytest.raises(PositionOutOfBounds): position += (-1, 0) self.assertEqual(position, Position(0, 0))
def test_try_move_with_capture(self, board): white_rook_position = Position(4, 4) white_rook = Rook(board, constants.WHITE, white_rook_position) to_position = Position(5, 4) black_rook = Rook(board, constants.BLACK, to_position) board.in_check.return_value = False game = Game() game._board = board self.assertTrue(game.try_move(white_rook, to_position)) self.assertEqual(white_rook.position, white_rook_position) self.assertEqual(black_rook.position, to_position) board.in_check.return_value = True self.assertFalse(game.try_move(white_rook, to_position)) self.assertEqual(white_rook.position, white_rook_position) self.assertEqual(black_rook.position, to_position)
def test_player_move_castle_fails(self): """Test castling fails when a position in between king and rook is attacked""" white_rook_position = Position(0, 0) black_rook = Rook(None, constants.BLACK, Position(2, 5)) white_rook = Rook(None, constants.WHITE, white_rook_position) test_board = { 'white_pieces': [white_rook], 'black_pieces': [black_rook] } board = set_up_test_board(new_game=False, test_board=test_board) game = set_up_test_game(board) with pytest.raises(InvalidMoveError): game.player_move(Position(4, 0), Position(2, 0)) self.assertEqual(white_rook.position, white_rook_position) self.assertTrue(isinstance(board.get_piece(Position(4, 0)), King))
def test_player_move_already_in_check_fails(self): """Test trying to move when you're in check and still in check after the move fails""" white_bishop_position = Position(3, 3) black_rook = Rook(None, constants.BLACK, Position(4, 5)) white_bishop = Rook(None, constants.WHITE, white_bishop_position) test_board = { 'white_pieces': [white_bishop], 'black_pieces': [black_rook] } board = set_up_test_board(new_game=False, test_board=test_board) game = set_up_test_game(board) with pytest.raises(InvalidMoveError): game.player_move(white_bishop_position, Position(6, 6))
def test_pawn_promotion(self): """Test when a pawn reaches the end of the board that it is promoted to a queen""" white_pawn_position = Position(0, 6) end_position = Position(0, 7) white_pawn = Pawn(None, constants.WHITE, white_pawn_position) test_board = {'white_pieces': [white_pawn]} board = set_up_test_board(new_game=False, test_board=test_board) game = set_up_test_game(board) game.player_move(white_pawn_position, end_position) self.assertIsNone(white_pawn.position) self.assertNotIn(white_pawn, board.white_pieces) queen = board.get_piece(end_position) self.assertTrue(isinstance(queen, Queen))
def test_player_move_fails_invalid_move(self, board, mock_get_moves): rook_position = Position(1, 1) rook = Rook(board, constants.WHITE, rook_position) board.get_piece.return_value = rook mock_get_moves.return_value = { rook: { constants.MOVES: [Position(0, 1), Position(1, 2)], constants.SPECIAL_MOVES: {} } } game = Game() game._board = board with pytest.raises(InvalidMoveError): game.player_move(rook.position, Position(2, 2))
def get_moves(self, colour): """Returns all the valid moves a player can make""" board_moves = self.board.get_moves(colour) pieces = list(board_moves.keys()) no_moves = True for piece in pieces: moves = board_moves[piece][constants.MOVES] for move in deepcopy(moves): if not self.try_move(piece, move): moves.remove(move) special_moves = board_moves[piece][constants.SPECIAL_MOVES] for key in deepcopy(list(special_moves.keys())): move = Position(key[0], key[1]) move_type = special_moves[key].get(constants.TYPE, None) if move_type is constants.EN_PASSANT: if not self.is_valid_passant( piece, move, special_moves[key][constants.CAPTURE_PIECE]): special_moves.pop(key) elif move_type is constants.CASTLE: if not self.try_move(piece, move): special_moves.pop(key) if moves or special_moves: no_moves = False if no_moves: if self.board.in_check(colour): self._status = constants.CHECKMATE else: self._status = constants.STALEMATE return board_moves
def test_in_check(self): """Test in_check returns true when king is in_check""" black_rook = Rook(None, BLACK, Position(4, 4)) test_board = { 'black_pieces': [black_rook] } board = set_up_test_board(new_game=False, test_board=test_board) self.assertTrue(board.in_check(WHITE))
def test_player_move_capture_success(self): """Test a basic capture of a piece works as expected""" white_rook_position = Position(2, 0) black_rook_position = Position(2, 5) black_rook = Rook(None, constants.BLACK, black_rook_position) white_rook = Rook(None, constants.WHITE, white_rook_position) test_board = { 'white_pieces': [white_rook], 'black_pieces': [black_rook] } board = set_up_test_board(new_game=False, test_board=test_board) game = set_up_test_game(board) game.player_move(white_rook_position, black_rook_position) self.assertEqual(white_rook.position, black_rook_position) self.assertIsNone(black_rook.position) self.assertNotIn(black_rook, board.black_pieces)
def set_up_test_board(new_game=False, test_board={}): """Helper method for tests to set up a board in a custom position""" board = Board(new_game=new_game) if not new_game: board.black_king = test_board.get('black_king', King(board, BLACK, Position(4, 7))) board.white_king = test_board.get('white_king', King(board, WHITE, Position(4, 0))) board.white_pieces = [board.white_king] board.black_pieces = [board.black_king] board.white_pieces += test_board.get('white_pieces', []) board.black_pieces += test_board.get('black_pieces', []) for piece in board.white_pieces: piece.board = board for piece in board.black_pieces: piece.board = board return board
def test_add_list_moves_friendly_squares(self, board): """Test add list moves with with friendly pieces in the way, returns the correct moves""" board.is_open.return_value = False board.is_enemy.return_value = False piece = Piece(board, WHITE, Position(1, 1)) list_of_moves = [(3, 3), (1, 2)] moves = piece.add_list_moves(list_of_moves) self.assertEqual(len(moves), 0)
def test_player_move_fails_wrong_turn(self, board): rook_position = Position(1, 1) board.get_piece.return_value = Rook(board, constants.BLACK, rook_position) game = Game() game._board = board game._turn = constants.WHITE with pytest.raises(InvalidMoveError): game.player_move(rook_position, Position)
def test_player_move_en_passant_fails(self): """Test calling making an en passant move is fails when not valid""" white_position = Position(0, 3) black_position = Position(1, 3) white_rook = Rook(None, constants.WHITE, white_position) black_pawn = Pawn(None, constants.BLACK, black_position) test_board = { 'white_pieces': [white_rook], 'black_pieces': [black_pawn] } board = set_up_test_board(new_game=False, test_board=test_board) game = set_up_test_game(board, turn=constants.BLACK, history=get_pawn_move_record_passant( white_position, Position(0, 1))) en_passant_position = Position(0, 2) with pytest.raises(InvalidMoveError): game.player_move(black_position, en_passant_position)