Exemplo n.º 1
0
def get_board_bits(board: chess.Board):
    out = [board.pieces(p, chess.WHITE).tolist() for p in chess.PIECE_TYPES]
    out += [board.pieces(p, chess.BLACK).tolist() for p in chess.PIECE_TYPES]
    out = list(reduce(lambda a, b: a + b, out, []))
    out += [
        board.has_kingside_castling_rights(chess.WHITE),
        board.has_queenside_castling_rights(chess.WHITE),
        board.has_kingside_castling_rights(chess.BLACK),
        board.has_queenside_castling_rights(chess.BLACK)
    ]
    return list(map(int, out))
Exemplo n.º 2
0
    def hash_castling(self, board: chess.Board) -> int:
        zobrist_hash = 0

        # Hash in the castling flags.
        if board.has_kingside_castling_rights(chess.WHITE):
            zobrist_hash ^= self.array[768]
        if board.has_queenside_castling_rights(chess.WHITE):
            zobrist_hash ^= self.array[768 + 1]
        if board.has_kingside_castling_rights(chess.BLACK):
            zobrist_hash ^= self.array[768 + 2]
        if board.has_queenside_castling_rights(chess.BLACK):
            zobrist_hash ^= self.array[768 + 3]

        return zobrist_hash
Exemplo n.º 3
0
def get_board_score(board: chess.Board, color: chess.Color) -> int:

    total = 0

    pawns = board.pieces(chess.PAWN, color)
    bishops = board.pieces(chess.BISHOP, color)
    knights = board.pieces(chess.KNIGHT, color)
    queens = board.pieces(chess.QUEEN, color)
    rooks = board.pieces(chess.ROOK, color)

    for _ in pawns:
        total += 10
    for _ in bishops:
        total += 30
    for _ in knights:
        total += 30
    for _ in queens:
        total += 90
    for _ in rooks:
        total += 50

    if (board.has_kingside_castling_rights(color)):
        total += 1

    if (board.has_queenside_castling_rights(color)):
        total += 1

    return total
Exemplo n.º 4
0
def get_additional_features(board: chess.Board):
    '''
    Get additional features like color, total move count, etc from a board state
    :param board: chess.Board
    :param env: ChessEnv environment
    :return: additional features
    '''

    color = board.turn
    total_moves = board.fullmove_number
    w_castling = [board.has_kingside_castling_rights(chess.WHITE),
                  board.has_queenside_castling_rights(chess.WHITE)]
    b_castling = [board.has_kingside_castling_rights(chess.BLACK),
                  board.has_queenside_castling_rights(chess.BLACK)]
    no_progress_count = int(board.halfmove_clock / 2)

    return color, total_moves, w_castling, b_castling, no_progress_count
Exemplo n.º 5
0
    def write_json(self, board: chess.Board):
        """
        Writes all of the board info in json
        """

        best_move = self.get_best_move(board)

        output = OrderedDict([
            ('fen', board.fen()),
            ('fullmoveNumber', board.fullmove_number),
            ('result', board.result()),
            ('isGameOver', board.is_game_over()),
            ('isCheckmate', board.is_checkmate()),
            ('isStalemate', board.is_stalemate()),
            ('isInsufficientMaterial', board.is_insufficient_material()),
            ('isSeventyfiveMoves', board.is_seventyfive_moves()),
            ('isFivefoldRepetition', board.is_fivefold_repetition()),
            ('white',
             OrderedDict([
                 ('hasKingsideCastlingRights',
                  board.has_kingside_castling_rights(chess.WHITE)),
                 ('hasQueensideCastlingRights',
                  board.has_queenside_castling_rights(chess.WHITE)),
             ])),
            ('black',
             OrderedDict([
                 ('hasKingsideCastlingRights',
                  board.has_kingside_castling_rights(chess.BLACK)),
                 ('hasQueensideCastlingRights',
                  board.has_queenside_castling_rights(chess.BLACK)),
             ])),
            ('turn',
             OrderedDict([
                 ('color', 'white' if board.turn is chess.WHITE else 'black'),
                 ('isInCheck', board.is_check()),
                 ('bestMove', best_move),
                 ('legalMoves', [move.uci() for move in board.legal_moves]),
                 ('canClaimDraw', board.can_claim_draw()),
                 ('canClaimFiftyMoves', board.can_claim_fifty_moves()),
                 ('canClaimThreefoldRepetition',
                  board.can_claim_threefold_repetition()),
             ])),
        ])

        self.finish(output)
Exemplo n.º 6
0
    def observation(self, board: chess.Board) -> np.array:
        """Converts chess.Board observations instance to numpy arrays.
        
        Note: 
            This method is called by gym.ObservationWrapper to transform the 
            observations returned by the wrapped env's step() method. In 
            particular, calling this method will add the given board position
            to the history. Do NOT call this method manually to recover a 
            previous observation returned by step().
        """

        self._history.push(board)

        history = self._history.view(orientation=board.turn)

        meta = np.zeros(shape=(8, 8, 7), dtype=np.int)

        # Active player color
        meta[:, :, 0] = int(board.turn)

        # Total move count
        meta[:, :, 1] = board.fullmove_number

        # Active player castling rights
        meta[:, :, 2] = board.has_kingside_castling_rights(board.turn)
        meta[:, :, 3] = board.has_queenside_castling_rights(board.turn)

        # Opponent player castling rights
        meta[:, :, 4] = board.has_kingside_castling_rights(not board.turn)
        meta[:, :, 5] = board.has_queenside_castling_rights(not board.turn)

        # No-progress counter
        meta[:, :, 6] = board.halfmove_clock

        observation = np.concatenate([history, meta], axis=-1)
        return observation
def is_illegal_castle(board: chess.Board, move: chess.Move) -> bool:
    if not board.is_castling(move):
        return False

    # illegal without kingside rights
    if board.is_kingside_castling(move) and not board.has_kingside_castling_rights(board.turn):
        return True

    # illegal without queenside rights
    if board.is_queenside_castling(move) and not board.has_queenside_castling_rights(board.turn):
        return True

    # illegal if any pieces are between king & rook
    rook_square = chess.square(7 if board.is_kingside_castling(move) else 0, chess.square_rank(move.from_square))
    between_squares = chess.SquareSet(chess.between(move.from_square, rook_square))
    if any(map(lambda s: board.piece_at(s), between_squares)):
        return True

    # its legal
    return False
Exemplo n.º 8
0
def old_evaluate(board: chess.Board):
    """Evaluate position."""
    white_score = 0
    black_score = 0
    if board.is_stalemate():
        return 0
    if board.is_checkmate():
        if board.turn == chess.WHITE:
            return -10000
        return 10000
    piece_map = board.piece_map()
    # Counters for bishop pair
    white_bishops = 0
    black_bishops = 0
    for piece in piece_map:
        if piece_map[piece].symbol().isupper():
            white_score += PIECES_VALUES[piece_map[piece].symbol().lower()]
            if piece in CENTRAL_SQUARES:
                white_score += CENTER_BONUS
            if piece in ELARGED_SQUARES:
                white_score += int(CENTER_BONUS / 2)
            if piece_map[piece].symbol() == 'P' and piece in SEVENTH_ROW:
                white_score += PAWN_SEVENTH_ROW
            if piece_map[piece].symbol() == 'P' and piece in EIGHT_ROW:
                white_score += QUEEN_VALUE
            if piece_map[piece].symbol() == 'B':
                white_bishops += 1
            """if piece_map[piece].symbol() == 'Q' and piece in CENTRAL_SQUARES and len(piece_map) > 20:
                white_score -= 30"""
            if piece_map[piece].symbol() == 'K':
                if piece + 7 in piece_map and piece_map[piece + 7].symbol() == 'P' and len(piece_map) > 16:
                    white_score += PROTECTED_KING
                if piece + 8 in piece_map and piece_map[piece + 8].symbol() == 'P' and len(piece_map) > 16:
                    white_score += PROTECTED_KING
                if piece + 9 in piece_map and piece_map[piece + 9].symbol() == 'P' and len(piece_map) > 16:
                    white_score += PROTECTED_KING
        else:
            black_score += PIECES_VALUES[piece_map[piece].symbol()]
            if piece in CENTRAL_SQUARES and not piece_map[piece].symbol() == "q":
                black_score += 10
            if piece in ELARGED_SQUARES and not piece_map[piece].symbol() == "q":
                black_score += 5
            if piece_map[piece].symbol() == 'p' and piece in SECOND_ROW:
                black_score += 20
            if piece_map[piece].symbol() == 'p' and piece in FIRST_ROW:
                black_score += QUEEN_VALUE
            if piece_map[piece].symbol() == 'b':
                black_bishops += 1
            if piece_map[piece].symbol() == 'k':
                if piece - 7 in piece_map and piece_map[piece - 7].symbol() == 'p' and len(piece_map) > 16:
                    black_score += PROTECTED_KING
                if piece - 8 in piece_map and piece_map[piece - 8].symbol() == 'p' and len(piece_map) > 16:
                    black_score += PROTECTED_KING
                if piece - 9 in piece_map and piece_map[piece - 9].symbol() == 'p' and len(piece_map) > 16:
                    black_score += PROTECTED_KING
            if piece_map[piece].symbol() == 'q' and len(piece_map) > 28:
                black_score -= 30
    if white_bishops >= 2:
        white_score += BISHOPS_PAIR
    if black_bishops >= 2:
        black_score += BISHOPS_PAIR
    if board.has_kingside_castling_rights(chess.WHITE):
        white_score += 7
    if board.has_kingside_castling_rights(chess.BLACK):
        black_score += 7
    if board.has_queenside_castling_rights(chess.WHITE):
        white_score += 7
    if board.has_queenside_castling_rights(chess.BLACK):
        black_score += 7
    # if board.peek().uci() in ['e1g1', 'e1c1']:
        # white_score += 101
        # print("white castle !")
    # if board.peek().uci() in ['e8g8', 'e8c8']:
        # black_score += 101
        # print("black castle !")
    # Check isolated pawns
    for index, column in enumerate(COLUMNS):
        column_pawns = pawn_on_column(column, "P", piece_map)
        if column_pawns == 2:
            white_score -= DOUBLED_PAWNS
        if column_pawns == 3:
            white_score -= TRIPLED_PAWNS
        if column_pawns >= 4:
            white_score -= QUADRUPLED_PAWNS
        if column_pawns > 0:
            if column == COLUMN_A and pawn_on_column(COLUMN_B, "P", piece_map) == 0:
                white_score -= ISOLATED_PAWN
            elif column == COLUMN_H and pawn_on_column(COLUMN_G, "P", piece_map) == 0:
                white_score -= ISOLATED_PAWN
            else:
                if pawn_on_column(COLUMNS[index-1], "P", piece_map) == 0 and pawn_on_column(COLUMNS[index+1], "P", piece_map) == 0:
                    white_score -= ISOLATED_PAWN
    white_score += check_passed_pawns(board, True)
    if board.turn == chess.WHITE:
        white_score += len(list(board.legal_moves))
        board.push(chess.Move.from_uci("0000"))
        black_score += len(list(board.legal_moves))
        board.pop()
    else:
        black_score += len(list(board.legal_moves))
        board.push(chess.Move.from_uci("0000"))
        white_score += len(list(board.legal_moves))
        board.pop()
    return white_score-black_score
Exemplo n.º 9
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
Exemplo n.º 10
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