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, crazyhouse_only=main_config['policy_version'] == 1)
def board_to_planes(board, board_occ=0, normalize=True): """ Gets the plane representation of a given board state. (No 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 """ # return the plane representation of the given board return variants.board_to_planes(board, board_occ, normalize, mode=MODE_CRAZYHOUSE)
def board_to_planes(board, board_occ=0, normalize=True): """ Gets the plane representation of a given board state. (No history of past board positions is used.) ## 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 * * * 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 * * * 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 The total number of planes is calculated as follows: # -------------- 15 + 7 + 1 + 16 Total: 39 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 """ # return the plane representation of the given board return variants.board_to_planes(board, board_occ, normalize, mode=MODE_CHESS)
def get_planes_from_game(game, mate_in_one=False): """ Returns all plane descriptions of a given game and their corresponding target values: - the game outcome (-1, 0, 1) - the next move which will be played in each position :param game: Game handle which is a python-chess object (e.g. mv_hist_len = 8 means that the current position and the 7 previous positions are exported) :param mate_in_one: Decide weather only to export the position before the last mate-in-one move (this option is for evaluation and DEBUG purposes) :return: x - the position description of all moves in the game y_value - the target values of the scene description. Here the game outcome. returns -1 if the current player lost, +1 if the current player won, 0 for draw y_policy - the policy vector one-hot encoded indicating the next move the player current player chose in this position plys_to_end - array of how many plys to the end of the game for each position. This can be used to apply discounting """ fen_dic = { } # A dictionary which maps the fen description to its number of occurrences x = [] y_value = [] y_policy = [] board = game.board() # get the initial board state # update the y value accordingly if board.turn == chess.WHITE: y_init = 1 else: y_init = -1 if game.headers["Result"] == "0-1": y_init *= -1 elif game.headers["Result"] == "1/2-1/2": y_init = 0 all_moves = [] # Extract all moves first and save them into a list for move in game.main_line(): all_moves.append(move) # Iterate through all moves (except the last one) and play them on a board. # you don't want to push the last move on the board because you had no movement policy to learn from in this case # The moves get pushed at the end of the for-loop and is only used in the next loop. # Therefore we can iterate over 'all' moves plys = 0 for move in all_moves: board_occ = 0 # by default the positions hasn't occurred before fen = board.fen() # remove the halfmove counter & move counter from this fen to make repetitions possible fen = fen[:fen.find(" ") + 2] # save the board state to the fen dictionary if fen in list(fen_dic.keys()): board_occ = fen_dic[fen] fen_dic[fen] += 1 else: fen_dic[fen] = 1 # create a new entry # we insert the move i (and not i+1), because the start is the empty board position next_move = all_moves[plys] # check if you need to export a mate_in_one_scenario if not mate_in_one or plys == len(all_moves) - 1: # build the last move vector by putting the most recent move on top followed by the remaining past moves last_moves = [None] * NB_LAST_MOVES if plys != 0: last_moves[0:min(plys, NB_LAST_MOVES)] = all_moves[ max(plys - NB_LAST_MOVES, 0):plys][::-1] # receive the board and the evaluation of the current position in plane representation # We don't want to store float values because the integer datatype is cheaper, # that's why normalize is set to false x_cur = board_to_planes(board, board_occ, normalize=False, mode=main_config["mode"], last_moves=last_moves) # add the evaluation of 1 position to the list x.append(x_cur) y_value.append(y_init) # add the next move defined in policy vector notation to the policy list # the network always sees the board as if he's the white player, that's the move is mirrored fro black y_policy.append( move_to_policy(next_move, is_white_to_move=board.turn)) y_init *= -1 # flip the y_init value after each move board.push(move) # push the next move on the board plys += 1 plys_to_end = np.arange(plys)[::-1] # check if there has been any moves if x and y_value and y_policy: x = np.stack(x, axis=0) y_value = np.stack(y_value, axis=0) y_policy = np.stack(y_policy, axis=0) else: print("game.headers:") print(game.headers) print("len(all_moves)", len(all_moves)) print("game", game) raise Exception("The given pgn file's mainline is empty!") return x, y_value, y_policy, plys_to_end
def board_to_planes(board, board_occ=0, normalize=True): """ 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 # -------------- * * * Last 8 moves | 16 (indicated by origin and destination square, the most recent move is described by first 2 planes) -> added since version 2 --- 16 planes The total number of planes is calculated as follows: 27 + 11 + 9 + 16 Total: 63 planes (version 2) Total: 47 planes (version 1) :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) :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_LICHESS)
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, mode=main_config['mode'])