Ejemplo n.º 1
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
Ejemplo n.º 2
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