Пример #1
0
        def test_move_planes_round_trip_given_knight_move_expect_round_trip(self):
            board = CrazyhouseBoard()

            move = construct_move_from_positions([4, 4], [2, 3])
            assert_round_trip(self, board, move, comment="knight move 0")  # A

            move = construct_move_from_positions([4, 4], [3, 2])
            assert_round_trip(self, board, move, comment="knight move 1")  # B

            move = construct_move_from_positions([4, 4], [2, 5])
            assert_round_trip(self, board, move, comment="knight move 2")  # C

            move = construct_move_from_positions([4, 4], [3, 6])
            assert_round_trip(self, board, move, comment="knight move 3")  # D

            move = construct_move_from_positions([4, 4], [6, 3])
            assert_round_trip(self, board, move, comment="knight move 4")  # E

            move = construct_move_from_positions([4, 4], [5, 2])
            assert_round_trip(self, board, move, comment="knight move 5")  # F

            move = construct_move_from_positions([4, 4], [6, 5])
            assert_round_trip(self, board, move, comment="knight move 2")  # G

            move = construct_move_from_positions([4, 4], [5, 6])
            assert_round_trip(self, board, move, comment="knight move 2")  # H
Пример #2
0
    def test_move_planes_round_trip_given_promotion_expect_round_trip(self):
        board = CrazyhouseBoard()

        move = construct_move_from_positions([6, 4], [7, 4], promotion=3)
        assert_round_trip(self, board, move, comment="underpromotion (straight)")

        move = construct_move_from_positions([6, 4], [7, 3], promotion=3)
        assert_round_trip(self, board, move, comment="underpromotion (left)")

        move = construct_move_from_positions([6, 4], [7, 5], promotion=3)
        assert_round_trip(self, board, move, comment="underpromotion (right)")
Пример #3
0
 def __init__(self, board=CrazyhouseBoard()):
     _GameState.__init__(self, board)
     self.board = board
     self._fen_dic = {}
     self._board_occ = 0
Пример #4
0
 def new_game(self):
     self.board = CrazyhouseBoard()
     self._fen_dic = {}
Пример #5
0
class GameState(_GameState):
    def __init__(self, board=CrazyhouseBoard()):
        _GameState.__init__(self, board)
        self.board = board
        self._fen_dic = {}
        self._board_occ = 0

    def apply_move(self, move: chess.Move):  # , remember_state=False):
        # apply the move on the board
        self.board.push(move)

        # if remember_state is True:
        #    self._remember_board_state()

    def get_state_planes(self):
        return board_to_planes(self.board,
                               board_occ=self._board_occ,
                               normalize=True)
        # return np.random.random((34, 8, 8))

    def get_pythonchess_board(self):
        return self.board

    def is_draw(self):
        # check if you can claim a draw - its assumed that the draw is always claimed
        return self.can_claim_threefold_repetition(
        ) or self.board.can_claim_fifty_moves()
        # return self.board.can_claim_draw()

    def can_claim_threefold_repetition(self):
        """
        Custom implementation for threefold-repetition check which uses the board_occ variable.
        :return: True if claim is legal else False
        """
        return self._board_occ >= 2

    def is_won(self):
        # only a is_won() and no is_lost() function is needed because the game is over
        #  after the player found checkmate successfully
        return self.board.is_checkmate()

    def get_legal_moves(self):
        # return list(self.board.legal_moves)
        legal_moves = []
        for mv in self.board.generate_legal_moves():
            legal_moves.append(mv)
        return legal_moves

    def is_white_to_move(self):
        return self.board.turn

    def __str__(self):
        return self.board.fen()

    def new_game(self):
        self.board = CrazyhouseBoard()
        self._fen_dic = {}

    def set_fen(self, fen, remember_state=True):
        self.board.set_fen(fen)

        # if remember_state is True:
        #    self._remember_board_state()

    # def _remember_board_state(self):
    # calculate the transposition key
    #    transposition_key = self.get_transposition_key()
    # update the number of board occurrences
    # self._board_occ = self._transposition_table[transposition_key]
    # increase the counter for this transposition key
    #    self._transposition_table.update((transposition_key,))

    def is_check(self):
        return self.board.is_check()

    def are_pocket_empty(self):
        """
        Checks wether at least one player has a piece available in their pocket
        :return:
        """

        return len(self.board.pockets[chess.WHITE]) == 0 and len(
            self.board.pockets[chess.BLACK]) == 0
Пример #6
0
def planes_to_board(planes, normalized_input=False, mode=MODE_CRAZYHOUSE):
    """
    Converts a board in plane representation to the python chess board representation
    see get_planes_of_board() for input encoding description

    :param planes: Input plane representation
    :param normalized_input: True if the input has been normalized to range[0., 1.]
    :param mode: 0 - MODE_CRAZYHOUSE: Crazyhouse only specification.
                 (Visit variants.crazyhouse.input_representation for detailed documentation)
                 1 - MODE_LICHESS: Specification for all supported variants on lichess.org
                 (Visit variants.lichess.input_representation for detailed documentation)
                 2 - MODE_CHESS: Specification for chess only with chess960 support
                 (Visit variants.chess.input_representation for detailed documentation)
    :return: python chess board object
    """
    if mode not in MODES:
        raise ValueError(f"Given {mode} is not {MODES}.")

    # extract the maps for the board position
    planes_pos = planes[:NB_CHANNELS_POS]
    # extract the last maps which for the constant values
    end_board_idx = NB_CHANNELS_POS + NB_CHANNELS_CONST
    planes_const = planes[NB_CHANNELS_POS:end_board_idx]

    if mode == MODE_LICHESS:
        # extract the variants definition section
        planes_variants = planes[end_board_idx:end_board_idx +
                                 NB_CHANNELS_VARIANTS]

        # setup new initial board
        is960 = planes_variants[CHANNEL_MAPPING_VARIANTS["is960"], 0, 0] == 1

        # iterate through all available variants
        board = None
        for variant in VARIANT_MAPPING_BOARDS:  # exclude "is960"
            if planes_variants[CHANNEL_MAPPING_VARIANTS[variant], 0, 0] == 1:
                board = VARIANT_MAPPING_BOARDS[variant](chess960=is960)
                break

        if board is None:
            raise Exception(
                "No chess variant was recognized in your given input planes")
    elif mode == MODE_CRAZYHOUSE:
        board = CrazyhouseBoard()
    else:  # mode = MODE_CHESS:
        # extract the variants definition section
        plane_960 = planes[-1:]
        is960 = plane_960[CHANNEL_MAPPING_VARIANTS["is960"], 0, 0] == 1
        board = chess.Board(chess960=is960)

    # clear the full board (the pieces will be set later)
    board.clear()

    # iterate over all piece types
    for idx, piece in enumerate(PIECES):
        # iterate over all fields and set the current piece type
        for row in range(BOARD_HEIGHT):
            for col in range(BOARD_WIDTH):
                # check if there's a piece at the current position
                if planes_pos[idx, row, col] == 1:
                    # check if the piece was promoted
                    promoted = False
                    if mode == MODE_CRAZYHOUSE or mode == MODE_LICHESS:
                        # promoted pieces are not defined in the chess plane representation
                        channel = CHANNEL_MAPPING_POS["promo"]
                        if planes_pos[channel, row,
                                      col] == 1 or planes_pos[channel + 1, row,
                                                              col] == 1:
                            promoted = True

                    board.set_piece_at(
                        square=get_board_position_index(row, col),
                        piece=chess.Piece.from_symbol(piece),
                        promoted=promoted,
                    )

    # (I) Fill in the Repetition Data
    # check how often the position has already occurred in the game
    # TODO: Find a way to set this on the board state
    # -> apparently this isn't possible because it's also not available in the board uci representation

    # ch = channel_mapping['repetitions']

    # Fill in the Prisoners / Pocket Pieces
    if mode == MODE_CRAZYHOUSE or board.uci_variant == "crazyhouse":
        # iterate over all pieces except the king
        for p_type in chess.PIECE_TYPES[:-1]:
            # p_type -1 because p_type starts with 1
            channel = CHANNEL_MAPPING_POS["prisoners"] + p_type - 1

            # the full board is filled with the same value
            # it's sufficient to take only the first value
            nb_prisoners = planes_pos[channel, 0, 0]

            # add prisoners for the current player
            # the whole board is set with the same entry, we can just take the first one
            if normalized_input is True:
                nb_prisoners *= MAX_NB_PRISONERS
                nb_prisoners = int(round(nb_prisoners))

            for _ in range(nb_prisoners):
                board.pockets[chess.WHITE].add(p_type)

            # add prisoners for the opponent
            nb_prisoners = planes_pos[channel + 5, 0, 0]
            if normalized_input is True:
                nb_prisoners *= MAX_NB_PRISONERS
                nb_prisoners = int(round(nb_prisoners))

            for _ in range(nb_prisoners):
                board.pockets[chess.BLACK].add(p_type)

    # (I.5) En Passant Square
    # mark the square where an en-passant capture is possible
    channel = CHANNEL_MAPPING_POS["ep_square"]
    ep_square = np.argmax(planes_pos[channel])
    if ep_square != 0:
        # if no entry 'one' exists, index 0 will be returned
        board.ep_square = ep_square

    # (II) Constant Value Inputs
    # (II.1) Total Move Count
    channel = CHANNEL_MAPPING_CONST["total_mv_cnt"]
    total_mv_cnt = planes_const[channel, 0, 0]

    if normalized_input is True:
        total_mv_cnt *= MAX_NB_MOVES
        total_mv_cnt = int(round(total_mv_cnt))

    board.fullmove_number = total_mv_cnt

    # (II.2) Castling Rights
    channel = CHANNEL_MAPPING_CONST["castling"]

    # reset the castling_rights for initialization
    # set to 0, previously called chess.BB_VOID for chess version of 0.23.X and chess.BB_EMPTY for versions > 0.27.X
    board.castling_rights = 0

    # WHITE
    # check for King Side Castling
    # White can castle with the h1 rook

    # add castling option by modifying the castling fen
    castling_fen = ""
    # check for King Side Castling
    if planes_const[channel, 0, 0] == 1:
        castling_fen += "K"
        board.castling_rights |= chess.BB_H1
    # check for Queen Side Castling
    if planes_const[channel + 1, 0, 0] == 1:
        castling_fen += "Q"
        board.castling_rights |= chess.BB_A1

    # BLACK
    # check for King Side Castling
    if planes_const[channel + 2, 0, 0] == 1:
        castling_fen += "k"
        board.castling_rights |= chess.BB_H8
    # check for Queen Side Castling
    if planes_const[channel + 3, 0, 0] == 1:
        castling_fen += "q"
        board.castling_rights |= chess.BB_A8

    # configure the castling rights
    if castling_fen:
        board.set_castling_fen(castling_fen)

    # (II.3) No Progress Count
    channel = CHANNEL_MAPPING_CONST["no_progress_cnt"]
    no_progress_cnt = planes_const[channel, 0, 0]
    if normalized_input is True:
        no_progress_cnt *= MAX_NB_NO_PROGRESS
        no_progress_cnt = int(round(no_progress_cnt))

    board.halfmove_clock = no_progress_cnt

    # set the number of remaining checks (only needed for 3check) and might be mirrored later
    if mode == MODE_LICHESS:
        channel = CHANNEL_MAPPING_CONST["remaining_checks"]
        if planes_const[channel, 0, 0] == 1:
            board.remaining_checks[chess.WHITE] -= 1
        if planes_const[channel + 1, 0, 0] == 1:
            board.remaining_checks[chess.WHITE] -= 1
        if planes_const[channel + 2, 0, 0] == 1:
            board.remaining_checks[chess.BLACK] -= 1
        if planes_const[channel + 3, 0, 0] == 1:
            board.remaining_checks[chess.BLACK] -= 1

    # (II.4) Color
    channel = CHANNEL_MAPPING_CONST["color"]

    if planes_const[channel, 0, 0] == 1:
        board.board_turn = chess.WHITE
    else:
        board = board.mirror()
        board.board_turn = chess.BLACK

    return board
Пример #7
0
 def __init__(self):
     self.boards = {
         FIRST_BOARD: CrazyhouseBoard(),
         SECOND_BOARD: CrazyhouseBoard(),
     }
     self.reset()
Пример #8
0
def planes_to_board(planes, normalized_input=False):
    """
    Converts a board in plane representation to the python chess board representation
    see get_planes_of_board() for input encoding description

    :param planes: Input plane representation
    :param normalized_input: True if the input has been normalized to range[0., 1.]
    :return: python chess board object
    """

    # setup new initial board
    board = CrazyhouseBoard()
    board.clear_board()

    # extract the maps for the board position
    mat_pos = planes[:NB_CHANNELS_POS]
    # extract the last maps which for the constant values
    mat_const = planes[-NB_CHANNELS_CONST:]
    # iterate over all piece types
    for idx, piece in enumerate(PIECES):
        # iterate over all fields and set the current piece type
        for row in range(BOARD_HEIGHT):
            for col in range(BOARD_WIDTH):
                # check if there's a piece at the current position
                if mat_pos[idx, row, col] == 1:
                    # check if the piece was promoted
                    promoted = False
                    ch = CHANNEL_MAPPING_POS['promo']
                    if mat_pos[ch, row, col] == 1 or mat_pos[ch + 1, row,
                                                             col] == 1:
                        promoted = True

                    board.set_piece_at(square=get_board_position_index(
                        row, col),
                                       piece=chess.Piece.from_symbol(piece),
                                       promoted=promoted)

    # (I) Fill in the Repetition Data
    # check how often the position has already occurred in the game
    # TODO: Find a way to set this on the board state
    # -> apparently this isn't possible because it's also not available in the board uci representation

    # ch = channel_mapping['repetitions']

    # Fill in the Prisoners / Pocket Pieces

    # iterate over all pieces except the king
    for p_type in chess.PIECE_TYPES[:-1]:
        # p_type -1 because p_type starts with 1
        ch = CHANNEL_MAPPING_POS['prisoners'] + p_type - 1

        # the full board is filled with the same value
        # it's sufficient to take only the first value
        nb_prisoners = mat_pos[ch, 0, 0]

        # add prisoners for the current player
        # the whole board is set with the same entry, we can just take the first one
        if normalized_input is True:
            nb_prisoners *= MAX_NB_PRISONERS
            nb_prisoners = int(round(nb_prisoners))

        for i in range(nb_prisoners):
            board.pockets[chess.WHITE].add(p_type)

        # add prisoners for the opponent
        nb_prisoners = mat_pos[ch + 5, 0, 0]
        if normalized_input is True:
            nb_prisoners *= MAX_NB_PRISONERS
            nb_prisoners = int(round(nb_prisoners))

        for i in range(nb_prisoners):
            board.pockets[chess.BLACK].add(p_type)

    # (I.5) En Passant Square
    # mark the square where an en-passant capture is possible
    ch = CHANNEL_MAPPING_POS['ep_square']
    ep_square = np.argmax(mat_pos[ch])
    if ep_square != 0:
        # if no entry 'one' exists, index 0 will be returned
        board.ep_square = ep_square

    # (II) Constant Value Inputs
    # (II.1) Total Move Count
    ch = CHANNEL_MAPPING_CONST['total_mv_cnt']
    total_mv_cnt = mat_const[ch, 0, 0]

    if normalized_input is True:
        total_mv_cnt *= MAX_NB_MOVES
        total_mv_cnt = int(round(total_mv_cnt))

    board.fullmove_number = total_mv_cnt

    # (II.2) Castling Rights
    ch = CHANNEL_MAPPING_CONST['castling']

    # reset the castling_rights for initialization
    board.castling_rights = chess.BB_VOID

    # WHITE
    # check for King Side Castling
    # White can castle with the h1 rook

    # add castling option by applying logical-OR operation
    if mat_const[ch, 0, 0] == 1:
        board.castling_rights |= chess.BB_H1
    # check for Queen Side Castling
    if mat_const[ch + 1, 0, 0] == 1:
        board.castling_rights |= chess.BB_A1

    # BLACK
    # check for King Side Castling
    if mat_const[ch + 2, 0, 0] == 1:
        board.castling_rights |= chess.BB_H8
    # check for Queen Side Castling
    if mat_const[ch + 3, 0, 0] == 1:
        board.castling_rights |= chess.BB_A8

    # (II.3) No Progress Count
    ch = CHANNEL_MAPPING_CONST['no_progress_cnt']
    no_progress_cnt = mat_const[ch, 0, 0]
    if normalized_input is True:
        no_progress_cnt *= MAX_NB_NO_PROGRESS
        no_progress_cnt = int(round(no_progress_cnt))

    board.halfmove_clock = no_progress_cnt

    # (II.4) Color
    ch = CHANNEL_MAPPING_CONST['color']

    if mat_const[ch, 0, 0] == 1:
        board.board_turn = chess.WHITE
    else:
        board = board.mirror()
        board.board_turn = chess.BLACK

    return board
Пример #9
0
    def test_move_planes_round_trip_given_drop_expect_round_trip(self):
        board = CrazyhouseBoard()

        move = construct_move_from_positions([5, 4], [5, 4], drop=2)
        assert_round_trip(self, board, move, comment="dropping (bishop)")
Пример #10
0
    def test_move_planes_round_trip_given_pawn_move_expect_round_trip(self):
        board = CrazyhouseBoard()

        move = construct_move_from_positions([1, 4], [2, 4])
        assert_round_trip(self, board, move, comment="pawn move")
Пример #11
0
 def new_game(self):
     """ Create a new board on the starting position"""
     self.board = CrazyhouseBoard()
     self._fen_dic = {}
Пример #12
0
class GameState(AbsGameState):
    """File to group everything to recognize the game state"""
    def __init__(self, board=CrazyhouseBoard()):
        AbsGameState.__init__(self, board)
        self.board = board
        self._fen_dic = {}
        self._board_occ = 0

    def apply_move(self, move: chess.Move):  # , remember_state=False):
        """ Apply the move on the board"""
        self.board.push(move)

        # if remember_state is True:
        #    self._remember_board_state()

    def get_state_planes(self):
        """Transform the current board state to a plane"""
        return board_to_planes(self.board,
                               board_occ=self._board_occ,
                               normalize=True)
        # return np.random.random((34, 8, 8))

    def get_pythonchess_board(self):
        """ Get the board by calling a method"""
        return self.board

    def is_draw(self):
        """ Check if you can claim a draw - its assumed that the draw is always claimed """
        return self.can_claim_threefold_repetition(
        ) or self.board.can_claim_fifty_moves()
        # return self.board.can_claim_draw()

    def can_claim_threefold_repetition(self):
        """
        Custom implementation for threefold-repetition check which uses the board_occ variable.
        :return: True if claim is legal else False
        """
        return self._board_occ >= 2

    def is_won(self):
        """ Check if you can claim the win by checkmate"""
        # only a is_won() and no is_lost() function is needed because the game is over
        return self.board.is_checkmate(
        )  # after the player found checkmate successfully

    def get_legal_moves(self):
        """ Returns the legal moves based on current board state"""
        return [*self.board.legal_moves
                ]  # is same as list(self.board.legal_moves)
        # legal_moves = []
        # for move in self.board.generate_legal_moves():
        #     legal_moves.append(move)
        # return legal_moves

    def is_white_to_move(self):
        """ Returns true if its whites turn"""
        return self.board.turn

    def __str__(self):
        return self.board.fen()

    def new_game(self):
        """ Create a new board on the starting position"""
        self.board = CrazyhouseBoard()
        self._fen_dic = {}

    def set_fen(self, fen):  # , remember_state=True
        """ Returns the fen of the current state"""
        self.board.set_fen(fen)

        # if remember_state is True:
        #    self._remember_board_state()

    # def _remember_board_state(self):
    # calculate the transposition key
    #    transposition_key = self.get_transposition_key()
    # update the number of board occurrences
    # self._board_occ = self._transposition_table[transposition_key]
    # increase the counter for this transposition key
    #    self._transposition_table.update((transposition_key,))

    def is_check(self):
        """ Check if the king of the player of the turn is in check"""
        return self.board.is_check()

    def are_pocket_empty(self):
        """ Checks if at least one player has a piece available in their pocket """
        return not self.board.pockets[chess.WHITE] and not self.board.pockets[
            chess.BLACK]