Exemple #1
0
    def get_path_to_square(self, start, end, board):
        """Attempts to get the path for standard pieces (bishops, rooks, and queens).
        Raises an InvalidMoveException if the move is illegal for some reason.
        """
        # check if we can reach destination given the piece's moveset
        if not self.can_reach_square(start, end):
            raise InvalidMoveException('destination not reachable with piece')
        # get the movement necessary to reach destination
        offset = self._get_necessary_offset(start, end)

        cur_square = start
        path = [start]
        while cur_square is not end:
            next_row_idx, next_col_idx = tuple(
                map(sum, zip(cur_square.coords, offset)))
            cur_square = board.squares[next_row_idx][next_col_idx]
            path.append(cur_square)
            if cur_square.is_occupied():
                break
        if cur_square is not end:
            # reached a block on the path. raise
            raise InvalidMoveException(
                'destination not reachable due to block')
        # on end square. Check to see if occupied
        if cur_square.is_occupied():
            # raise if moving piece color is same as end square piece color
            if cur_square.piece.color is self.color:
                raise InvalidMoveException(
                    'cannot move into square occupied by player piece')
        # found a valid path
        return path
Exemple #2
0
 def get_two_move_path(self, start, end, board):
     """Attempts to get a path for a pawn moving two squares.
         Should be called after the destination is valid.
     """
     if self.has_moved:
         raise InvalidMoveException(
             'Tried to move pawn two pieces after it had already moved.')
     start_row_idx = start.row_idx
     mid_row_idx = start_row_idx + 1 if self.color is ChessColor.WHITE else start_row_idx - 1
     mid = board.squares[mid_row_idx][start.col_idx]
     if mid.is_occupied() or end.is_occupied():
         raise InvalidMoveException('Pawn blocked from moving forward.')
     return [start, mid, end]
Exemple #3
0
    def get_path_to_square(self, start, end, board):
        """Attempts to get the path for knights.
        Raises an InvalidMoveException if the move is illegal for some reason.
        """
        if not self.can_reach_square(start, end):
            raise InvalidMoveException('destination not reachable with piece')

        path = [start, end]
        if not end.is_occupied():
            return path, None
        # some piece on destination. Check color
        if end.piece.color is self.color:
            raise InvalidMoveException(
                'cannot move into square occupied by player piece')
        # capturing opponent piece
        return path
Exemple #4
0
    def test_get_checking_pieces(self, active_pieces_mock, path_mock):
        """Tests for the get_checking_pieces method."""
        num_rows = constants.STD_BOARD_WIDTH
        num_cols = constants.STD_BOARD_HEIGHT
        test_board = Board({'num_rows': num_rows, 'num_cols': num_cols})

        white_dummy_king = Piece(ChessColor.WHITE)
        white_mapping = [(white_dummy_king, test_board.squares[1][1]),
                         (Piece(ChessColor.WHITE), test_board.squares[1][2]),
                         (Piece(ChessColor.WHITE), test_board.squares[1][3])]
        black_dummy_king = Piece(ChessColor.BLACK)
        black_mapping = [(black_dummy_king, test_board.squares[7][1]),
                         (Piece(ChessColor.BLACK), test_board.squares[7][2]),
                         (Piece(ChessColor.BLACK), test_board.squares[7][3])]
        active_pieces_mock.return_value = (white_mapping, black_mapping)
        # should use the correct piece mapping
        # TODO

        # should return any pieces checking king
        path_mock.side_effect = [
            'path1', InvalidMoveException('path exception'), 'path2'
        ]
        expected_res = [
            CheckingReturnType(black_mapping[0][0], 'path1'),
            CheckingReturnType(black_mapping[2][0], 'path2')
        ]
        res = test_board.get_checking_pieces(ChessColor.WHITE)
        self.assertEqual(res, expected_res)
Exemple #5
0
    def test_get_path_to_square(self, valid_move_mock, one_move_mock,
                                two_move_mock):
        """Tests the overwritten get_path_to_square method."""
        one_move_return = 'one move return'
        two_move_return = 'two move return'
        valid_move_mock.return_value = True
        one_move_mock.return_value = one_move_return
        two_move_mock.return_value = two_move_return

        squares = self.board.squares
        row, col = 1, 1
        start = squares[row][col]
        straight1 = squares[row + 1][col]
        straight2 = squares[row + 2][col]
        right_diag = squares[row + 1][col + 1]
        left_diag = squares[row + 1][col - 1]

        valid_move_mock.return_value = False
        # should raise if piece cannot reach end square
        with self.assertRaises(InvalidMoveException):
            self.white_pawn.get_path_to_square(start, straight1, self.board)
        valid_move_mock.return_value = True

        # should properly call two move and one move functions
        one_ends = [straight1, left_diag, right_diag]
        for end in one_ends:
            res = self.white_pawn.get_path_to_square(start, end, self.board)
            self.assertEqual(res, one_move_return)

        two_res = self.white_pawn.get_path_to_square(start, straight2,
                                                     self.board)
        self.assertEqual(two_res, two_move_return)

        # should raise if moving one square fails
        one_move_mock.side_effect = InvalidMoveException('dummy exception')
        with self.assertRaises(InvalidMoveException):
            self.white_pawn.get_path_to_square(start, straight1, self.board)
        one_move_mock.side_effect = None

        # should raise if moving two squares fails
        two_move_mock.side_effect = InvalidMoveException('dummy exception')
        with self.assertRaises(InvalidMoveException):
            self.white_pawn.get_path_to_square(start, straight2, self.board)
        two_move_mock.side_effect = None
Exemple #6
0
    def test_get_castle_params(self, check_mock, move_mock, undo_mock):
        """tests the get_castle_params method."""
        check_mock.return_value = []

        king = self.king
        board = self.board
        left_rook = Rook(ChessColor.WHITE)
        right_rook = Rook(ChessColor.WHITE)
        left_square = board.squares[0][0]
        right_square = board.squares[0][7]

        king_square = board.squares[0][4]
        king_square.piece = self.king
        left_target = board.squares[0][2]
        right_target = board.squares[0][6]

        # TODO: queenside castle tests, black king tests
        # should raise if king has moved
        king.move_count = 1
        with self.assertRaises(InvalidMoveException):
            king.get_castle_params(king_square, right_target, board)
        king.move_count = 0

        # should raise if no rook on end
        with self.assertRaises(InvalidMoveException):
            king.get_castle_params(king_square, right_target, board)
        right_square.piece = right_rook
        # should raise if targeted rook is opponent's
        right_rook.color = ChessColor.BLACK
        with self.assertRaises(InvalidMoveException):
            king.get_castle_params(king_square, right_target, board)
        right_rook.color = ChessColor.WHITE

        # should raise if piece in between rook and king
        other_piece = Piece(ChessColor.WHITE)
        board.squares[0][5].piece = other_piece
        with self.assertRaises(InvalidMoveException):
            king.get_castle_params(king_square, right_target, board)
        board.squares[0][5].piece = None

        # should raise if king is in check when move starts
        check_mock.return_value = ['something']
        with self.assertRaises(InvalidMoveException):
            king.get_castle_params(king_square, right_target, board)
        check_mock.return_value = []

        # should raise if king attempts to move through check
        move_mock.side_effect = InvalidMoveException('move exception')
        with self.assertRaises(InvalidMoveException):
            king.get_castle_params(king_square, right_target, board)
        move_mock.side_effect = None

        # should return the value otherwise
        expected_res = (king_square, right_target, None, None, MoveSideEffect.CASTLE)
        res = king.get_castle_params(king_square, right_target, board)
        self.assertEqual(res, expected_res)
Exemple #7
0
 def get_one_move_path(self, start, end, board):
     """Attempts to get a path for a pawn moving one square."""
     path = [start, end]
     col_offset = end.col_idx - start.col_idx
     if col_offset == 0:
         if end.is_occupied():
             raise InvalidMoveException('Pawn blocked from moving forward.')
         return path
     # abs(col_offset) is 1, moving diagonally
     if end.is_occupied():
         # attempting capture. Make sure color is correct
         if end.piece.color is self.color:
             raise InvalidMoveException(
                 'Pawn cannot capture piece of same color.')
         return path
     # no piece in diagonal move. only possible with en passant
     if not self.can_capture_en_passant(start, end, board):
         raise InvalidMoveException(
             'Pawn cannot move diagonally without capturing.')
     return path
Exemple #8
0
    def get_path_to_square(self, start, end, board):
        """Attempts to get the path from start to end given the piece is a pawn.

        Raises an InvalidMoveException if the move is illegal for some reason.
        """
        if not self.can_reach_square(start, end):
            raise InvalidMoveException('destination not reachable with piece')

        row_offset = end.row_idx - start.row_idx
        if row_offset == 2:
            return self.get_two_move_path(start, end, board)
        # row_offset of 1
        return self.get_one_move_path(start, end, board)
Exemple #9
0
    def test_make_move(self, move_mock, end_mock, validate_mock):
        """tests function that processes an attempted move of a piece."""
        test_game = Game(self.white_player, self.black_player)
        start_coords, end_coords = ((1, 1), (2, 1))
        start_square = test_game.board.squares[1][1]
        end_square = test_game.board.squares[2][1]
        move_mock.return_value = Move(start_square, end_square)

        # TODO: test adding captured piece to player

        # should use the proper players as cur_player/opponent
        test_game.make_move(start_coords, end_coords)
        move_mock.assert_called_with(start_coords, end_coords,
                                     ChessColor.WHITE)

        test_game.is_white_turn = False
        test_game.make_move(start_coords, end_coords)
        move_mock.assert_called_with(start_coords, end_coords,
                                     ChessColor.BLACK)

        test_game.is_white_turn = True
        # should raise if piece can't be moved
        move_mock.side_effect = InvalidMoveException('dummy exception')
        with self.assertRaises(InvalidMoveException):
            test_game.make_move(start_coords, end_coords)
        move_mock.side_effect = None

        # should raise if a pawn promotion is occuring
        move_mock.return_value = Move(start_square, end_square, None, None,
                                      MoveSideEffect.PAWN_PROMOTION)
        with self.assertRaises(PawnPromotionException):
            test_game.make_move(start_coords, end_coords)
        move_mock.return_value = Move(start_square, end_square)

        # should call end of game method and switch turns
        end_mock.reset_mock()
        test_game.make_move(start_coords, end_coords)
        end_mock.assert_called_once()
        test_game.is_white_turn = False
Exemple #10
0
    def get_castle_params(self, start, end, board):
        """Attempts to get move parameters for castling.
            Should only be called if the king is moving horizontally 2 squares.
        """
        # 1) king has not moved
        if self.has_moved:
            raise InvalidMoveException('cannot castle if king has moved.')
        # 2) targeted rook has not moved (targeted rook must be player's piece)
        row_idx = start.row_idx
        rook_col_idx = board.max_col if end.col_idx > start.col_idx else 0
        rook_square = board.squares[row_idx][rook_col_idx]
        rook = rook_square.piece
        if not rook:
            raise InvalidMoveException('no rook found for castling')
        if rook.color is not self.color:
            raise InvalidMoveException('cannot castle with opponent rook')
        # 3) no pieces in between king and rook
        i = 1 if rook_col_idx > start.col_idx else -1
        cur_col_idx = start.col_idx + i
        while cur_col_idx != rook_col_idx:
            cur_square = board.squares[row_idx][cur_col_idx]
            if cur_square.is_occupied():
                raise InvalidMoveException(
                    'cannot castle with piece in between rook and king')
            cur_col_idx += i
        # 4) make sure king isn't in check or moves through check
        # see if king starts in check
        start_checking_pieces = board.get_checking_pieces(self.color)
        if start_checking_pieces:
            raise InvalidMoveException('cannot castle while in check.')
        # see if king moves through check
        try:
            mid_coords = (row_idx, start.col_idx + i)
            board.move_piece(start.coords, mid_coords, self.color)
            board.undo_move()
        except (InvalidMoveException, ValueError):
            raise InvalidMoveException('cannot castle through check.')

        # can successfully castle
        return (start, end, None, None, MoveSideEffect.CASTLE)
Exemple #11
0
    def test_move_piece(self, move_mock, check_mock, side_effect_mock,
                        undo_mock):
        """tests function that actually moves pieces in the game."""
        num_rows = constants.STD_BOARD_WIDTH
        num_cols = constants.STD_BOARD_HEIGHT
        test_board = Board({'num_rows': num_rows, 'num_cols': num_cols})
        test_piece = Piece(ChessColor.WHITE)

        # should raise if coordinates are out of bounds
        oob_cases = [
            ((num_rows, 0), (0, 0)),
            ((0, 0), (num_rows, 0)),
            ((0, num_cols), (0, 0)),
            ((0, 0), (0, num_cols)),
            ((-1, 0), (0, 0)),
            ((0, 0), (-1, 0)),
            ((0, -1), (0, 0)),
            ((0, 0), (0, -1)),
        ]
        for oob_start, oob_end in oob_cases:
            with self.assertRaises(ValueError):
                test_board.move_piece(oob_start, oob_end, ChessColor.WHITE)

        start_coords, end_coords = ((0, 0), (1, 0))
        start_square = test_board.squares[start_coords[0]][start_coords[1]]
        end_square = test_board.squares[end_coords[0]][end_coords[1]]
        # should throw if start and end coords are equal
        with self.assertRaises(InvalidMoveException):
            test_board.move_piece(start_coords, start_coords, ChessColor.WHITE)

        # should raise if no piece lies on starting square
        with self.assertRaises(InvalidMoveException):
            test_board.move_piece(start_coords, end_coords, ChessColor.WHITE)

        start_square.piece = test_piece
        # should raise if active_color is not equal to the piece being moved
        with self.assertRaises(InvalidMoveException):
            test_board.move_piece(start_coords, end_coords, ChessColor.BLACK)

        # should raise if piece.get_move throws
        move_mock.side_effect = InvalidMoveException('mock exception')
        with self.assertRaises(InvalidMoveException):
            test_board.move_piece(start_coords, end_coords, ChessColor.WHITE)
        move_mock.side_effect = None

        # should successfully move the piece otherwise
        basic_move_params = (start_square, end_square)
        basic_move = Move(*basic_move_params)
        move_mock.return_value = basic_move_params

        res = test_board.move_piece(start_coords, end_coords, ChessColor.WHITE)
        self.assertEqual(res, basic_move)
        self.assertFalse(start_square.is_occupied())
        self.assertEqual(test_piece, end_square.piece)
        self.assertEqual(test_board.move_history, [basic_move])
        self.assertTrue(test_piece.has_moved)

        test_piece.move_count = 0
        end_square.piece = None
        start_square.piece = test_piece

        # should call the side effect function if appropriate
        side_effect_move_params = (start_square, end_square, None, None,
                                   'some effect')
        side_effect_move = Move(*side_effect_move_params)
        move_mock.return_value = side_effect_move_params

        res = test_board.move_piece(start_coords, end_coords, ChessColor.WHITE)
        self.assertEqual(res, side_effect_move)
        side_effect_mock.assert_called_with(side_effect_move)

        test_piece.move_count = 0
        end_square.piece = None
        start_square.piece = test_piece

        # should raise if player puts themselves in check and call undo_move
        check_mock.return_value = ['something']
        with self.assertRaises(InvalidMoveException):
            test_board.move_piece(start_coords, end_coords, ChessColor.WHITE)
            undo_mock.assert_called_once()
        check_mock.return_value = []