def board_to_planes(board, board_occ=0, normalize=True, mode=MODE_CRAZYHOUSE, last_moves=None): """ Gets the plane representation of a given board state. (No history of past board positions is used.) :param board: Board handle (Python-chess object) :param board_occ: Sets how often the board state has occurred before (by default 0) :param normalize: True if the inputs shall be normalized to the 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) :param last_moves: List of last moves. It is assumed that the most recent move is the first entry ! :return: planes - the plane representation of the current board state """ # TODO: Remove board.mirror() for black by addressing the according color channel # (I) Define the Input Representation for one position planes_pos = np.zeros((NB_CHANNELS_POS, BOARD_HEIGHT, BOARD_WIDTH)) planes_const = np.zeros((NB_CHANNELS_CONST, BOARD_HEIGHT, BOARD_WIDTH)) if mode == MODE_CRAZYHOUSE: # crazyhouse only doesn't contain remaining checks planes_variants = False elif mode == MODE_LICHESS: planes_variants = np.zeros( (NB_CHANNELS_VARIANTS, BOARD_HEIGHT, BOARD_WIDTH)) else: # mode = MODE_CHESS # chess doesn't contain pocket pieces and remaining checks planes_variants = np.zeros( (NB_CHANNELS_VARIANTS, BOARD_HEIGHT, BOARD_WIDTH)) # save whose turn it is board_turn = chess.WHITE # check who's player turn it is and flip the board if it's black turn if board.turn == chess.BLACK: board_turn = chess.BLACK board = board.mirror() _fill_position_planes(planes_pos, board, board_occ, mode) _fill_constant_planes(planes_const, board, board_turn) if mode == MODE_LICHESS: _fill_variants_plane(board, planes_variants) elif mode == MODE_CHESS: if board.chess960 is True: planes_variants[:, :, :] = 1 # create the move planes planes_moves = np.zeros((NB_CHANNELS_HISTORY, BOARD_HEIGHT, BOARD_WIDTH)) if last_moves: for i, move in enumerate(last_moves): if move: from_row, from_col = get_row_col( move.from_square, mirror=board_turn == chess.BLACK) to_row, to_col = get_row_col(move.to_square, mirror=board_turn == chess.BLACK) planes_moves[i * 2, from_row, from_col] = 1 planes_moves[i * 2 + 1, to_row, to_col] = 1 # (VI) Merge the Matrix-Stack if mode == MODE_CRAZYHOUSE: planes = np.concatenate((planes_pos, planes_const), axis=0) else: # mode = MODE_LICHESS | mode == MODE_CHESS planes = np.concatenate( (planes_pos, planes_const, planes_variants, planes_moves), axis=0) # revert the board if the players turn was black # ! DO NOT DELETE OR UNCOMMENT THIS BLOCK BECAUSE THE PARAMETER board IS CHANGED IN PLACE ! if board_turn == chess.BLACK: board = board.mirror() if normalize is True: planes *= MATRIX_NORMALIZER # planes = normalize_input_planes(planes) # return the plane representation of the given board return planes
def board_to_planes(board, board_occ=0, normalize=True, crazyhouse_only=False): """ Gets the plane representation of a given board state. (No history of past board positions is used.) ## Chess Variants: 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) P1 prisoner count | 5 (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN) (excluding the KING) P2 prisoner count | 5 (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN) (excluding the KING) P1 Promoted Pawns Mask | 1 (binary map indicating the pieces which have been promoted) P2 Promoted Pawns Mask | 1 (binary map indicating the pieces which have been promoted) En-passant square | 1 (Binary map indicating the square where en-passant capture is possible) --- 27 planes * * * Colour | 1 (all zeros for black and all ones for white) Total move count | 1 (integer value setting the move count (uci notation)) 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) P1 remaining-checks | 2 (only needed for the 3check variant, after 3 checks by one player the game ends) P2 remaining-checks | 2 (only needed for the 3check variant, after 3 checks by one player the game ends) 11 planes * * * is960 = | 1 (boolean, 1 when active) Variants indicator each variant gets a whole channel assigned. All variants are one-hot encoded 1 - "chess" | 1 2 - "crazyhouse" | 1 3 - "kingofthehill" | 1 4 - "3check" | 1 5 - "giveaway" | 1 6 - "atomic" | 1 7 - "horde" | 1 8 - "racingkings" | 1 9 planes # -------------- The history list of the past 7 board states have been removed The total number of planes is calculated as follows: 27 + 11 + 9 Total: 47 planes :param board: Board handle (Python-chess object) :param board_occ: Sets how often the board state has occurred before (by default 0) :param normalize: True if the inputs shall be normalized to the range [0.-1.] :param crazyhouse_only: Boolean indicates if the older crazyhouse only specification shall be used Visit variants.crazyhouse.input_representation for documentation :return: planes - the plane representation of the current board state """ # TODO: Remove board.mirror() for black by addressing the according color channel # (I) Define the Input Representation for one position planes_pos = np.zeros((NB_CHANNELS_POS, BOARD_HEIGHT, BOARD_WIDTH)) if crazyhouse_only is True: # crazyhouse only doesn't contain remaining checks planes_const = np.zeros( (NB_CHANNELS_CONST_CZ, BOARD_HEIGHT, BOARD_WIDTH)) else: planes_const = np.zeros((NB_CHANNELS_CONST, BOARD_HEIGHT, BOARD_WIDTH)) if crazyhouse_only is False: planes_variants = np.zeros( (NB_CHANNELS_VARIANTS, BOARD_HEIGHT, BOARD_WIDTH)) else: planes_variants = False # save whose turn it is board_turn = chess.WHITE # check who's player turn it is and flip the board if it's black turn if board.turn == chess.BLACK: board_turn = chess.BLACK board = board.mirror() _fill_position_planes(planes_pos, board, board_occ) _fill_constant_planes(planes_const, board, board_turn) if crazyhouse_only is False: _fill_variants_plane(board, planes_variants) # (VI) Merge the Matrix-Stack if crazyhouse_only is True: planes = np.concatenate((planes_pos, planes_const), axis=0) else: planes = np.concatenate((planes_pos, planes_const, planes_variants), axis=0) # revert the board if the players turn was black # ! DO NOT DELETE OR UNCOMMENT THIS BLOCK BECAUSE THE PARAMETER board IS CHANGED IN PLACE ! if board_turn == chess.BLACK: board = board.mirror() if normalize is True: planes *= MATRIX_NORMALIZER # planes = normalize_input_planes(planes) # return the plane representation of the given board return planes
def board_to_planes(board, board_occ=0, normalize=True): """ Gets the plane representation of a given board state. (Now history of past board positions is used.) ## Crazyhouse: 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) P1 prisoner count | 5 (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN) (excluding the KING) P2 prisoner count | 5 (pieces are ordered: PAWN, KNIGHT, BISHOP, ROOK, QUEEN) (excluding the KING) P1 Promoted Pawns Mask | 1 (binary map indicating the pieces which have been promoted) P2 Promoted Pawns Mask | 1 (binary map indicating the pieces which have been promoted) En-passant square | 1 (Binary map indicating the square where en-passant capture is possible) --- 27 planes * * * Colour | 1 (all zeros for black and all ones for white) Total move count | 1 (integer value setting the move count (uci notation)) 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) # -------------- 7 planes The history list of the past 7 board states have been removed The total number of planes is calculated as follows: 27 + 7 Total: 34 planes :param board: Board handle (Python-chess object) :param board_occ: Sets how often the board state has occurred before (by default 0) :param normalize: True if the inputs shall be normalized to the range [0.-1.] :return: planes - the plane representation of the current board state """ # TODO: Remove board.mirror() for black by addressing the according color channel # (I) Define the Input Representation for one position planes_pos = np.zeros((NB_CHANNELS_POS, BOARD_HEIGHT, BOARD_WIDTH)) planes_const = np.zeros((NB_CHANNELS_CONST, BOARD_HEIGHT, BOARD_WIDTH)) # save whose turn it is board_turn = chess.WHITE # check who's player turn it is and flip the board if it's black turn if board.turn == chess.BLACK: board_turn = chess.BLACK board = board.mirror() # Fill in the piece positions # Iterate over both color starting with WHITE for z, color in enumerate(chess.COLORS): # the PIECE_TYPE is an integer list in python-chess for piece_type in chess.PIECE_TYPES: # define the channel by the piecetype (the input representation uses the same ordering as python-chess) # we add an offset for the black pieces # note that we subtract 1 because in python chess the PAWN has index 1 and not 0 channel = (piece_type - 1) + z * len(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) # set the bit at the right position planes_pos[channel, row, col] = 1 # (II) Fill in the Repetition Data # a game to test out if everything is working correctly is: https://lichess.org/jkItXBWy#73 ch = CHANNEL_MAPPING_POS["repetitions"] # 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_pos[ch, :, :] = 1 if board_occ >= 2: planes_pos[ch + 1, :, :] = 1 # 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 planes_pos[ch, :, :] = board.pockets[chess.WHITE].count(p_type) # the prison for black begins 5 channels later planes_pos[ch + 5, :, :] = board.pockets[chess.BLACK].count(p_type) # (III) Fill in the promoted pieces # iterate over all promoted pieces according to the mask and set the according bit ch = CHANNEL_MAPPING_POS["promo"] for pos in chess.SquareSet(board.promoted): row, col = get_row_col(pos) if board.piece_at(pos).color == chess.WHITE: planes_pos[ch, row, col] = 1 else: planes_pos[ch + 1, row, col] = 1 # (III.2) En Passant Square # mark the square where an en-passant capture is possible ch = CHANNEL_MAPPING_POS["ep_square"] if board.ep_square is not None: row, col = get_row_col(board.ep_square) planes_pos[ch, row, col] = 1 # (IV) Constant Value Inputs # (IV.1) Color if board_turn == chess.WHITE: planes_const[CHANNEL_MAPPING_CONST["color"], :, :] = 1 # otherwise the mat will remain zero # (IV.2) Total Move Count planes_const[ CHANNEL_MAPPING_CONST["total_mv_cnt"], :, :] = board.fullmove_number # alternatively, you could use the half-moves-counter: len(board.move_stack) # (IV.3) Castling Rights ch = CHANNEL_MAPPING_CONST["castling"] # WHITE # check for King Side Castling if bool(board.castling_rights & chess.BB_H1) is True: # White can castle with the h1 rook planes_const[ch, :, :] = 1 # check for Queen Side Castling if bool(board.castling_rights & chess.BB_A1) is True: planes_const[ch + 1, :, :] = 1 # BLACK # check for King Side Castling if bool(board.castling_rights & chess.BB_H8) is True: # White can castle with the h1 rook planes_const[ch + 2, :, :] = 1 # check for Queen Side Castling if bool(board.castling_rights & chess.BB_A8) is True: planes_const[ch + 3, :, :] = 1 # (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 no_progress_cnt = board.halfmove_clock # check how often the position has already occurred in the game planes_const[ CHANNEL_MAPPING_CONST["no_progress_cnt"], :, :] = no_progress_cnt # (V) Merge the Matrix-Stack planes = np.concatenate((planes_pos, planes_const), axis=0) # revert the board if the players turn was black # ! DO NOT DELETE OR UNCOMMENT THIS BLOCK BECAUSE THE PARAMETER board IS CHANGED IN PLACE ! if board_turn == chess.BLACK: board = board.mirror() if normalize is True: planes *= MATRIX_NORMALIZER # planes = normalize_input_planes(planes) # return the plane representation of the given board return planes
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
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