コード例 #1
0
    def test_read(self):
        pos = StandardPosition((0, 0))
        self.assertEqual(str(pos), 'a1')

        pos = StandardPosition((0, 7))
        self.assertEqual(str(pos), 'a8')

        pos = StandardPosition((7, 7))
        self.assertEqual(str(pos), 'h8')

        pos = StandardPosition((7, 0))
        self.assertEqual(str(pos), 'h1')
コード例 #2
0
    def test_write(self):
        pos = StandardPosition.from_str('a1')
        self.assertEqual(tuple(pos), (0, 0))

        pos = StandardPosition.from_str('a8')
        self.assertEqual(tuple(pos), (0, 7))

        pos = StandardPosition.from_str('h8')
        self.assertEqual(tuple(pos), (7, 7))

        pos = StandardPosition.from_str('h1')
        self.assertEqual(tuple(pos), (7, 0))
コード例 #3
0
def generate_moves(str_moves):
    for str_move in str_moves:
        source = StandardPosition.from_str(str_move[0:2])
        destination = StandardPosition.from_str(str_move[2:4])
        promotion_char = str_move[4:]
        if promotion_char:
            yield StandardMove(source=source,
                               destination=destination,
                               promotion=from_str(promotion_char,
                                                  initialized=False))
        else:
            yield StandardMove(source=source, destination=destination)
コード例 #4
0
    def from_str(cls, move_str: str):
        if len(move_str) == 4:
            return cls(StandardPosition.from_str(move_str[0:2]),
                       StandardPosition.from_str(move_str[2:4]))

        elif len(move_str) == 5:
            return cls(StandardPosition.from_str(move_str[0:2]),
                       StandardPosition.from_str(move_str[2:4]),
                       from_str(move_str[4], initialized=False))
        else:
            raise ValueError(
                'Primitive move is described as 4-5 length string (without or with promotion)'
            )
コード例 #5
0
    def test_read_fenstring(self):
        board = StandardBoard()
        board.put_piece(piece=King(White),
                        position=StandardPosition.from_str('e1'))
        self.assertEqual(board.get_fen(), '8/8/8/8/8/8/8/4K3')

        board.put_piece(piece=King(Black),
                        position=StandardPosition.from_str('e8'))
        self.assertEqual(board.get_fen(), '4k3/8/8/8/8/8/8/4K3')

        board.put_piece(piece=Queen(White),
                        position=StandardPosition.from_str('d1'))
        board.put_piece(piece=Queen(Black),
                        position=StandardPosition.from_str('d8'))
        self.assertEqual(board.get_fen(), '3qk3/8/8/8/8/8/8/3QK3')

        board.put_piece(piece=Rook(White),
                        position=StandardPosition.from_str('a1'))
        board.put_piece(piece=Rook(White),
                        position=StandardPosition.from_str('h1'))
        board.put_piece(piece=Rook(Black),
                        position=StandardPosition.from_str('a8'))
        board.put_piece(piece=Rook(Black),
                        position=StandardPosition.from_str('h8'))

        self.assertEqual(board.get_fen(), 'r2qk2r/8/8/8/8/8/8/R2QK2R')
コード例 #6
0
    def test_write_above_standard(self):
        pos = StandardPosition.from_str('z100')
        self.assertEqual(tuple(pos), (25, 99))

        pos = StandardPosition.from_str('ba100')
        self.assertEqual(tuple(pos), (26, 99))

        pos = StandardPosition.from_str('bz100')
        self.assertEqual(tuple(pos), (51, 99))

        pos = StandardPosition.from_str('ca100')
        self.assertEqual(tuple(pos), (52, 99))

        pos = StandardPosition.from_str('baa100')
        self.assertEqual(tuple(pos), (676, 99))
コード例 #7
0
    def test_read_above_standard(self):
        pos = StandardPosition((25, 99))
        self.assertEqual(str(pos), 'z100')  # 'b1 means the same as ab1 just like decimals - 001 == 1'

        pos = StandardPosition((26, 99))
        self.assertEqual(str(pos), 'ba100')

        pos = StandardPosition((51, 99))
        self.assertEqual(str(pos), 'bz100')

        pos = StandardPosition((52, 99))
        self.assertEqual(str(pos), 'ca100')

        pos = StandardPosition((676, 99))
        self.assertEqual(str(pos), 'baa100')
コード例 #8
0
    def set_fen(self, board_fen: str):
        # TODO: validate input string
        """
        Sets board state from FEN
        :param board_fen: string, min 15 letters (ranks separated by slash)
        """
        if self.files != 8 or self.ranks != 8:  # FEN is not supported on other-sized board than 8x8
            raise NotImplemented

        pieces_tmp: Dict['StandardPosition', 'Piece'] = {}

        rank_counter = self.ranks - 1
        for rank in board_fen.split('/'):
            file_counter = 0
            for piece in rank:
                if piece not in digits:
                    position_object = StandardPosition(
                        (file_counter, rank_counter))
                    piece_object = from_str(piece)
                    pieces_tmp.update({position_object: piece_object})
                    file_counter += 1
                else:
                    for i in range(int(piece)):
                        file_counter += 1
            rank_counter -= 1

        self.__pieces = pieces_tmp
コード例 #9
0
def test_init_move():
    a_pos = StandardPosition((0, 0))
    b_pos = StandardPosition((0, 1))

    move = StandardMove(source=a_pos, destination=b_pos)
    assert move.source == a_pos
    assert move.destination == b_pos
    assert move.promotion is None

    move = StandardMove(source=a_pos, destination=b_pos, promotion=Queen)
    assert move.source == a_pos
    assert move.destination == b_pos
    assert move.promotion == Queen

    with pytest.raises(ValueError, message="Source and destination should be not the same field"):
        StandardMove(source=StandardPosition((0, 0)), destination=StandardPosition((0, 0)))
コード例 #10
0
 def assert_move(self, move: 'StandardMove'):
     """
     verify if given move is valid in current game state, proper exception is raised when needed
     """
     source, destination = move.source, move.destination
     piece = self.board.get_piece(source)
     if not piece:
         raise NoPiece("Any piece on %s, you need to move pieces, not an air." % source)
     # check if requested destination field is in available fields generated by game logic
     available_dest = self.standard_moves(source) | self.standard_captures(source) | self.special_moves(source)
     if destination not in available_dest:
         raise NotAValidMove("%s is not a proper move for a %s %s\npositions available for that piece: %s" % (
             move, piece.side, piece.name, ', '.join({str(pos) for pos in available_dest})))
     # create test board for real validations
     test_board = deepcopy(self.board)
     test_piece = test_board.remove_piece(source)  # TODO: test special moves
     test_board.put_piece(test_piece, destination)
     if isinstance(test_piece, Pawn):  # TODO: here is an example of above TODO
         if destination == self.en_passant:
             test_board.remove_piece(StandardPosition((destination.file, source.rank)))
     # test on the copied board if move not causing any self-check
     king_pos, king = test_board.find_pieces(requested_piece=King(self.on_move))[0]
     if king_pos in self.attacked_fields_by_sides(set(self.sides) - {piece.side}, test_board):
         raise CausesCheck("{move} move causes {side} {name} ({pos}) check delivered by: [{atck}]".format(
             move=move, side=king.side, name=king.name, pos=king_pos,
             atck=', '.join(
                 ["%s: %s" % (position, '%s %s' % (piece.side, piece.name))
                  for position, piece
                  in self.who_can_step_here(king_pos, test_board).items()]
             )
         ))
     # simple validation when promotion was declared for promoted pawn
     if isinstance(piece, Pawn) and destination.rank in (7, 0):
         if not move.promotion:
             raise NotAValidPromotion("Proper promotion are required when promoting a pawn")
コード例 #11
0
    def standard_moves(self, position: 'StandardPosition', board: StandardBoard = None) -> Set['StandardPosition']:
        """
        return set of available moves
        """
        if not board:
            board = self.board
        piece = board.get_piece(position)
        if not piece:
            raise NoPiece('Any piece on %s' % position)

        new_positions = set()
        for m_desc in piece.movement.move:
            for vector in self.__transform_vector(m_desc.vector, m_desc.any_direction, piece.side):
                if m_desc.distance is infinity:
                    loop = itertools.count(1)
                else:
                    loop = range(1, m_desc.distance + 1)

                for distance in loop:
                    new_position = StandardPosition(
                        (position[0] + int(vector[0] * distance),
                         position[1] + int(vector[1] * distance))
                    )
                    if not board.validate_position(new_position):
                        break

                    new_piece = board.get_piece(new_position)
                    if new_piece is not None:
                        break
                    new_positions.add(new_position)

        return new_positions
コード例 #12
0
 def test_white_pawn_capture_available(self):
     piece = Pawn(White)
     pos = StandardPosition.from_str('b4')
     self.game.board.put_piece(piece, pos)
     self.game.board.put_piece(Pawn(Black), StandardPosition.from_str('a5'))
     self.game.board.put_piece(Pawn(Black), StandardPosition.from_str('b5'))
     self.game.board.put_piece(Pawn(Black), StandardPosition.from_str('c5'))
     self.game.board.put_piece(Pawn(Black), StandardPosition.from_str('a4'))
     self.game.board.put_piece(Pawn(Black), StandardPosition.from_str('c4'))
     self.game.board.put_piece(Pawn(Black), StandardPosition.from_str('a3'))
     self.game.board.put_piece(Pawn(Black), StandardPosition.from_str('b3'))
     self.game.board.put_piece(Pawn(Black), StandardPosition.from_str('c3'))
     self.assertEqual(self._get_available_move_strings(pos), set())
     self.assertEqual(self._get_available_capture_strings(pos),
                      {'a5', 'c5'})
コード例 #13
0
    def special_moves(self, position: 'StandardPosition', board: StandardBoard = None) -> Set['StandardPosition']:
        """
        return set of available special moves (castling and en passant)
        """
        # TODO: replace for some more convenient solution
        if not board:
            board = self.board
        piece = board.get_piece(position)
        if not piece:
            raise NoPiece('Any piece on %s' % position)

        new_positions = set()
        if isinstance(piece, Pawn):
            new_position = None
            if piece == Pawn(Black) and position.rank == 6 and not board.get_piece(
                    StandardPosition((position.file, 5))):
                new_position = StandardPosition(
                    (position.file,
                     position.rank - 2)
                )
            elif piece == Pawn(White) and position.rank == 1 and not board.get_piece(
                    StandardPosition((position.file, 2))):
                new_position = StandardPosition(
                    (position.file,
                     position.rank + 2)
                )
            if new_position and board.validate_position(new_position):
                new_piece = board.get_piece(new_position)
                if new_piece is None:
                    new_positions.add(new_position)

        elif isinstance(piece, King) and self.__castling and position.file == 4:
            attacked_fields = self.attacked_fields_by_sides(set(self.sides) - {self.on_move})

            if King(self.on_move) in self.__castling:
                pos1 = StandardPosition((position.file + 1, position.rank))
                pos2 = StandardPosition((position.file + 2, position.rank))
                if not board.get_piece(pos1) and not board.get_piece(pos2) and not {pos1, pos2} & attacked_fields:
                    new_positions.add(pos2)
            if Queen(self.on_move) in self.__castling:
                pos1 = StandardPosition((position.file - 1, position.rank))
                pos2 = StandardPosition((position.file - 2, position.rank))
                pos3 = StandardPosition((position.file - 2, position.rank))
                if not board.get_piece(pos1) and not board.get_piece(pos2) and not board.get_piece(pos3) \
                        and not {pos1, pos2} & attacked_fields:
                    new_positions.add(pos2)

        return new_positions
コード例 #14
0
def test_init_move_from_uci():
    move = StandardMove.from_str('e2e4')
    assert move.source == StandardPosition((4, 1))
    assert move.destination == StandardPosition((4, 3))
    assert move.promotion is None

    move = StandardMove.from_str('a7a8q')
    assert move.source == StandardPosition((0, 6))
    assert move.destination == StandardPosition((0, 7))
    assert move.promotion == Queen

    # cases based on wrong length
    bad_moves = ['a', 'e2', 'e2e', 'e2e4qq', 'sadnhjfegesj']
    # edge cases, depended on proper UCI syntax and position ranges
    bad_moves += ['eeee', 'e234', '4123f', 'qqqqq']

    for move_str in bad_moves:
        with pytest.raises(ValueError, message='Not a proper UCI format should be declined'):
            StandardMove.from_str(move_str)

    with pytest.raises(ValueError, message="Source and destination should be not the same field"):
        StandardMove.from_str('e2e2')
コード例 #15
0
    def move(self, move: 'StandardMove') -> Optional['Piece']:
        """
        try to execute given move, return captured piece if not fails
        """
        self.assert_move(move)
        source, destination = move.source, move.destination
        moved_piece = self.board.get_piece(position=source)
        if moved_piece.side != self.on_move:
            raise WrongMoveOrder("You are trying to move %s when %s are on move" % (moved_piece.side, self.on_move))
        # move are accepted by the game logic, all below concerns game state (counters, history, proper move execution)
        self.save_history()
        moved_piece = self.board.remove_piece(position=source)
        taken_piece = self.board.put_piece(piece=moved_piece, position=destination)
        if isinstance(moved_piece, Pawn):
            if destination == self.en_passant:  # check if en passant move was involved
                taken_piece = self.board.remove_piece(StandardPosition((destination.file, source.rank)))

            # check if pawn was pushed by two fields, set needed en passant position if so
            if abs(source.rank - destination.rank) == 2:
                self.__en_passant = StandardPosition(
                    (source.file,
                     int((source.rank + destination.rank) / 2))
                )
            else:  # clear en passant position on any other pawn-move
                self.__en_passant = None
            self.__half_moves_since_pawn_moved = 0

            if destination.rank in (7, 0):  # handle promotion
                self.board.put_piece(move.promotion(self.on_move), destination)

        else:  # clear en passant position on any other piece-move
            if self.__en_passant:
                self.__en_passant = None
            self.__half_moves_since_pawn_moved += 1

        # simple and ugly check for castling execution
        if isinstance(moved_piece, King) and abs(source.file - destination.file) == 2:
            rank = source.rank
            if move.destination.file == 6:  # king-castle
                moved_rook = self.board.remove_piece(position=StandardPosition((7, rank)))
                self.board.put_piece(moved_rook, StandardPosition((5, rank)))
            elif destination.file == 2:  # queen-castle
                moved_rook = self.board.remove_piece(position=StandardPosition((0, rank)))
                self.board.put_piece(moved_rook, StandardPosition((3, rank)))

        if not taken_piece:
            self.__half_moves_since_capture += 1
        else:
            self.__half_moves_since_capture = 0
            self.__pocket[self.on_move].append(taken_piece)  # put taken piece to the side pocket!

        self.__update_castling_info(source, destination)
        self.__position_occurence[hash(self.board)] += 1

        self.moves_history.append(move)
        self.__half_moves += 1
        return taken_piece
コード例 #16
0
    def test_board_get_put_remove_piece(self):
        board = StandardBoard()
        old_piece = board.put_piece(piece=Rook(White),
                                    position=StandardPosition.from_str('e4'))
        self.assertEqual(old_piece, None)
        print(board.get_piece(StandardPosition.from_str('e3')))
        print(Rook(White))
        self.assertNotEqual(board.get_piece(StandardPosition.from_str('e3')),
                            Rook(White))
        self.assertEqual(board.get_piece(StandardPosition.from_str('e4')),
                         Rook(White))

        old_piece = board.put_piece(piece=Queen(Black),
                                    position=StandardPosition.from_str('e4'))
        self.assertEqual(old_piece, Rook(White))
        self.assertEqual(board.get_piece(StandardPosition.from_str('e4')),
                         Queen(Black))

        old_piece = board.remove_piece(StandardPosition.from_str('e4'))
        self.assertEqual(board.get_piece(StandardPosition.from_str('e4')),
                         None)
        self.assertEqual(old_piece, Queen(Black))
コード例 #17
0
    def standard_captures(self, position: 'StandardPosition', board: StandardBoard = None) -> Set['StandardPosition']:
        """
        return set of available captures
        """
        if not board:
            board = self.board
        piece = board.get_piece(position)
        if not piece:
            raise NoPiece('Any piece on %s' % position)

        new_positions = set()
        if isinstance(piece, Pawn) and self.en_passant and self.en_passant in self.attacked_fields(position, board):
            # TODO: move to "special captures" or something
            new_positions.add(self.en_passant)

        for c_desc in piece.movement.capture:
            for vector in self.__transform_vector(c_desc.vector, c_desc.any_direction, piece.side):
                if c_desc.distance is infinity:
                    loop = itertools.count(1)
                else:
                    loop = range(1, c_desc.distance + 1)

                for distance in loop:
                    new_position = StandardPosition(
                        (position[0] + int(vector[0] * distance),
                         position[1] + int(vector[1] * distance))
                    )
                    if not board.validate_position(new_position):
                        break

                    new_piece = board.get_piece(new_position)
                    if not new_piece:
                        continue
                    if new_piece and new_piece.side != piece.side:
                        new_positions.add(new_position)
                        if c_desc.capture_break:
                            break
                    elif new_piece and new_piece.side == piece.side:
                        break

        return new_positions
コード例 #18
0
    def __update_castling_info(self, source, destination):
        """
        simply removes castling ability if given move do so
        """
        if self.__castling:
            if StandardPosition.from_str('e1') in (source,):
                self.__castling = self.__castling - {King(White), Queen(White)}
            elif StandardPosition.from_str('a1') in (source, destination):
                self.__castling = self.__castling - {Queen(White)}
            elif StandardPosition.from_str('h1') in (source, destination):
                self.__castling = self.__castling - {King(White)}

            if StandardPosition.from_str('e8') in (source,):
                self.__castling = self.__castling - {King(Black), Queen(Black)}
            elif StandardPosition.from_str('a8') in (source, destination):
                self.__castling = self.__castling - {Queen(Black)}
            elif StandardPosition.from_str('h8') in (source, destination):
                self.__castling = self.__castling - {King(Black)}
コード例 #19
0
    def attacked_fields(self, position: 'StandardPosition', board: StandardBoard = None) -> Set['StandardPosition']:
        """
        return set of attacked positions which as coming from given position (piece on that position)
        """
        if not board:
            board = self.board
        piece = board.get_piece(position)
        if not piece:
            raise NoPiece('Any piece on %s' % position)

        new_positions = set()
        for c_desc in piece.movement.capture:
            for vector in self.__transform_vector(c_desc.vector, c_desc.any_direction, piece.side):
                if c_desc.distance is infinity:
                    loop = itertools.count(1)
                else:
                    loop = range(1, c_desc.distance + 1)

                for distance in loop:
                    new_position = StandardPosition(
                        (position[0] + int(vector[0] * distance),
                         position[1] + int(vector[1] * distance))
                    )
                    if not board.validate_position(new_position):
                        break

                    new_piece = board.get_piece(new_position)
                    if not new_piece:
                        new_positions.add(new_position)
                    elif new_piece and new_piece.side != piece.side:
                        new_positions.add(new_position)
                        break
                    elif new_piece and new_piece.side == piece.side:
                        break

        return new_positions
コード例 #20
0
    def test_write_fenstring(self):
        board = StandardBoard()
        board.set_fen('8/8/8/8/8/8/8/4K3')
        self.assertEqual(
            board.get_piece(position=StandardPosition.from_str('e1')),
            King(White))
        self.assertEqual(
            board.get_piece(position=StandardPosition.from_str('e8')), None)

        board.set_fen('4k3/8/8/8/8/8/8/4K3')
        self.assertEqual(
            board.get_piece(position=StandardPosition.from_str('e1')),
            King(White))
        self.assertEqual(
            board.get_piece(position=StandardPosition.from_str('e8')),
            King(Black))
        self.assertNotEqual(
            board.get_piece(position=StandardPosition.from_str('e8')),
            King(White))
        self.assertNotEqual(
            board.get_piece(position=StandardPosition.from_str('e8')),
            Rook(Black))
コード例 #21
0
 def move_from_str(self, move_str: str):
     return StandardMove(
         StandardPosition.from_str(move_str[:2]),
         StandardPosition.from_str(move_str[2:])
     )
コード例 #22
0
 def moves_from_str(self, moves_str):
     for move_str in moves_str:
         yield StandardMove(
             StandardPosition.from_str(move_str[:2]),
             StandardPosition.from_str(move_str[-2:])
         )
コード例 #23
0
        while True:
            move_str = input("Move: ")
            if move_str == "board":
                print(board_rendererer.normal(game.board))
                continue
            elif move_str == "back":
                i = int(input("How many moves do you want to rollback? "))
                try:
                    game.variant.load_history(i)
                except IndexError:
                    print("Given value are above of the length of moves history")
                    continue
                print(board_rendererer.normal(game.board))
                continue
            try:
                source = StandardPosition.from_str(move_str[0:2])
                destination = StandardPosition.from_str(move_str[2:4])
                promotion_char = move_str[4:]
            except ValueError as err:
                print("bad syntax (%s)" % err)
                continue
            if not game.board.validate_position(source) or not game.board.validate_position(destination):
                print("You give position above actual board range (%dx%d)" % game.board.size)
                continue

            if promotion_char:
                move = StandardMove(source=source, destination=destination,
                                    promotion=from_str(promotion_char, initialized=False))
            else:
                move = StandardMove(source=source, destination=destination)
            try:
コード例 #24
0
    def init_board_state(self):
        """
        Set board start position for classic chess variant
        """
        self.board.put_piece(piece=Rook(White), position=StandardPosition((0, 0)))
        self.board.put_piece(piece=Rook(White), position=StandardPosition((7, 0)))
        self.board.put_piece(piece=Knight(White), position=StandardPosition((1, 0)))
        self.board.put_piece(piece=Knight(White), position=StandardPosition((6, 0)))
        self.board.put_piece(piece=Bishop(White), position=StandardPosition((2, 0)))
        self.board.put_piece(piece=Bishop(White), position=StandardPosition((5, 0)))
        self.board.put_piece(piece=Queen(White), position=StandardPosition((3, 0)))
        self.board.put_piece(piece=King(White), position=StandardPosition((4, 0)))

        for i in range(8):
            self.board.put_piece(piece=Pawn(White), position=StandardPosition((i, 1)))

        for i in range(8):
            self.board.put_piece(piece=Pawn(Black), position=StandardPosition((i, 6)))

        self.board.put_piece(piece=Rook(Black), position=StandardPosition((0, 7)))
        self.board.put_piece(piece=Rook(Black), position=StandardPosition((7, 7)))
        self.board.put_piece(piece=Knight(Black), position=StandardPosition((1, 7)))
        self.board.put_piece(piece=Knight(Black), position=StandardPosition((6, 7)))
        self.board.put_piece(piece=Bishop(Black), position=StandardPosition((2, 7)))
        self.board.put_piece(piece=Bishop(Black), position=StandardPosition((5, 7)))
        self.board.put_piece(piece=Queen(Black), position=StandardPosition((3, 7)))
        self.board.put_piece(piece=King(Black), position=StandardPosition((4, 7)))

        return self.board.get_fen()
コード例 #25
0
 def test_white_pawn_one_move(self):
     piece = Pawn(White)
     pos = StandardPosition.from_str('b3')
     self.game.board.put_piece(piece, pos)
     self.assertEqual(self._get_available_move_strings(pos), {'b4'})
     self.assertEqual(self._get_available_capture_strings(pos), set())