Example #1
0
def get_board_state(board: chess.Board) -> np.ndarray:
    """Function adapted from chess.Board.__str__() to generate a numpy array representing the board
    state"""
    # 6 pieces * 2 colors + 2 for en passant and castling. 8*8 fields of the board.
    builder = np.zeros([12 + 2, 8 * 8])
    for square in SQUARES_180:

        piece = board.piece_at(square)

        if piece:
            piece_int = PIECE_INT_LOOKUP[piece.symbol()]
            builder[piece_int, square] = 1

    builder = builder.reshape([14, 8, 8])

    if board.has_legal_en_passant():
        set_en_passant(board=board, builder=builder)

    # As the SQUARES_180 is flipped I need to flip it back
    builder = np.flip(builder, 1)

    if board.has_castling_rights(color=True) or board.has_castling_rights(color=False):
        castling_ints = get_castling_ints(castling_fen=board.castling_shredder_fen())
        builder[13, 0, castling_ints] = 1

    return builder
Example #2
0
def board_2_tensor(board: chess.Board) -> torch.tensor:
    nums = np.empty(13, dtype=np.uint64)
    for i in range(6):
        nums[i] = int(board.pieces(i + 1, chess.WHITE))
        nums[i + 6] = int(board.pieces(i + 1, chess.BLACK))

    nums[12] = 1 << board.ep_square if board.has_legal_en_passant() else 0

    bits = np.unpackbits(nums.view(np.uint8))
    return torch.tensor(bits).float()
Example #3
0
def board_to_planes(board: chess.Board, normalize=True, last_moves=None):
    """
    Gets the plane representation of a given board state.

    ## Chess:

    Feature | Planes

    --- | ---

    P1 piece | 6 (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING)

    P2 piece | 6 (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING)

    En-passant square | 1 (Binary map indicating the square where en-passant capture is possible)

    ---
    13 planes

    * * *

    P1 castling | 2 (One if castling is possible, else zero)

    P2 castling | 2 (One if castling is possible, else zero)

    ---
    4 planes

    * * *

    Last 8 moves | 16 (indicated by origin and destination square, the most recent move is described by first 2 planes)

    ---
    16 planes

    * * *

    is960 = | 1 (boolean, 1 when active)

    ---
    1 plane

    ---

    P1 pieces | 1 | A grouped mask of all WHITE pieces |
    P2 pieces | 1 | A grouped mask of all BLACK pieces |
    Checkerboard | 1 | A chess board pattern |
    P1 Material Diff | 5 | (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN), normalized with 8, + means positive, - means negative |
    Opposite Color Bishops | 1 | Indicates if they are only two bishops and the bishops are opposite color |
    Checkers | 1 | Indicates all pieces giving check |
    Checking Moves | 2 | Indicates all checking moves (from sq, to sq) |
    Mobility | 1 | Indicates the number of legal moves
    P1 Material Count | 5 | (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN), normalized with 8 |
    ---
    18 planes

    The total number of planes is calculated as follows:
    # --------------
    13 + 4 + 2 + 1 + 18
    Total: 38 planes

    :param board: Board handle (Python-chess object)
    :param normalize: True if the inputs shall be normalized to the range [0.-1.]
    :param last_moves: List of last last moves. The most recent move is the first entry.
    :return: planes - the plane representation of the current board state
    """

    # return the plane representation of the given board
    # return variants.board_to_planes(board, board_occ, normalize, mode=MODE_CHESS)

    planes = np.zeros((NB_CHANNELS_TOTAL, BOARD_HEIGHT, BOARD_WIDTH))

    # channel will be incremented by 1 at first plane
    channel = 0
    me = board.turn
    you = not board.turn
    colors = [me, you]

    # mirror all bitboard entries for the black player
    mirror = board.turn == chess.BLACK

    assert (channel == CHANNEL_PIECES)
    # Fill in the piece positions
    # Channel: 0 - 11
    # Iterate over both color starting with WHITE
    for color in colors:
        # the PIECE_TYPE is an integer list in python-chess
        for piece_type in chess.PIECE_TYPES:
            # iterate over the piece mask and receive every position square of it
            for pos in board.pieces(piece_type, color):
                row, col = get_row_col(pos, mirror=mirror)
                # set the bit at the right position
                planes[channel, row, col] = 1
            channel += 1

    # Channel: 12
    # En Passant Square
    assert (channel == CHANNEL_EN_PASSANT)
    if board.ep_square and board.has_legal_en_passant():  # is not None:
        row, col = get_row_col(board.ep_square, mirror=mirror)
        planes[channel, row, col] = 1
    channel += 1

    # Channel: 13 - 16
    assert (channel == CHANNEL_CASTLING)
    for color in colors:
        # check for King Side Castling
        if board.has_kingside_castling_rights(color):
            planes[channel, :, :] = 1
        channel += 1
        # check for Queen Side Castling
        if board.has_queenside_castling_rights(color):
            planes[channel, :, :] = 1
        channel += 1

    # Channel: 17 - 18
    assert (channel == CHANNEL_LAST_MOVES)
    # Last 8 moves
    if last_moves:
        assert (len(last_moves) == NB_LAST_MOVES)
        for move in last_moves:
            if move:
                from_row, from_col = get_row_col(move.from_square,
                                                 mirror=mirror)
                to_row, to_col = get_row_col(move.to_square, mirror=mirror)
                planes[channel, from_row, from_col] = 1
                channel += 1
                planes[channel, to_row, to_col] = 1
                channel += 1
            else:
                channel += 2
    else:
        channel += NB_LAST_MOVES * NB_CHANNELS_PER_HISTORY_ITEM

    # Channel: 19
    # Chess960
    assert (channel == CHANNEL_IS_960)
    if board.chess960:
        planes[channel + 1, :, :] = 1
    channel += 1

    # Channel: 20 - 21
    # All white pieces and black pieces in a single map
    assert (channel == CHANNEL_PIECE_MASK)
    for color in colors:
        # the PIECE_TYPE is an integer list in python-chess
        for piece_type in chess.PIECE_TYPES:
            # iterate over the piece mask and receive every position square of it
            for pos in board.pieces(piece_type, color):
                row, col = get_row_col(pos, mirror=mirror)
                # set the bit at the right position
                planes[channel, row, col] = 1
        channel += 1

    # Channel: 22
    # Checkerboard
    assert (channel == CHANNEL_CHECKERBOARD)
    planes[channel, :, :] = checkerboard()
    channel += 1

    # Channel: 23 - 27
    # Relative material difference (negative if less pieces than opponent and positive if more)
    # iterate over all pieces except the king
    assert (channel == CHANNEL_MATERIAL_DIFF)
    for piece_type in chess.PIECE_TYPES[:-1]:
        material_count = len(board.pieces(piece_type, me)) - len(
            board.pieces(piece_type, you))
        planes[
            channel, :, :] = material_count / NORMALIZE_PIECE_NUMBER if normalize else material_count
        channel += 1

    # Channel: 28
    # Opposite color bishops
    assert (channel == CHANNEL_OPP_BISHOPS)
    if opposite_colored_bishops(board):
        planes[channel, :, :] = 1
    channel += 1

    # Channel: 29
    # Checkers
    assert channel == CHANNEL_CHECKERS
    board_checkers = checkers(board)
    if board_checkers:
        # iterate over the piece mask and receive every position square of it
        for pos in chess.SquareSet(board_checkers):
            row, col = get_row_col(pos, mirror=mirror)
            # set the bit at the right position
            planes[channel, row, col] = 1
    channel += 1

    my_legal_moves = list(board.legal_moves)

    # Channel: 30 - 31
    assert channel == CHANNEL_CHECK_MOVES
    for move in my_legal_moves:
        if gives_check(board, move):
            row, col = get_row_col(move.from_square, mirror=mirror)
            planes[channel, row, col] = 1
            row, col = get_row_col(move.to_square, mirror=mirror)
            planes[channel + 1, row, col] = 1
    channel += 2

    # Channel: 32
    # Mobility
    assert (channel == CHANNEL_MOBILITY)
    planes[channel, :, :] = len(
        my_legal_moves) / NORMALIZE_MOBILITY if normalize else len(
            my_legal_moves)
    channel += 1

    # Channel: 33
    # Material
    assert (channel == CHANNEL_MATERIAL_COUNT)
    for piece_type in chess.PIECE_TYPES[:-1]:
        material_count = len(board.pieces(piece_type, me))
        planes[
            channel, :, :] = material_count / NORMALIZE_PIECE_NUMBER if normalize else material_count
        channel += 1

    assert channel == NB_CHANNELS_TOTAL
    return planes
Example #4
0
def board_to_planes(board: chess.Board,
                    board_occ,
                    normalize=True,
                    last_moves=None):
    """
    Gets the plane representation of a given board state.

    ## Chess:

    Feature | Planes

    --- | ---

    P1 piece | 6 (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING)

    P2 piece | 6 (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING)

    Repetitions | 2 (two planes (full zeros/ones) indicating how often the board positions has occurred)

    En-passant square | 1 (Binary map indicating the square where en-passant capture is possible)

    ---
    15 planes

    * * *

    P1 castling | 2 (One if castling is possible, else zero)

    P2 castling | 2 (One if castling is possible, else zero)

    No-progress count | 1 (Setting the no progress counter as integer values, (described by uci halfmoves format)

    ---
    5 planes

    * * *

    Last 8 moves | 16 (indicated by origin and destination square, the most recent move is described by first 2 planes)

    ---
    16 planes

    * * *

    is960 = | 1 (boolean, 1 when active)

    ---
    1 plane

    ---

    P1 pieces | 1 | A grouped mask of all WHITE pieces |
    P2 pieces | 1 | A grouped mask of all BLACK pieces |
    Checkerboard | 1 | A chess board pattern |
    P1 Material Diff | 5 | (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN), normalized with 8, + means positive, - means negative |
    Opposite Color Bishops | 1 | Indicates if they are only two bishops and the bishops are opposite color |
    Checkers | 1 | Indicates all pieces giving check |
    P1 Material Count | 5 | (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN), normalized with 8 |
    ---
    15 planes

    The total number of planes is calculated as follows:
    # --------------
    15 + 5 + 16 + 1 + 15
    Total: 52 planes

    :param board: Board handle (Python-chess object)
    :param board_occ: Number of board occurences
    :param normalize: True if the inputs shall be normalized to the range [0.-1.]
    :param last_moves: List of last last moves. The most recent move is the first entry.
    :return: planes - the plane representation of the current board state
    """

    # return the plane representation of the given board
    # return variants.board_to_planes(board, board_occ, normalize, mode=MODE_CHESS)
    planes = np.zeros((NB_CHANNELS_TOTAL, BOARD_HEIGHT, BOARD_WIDTH))

    # channel will be incremented by 1 at first plane
    channel = 0
    me = board.turn
    you = not board.turn
    colors = [me, you]

    # mirror all bitboard entries for the black player
    mirror = board.turn == chess.BLACK

    assert channel == CHANNEL_PIECES
    # Fill in the piece positions
    # Channel: 0 - 11
    # Iterate over both color starting with WHITE
    for color in colors:
        # the PIECE_TYPE is an integer list in python-chess
        for piece_type in chess.PIECE_TYPES:
            # iterate over the piece mask and receive every position square of it
            for pos in board.pieces(piece_type, color):
                row, col = get_row_col(pos, mirror=mirror)
                # set the bit at the right position
                planes[channel, row, col] = 1
            channel += 1

    assert channel == CHANNEL_REPETITION
    # Channel: 12 - 13
    # set how often the position has already occurred in the game (default 0 times)
    # this is used to check for claiming the 3 fold repetition rule
    if board_occ >= 1:
        planes[channel, :, :] = 1
        if board_occ >= 2:
            planes[channel + 1, :, :] = 1
    channel += 2

    # Channel: 14
    # En Passant Square
    assert channel == CHANNEL_EN_PASSANT
    if board.ep_square and board.has_legal_en_passant():  # is not None:
        row, col = get_row_col(board.ep_square, mirror=mirror)
        planes[channel, row, col] = 1
    channel += 1

    # Channel: 15 - 18
    assert channel == CHANNEL_CASTLING
    for color in colors:
        # check for King Side Castling
        if board.has_kingside_castling_rights(color):
            planes[channel, :, :] = 1
        channel += 1
        # check for Queen Side Castling
        if board.has_queenside_castling_rights(color):
            planes[channel, :, :] = 1
        channel += 1

    # Channel: 19
    # (IV.4) No Progress Count
    # define a no 'progress' counter
    # it gets incremented by 1 each move
    # however, whenever a piece gets dropped, a piece is captured or a pawn is moved, it is reset to 0
    # halfmove_clock is an official metric in fen notation
    #  -> see: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
    # check how often the position has already occurred in the game
    assert channel == CHANNEL_NO_PROGRESS
    planes[
        channel, :, :] = board.halfmove_clock / NORMALIZE_50_MOVE_RULE if normalize else board.halfmove_clock
    channel += 1

    # Channel: 20 - 35
    assert channel == CHANNEL_LAST_MOVES
    # Last 8 moves
    if last_moves:
        assert (len(last_moves) == NB_LAST_MOVES)
        for move in last_moves:
            if move:
                from_row, from_col = get_row_col(move.from_square,
                                                 mirror=mirror)
                to_row, to_col = get_row_col(move.to_square, mirror=mirror)
                planes[channel, from_row, from_col] = 1
                channel += 1
                planes[channel, to_row, to_col] = 1
                channel += 1
            else:
                channel += 2
    else:
        channel += NB_LAST_MOVES * NB_CHANNELS_PER_HISTORY_ITEM

    # Channel: 36
    # Chess960
    assert channel == CHANNEL_IS_960
    if board.chess960:
        planes[channel + 1, :, :] = 1
    channel += 1

    # Channel: 37 - 38
    # All white pieces and black pieces in a single map
    assert channel == CHANNEL_PIECE_MASK
    for color in colors:
        # the PIECE_TYPE is an integer list in python-chess
        for piece_type in chess.PIECE_TYPES:
            # iterate over the piece mask and receive every position square of it
            for pos in board.pieces(piece_type, color):
                row, col = get_row_col(pos, mirror=mirror)
                # set the bit at the right position
                planes[channel, row, col] = 1
        channel += 1

    # Channel: 39
    # Checkerboard
    assert (channel == CHANNEL_CHECKERBOARD)
    planes[channel, :, :] = checkerboard()
    channel += 1

    # Channel: 40 - 44
    # Relative material difference (negative if less pieces than opponent and positive if more)
    # iterate over all pieces except the king
    assert channel == CHANNEL_MATERIAL_DIFF
    for piece_type in chess.PIECE_TYPES[:-1]:
        material_count = len(board.pieces(piece_type, me)) - len(
            board.pieces(piece_type, you))
        planes[
            channel, :, :] = material_count / NORMALIZE_PIECE_NUMBER if normalize else material_count
        channel += 1

    # Channel: 45
    # Opposite color bishops
    assert (channel == CHANNEL_OPP_BISHOPS)
    if opposite_colored_bishops(board):
        planes[channel, :, :] = 1
    channel += 1

    # Channel: 46
    # Checkers
    assert channel == CHANNEL_CHECKERS
    board_checkers = checkers(board)
    if board_checkers:
        # iterate over the piece mask and receive every position square of it
        for pos in chess.SquareSet(board_checkers):
            row, col = get_row_col(pos, mirror=mirror)
            # set the bit at the right position
            planes[channel, row, col] = 1
    channel += 1

    # Channel: 47 - 51
    # Material
    assert channel == CHANNEL_MATERIAL_COUNT
    for piece_type in chess.PIECE_TYPES[:-1]:
        material_count = len(board.pieces(piece_type, me))
        planes[
            channel, :, :] = material_count / NORMALIZE_PIECE_NUMBER if normalize else material_count
        channel += 1

    assert channel == NB_CHANNELS_TOTAL

    return planes