From: Fabian Fichter Date: Sun, 23 Apr 2023 18:41:24 +0000 (+0200) Subject: Extract Variant::conclude from header file X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=10109bcaecbe8b5c8e168234cf36b871a42eb249;p=fairystockfish.git Extract Variant::conclude from header file No functional change. --- diff --git a/src/variant.cpp b/src/variant.cpp index 6332047..d477eb6 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -1885,6 +1885,141 @@ void VariantMap::init() { } +// Pre-calculate derived properties +Variant* Variant::conclude() { + // Enforce consistency to allow runtime optimizations + if (!doubleStep) + doubleStepRegion[WHITE] = doubleStepRegion[BLACK] = 0; + if (!doubleStepRegion[WHITE] && !doubleStepRegion[BLACK]) + doubleStep = false; + + // Determine optimizations + bool restrictedMobility = false; + for (PieceSet ps = pieceTypes; !restrictedMobility && ps;) + { + PieceType pt = pop_lsb(ps); + if (mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt]) + restrictedMobility = true; + } + fastAttacks = !(pieceTypes & ~(CHESS_PIECES | COMMON_FAIRY_PIECES)) + && kingType == KING + && !restrictedMobility + && !cambodianMoves + && !diagonalLines; + fastAttacks2 = !(pieceTypes & ~(SHOGI_PIECES | COMMON_STEP_PIECES)) + && kingType == KING + && !restrictedMobility + && !cambodianMoves + && !diagonalLines; + + // Initialize calculated NNUE properties + nnueKing = pieceTypes & KING ? KING + : extinctionPieceCount == 0 && (extinctionPieceTypes & COMMONER) ? COMMONER + : NO_PIECE_TYPE; + // The nnueKing has to present exactly once and must not change in count + if (nnueKing != NO_PIECE_TYPE) + { + // If the nnueKing is involved in promotion, count might change + if ( ((promotionPawnTypes[WHITE] | promotionPawnTypes[BLACK]) & nnueKing) + || ((promotionPieceTypes[WHITE] | promotionPieceTypes[BLACK]) & nnueKing) + || std::find(std::begin(promotedPieceType), std::end(promotedPieceType), nnueKing) != std::end(promotedPieceType)) + nnueKing = NO_PIECE_TYPE; + } + if (nnueKing != NO_PIECE_TYPE) + { + std::string fenBoard = startFen.substr(0, startFen.find(' ')); + // Switch NNUE from KA to A if there is no unique piece + if ( std::count(fenBoard.begin(), fenBoard.end(), pieceToChar[make_piece(WHITE, nnueKing)]) != 1 + || std::count(fenBoard.begin(), fenBoard.end(), pieceToChar[make_piece(BLACK, nnueKing)]) != 1) + nnueKing = NO_PIECE_TYPE; + } + // We can not use popcount here yet, as the lookup tables are initialized after the variants + int nnueSquares = (maxRank + 1) * (maxFile + 1); + nnueUsePockets = (pieceDrops && (capturesToHand || (!mustDrop && std::bitset<64>(pieceTypes).count() != 1))) || seirawanGating; + int nnuePockets = nnueUsePockets ? 2 * int(maxFile + 1) : 0; + int nnueNonDropPieceIndices = (2 * std::bitset<64>(pieceTypes).count() - (nnueKing != NO_PIECE_TYPE)) * nnueSquares; + int nnuePieceIndices = nnueNonDropPieceIndices + 2 * (std::bitset<64>(pieceTypes).count() - (nnueKing != NO_PIECE_TYPE)) * nnuePockets; + int i = 0; + for (PieceSet ps = pieceTypes; ps;) + { + // Make sure that the nnueKing type gets the last index, since the NNUE architecture relies on that + PieceType pt = lsb(ps != piece_set(nnueKing) ? ps & ~piece_set(nnueKing) : ps); + ps ^= pt; + assert(pt != nnueKing || !ps); + + for (Color c : { WHITE, BLACK}) + { + pieceSquareIndex[c][make_piece(c, pt)] = 2 * i * nnueSquares; + pieceSquareIndex[c][make_piece(~c, pt)] = (2 * i + (pt != nnueKing)) * nnueSquares; + pieceHandIndex[c][make_piece(c, pt)] = 2 * i * nnuePockets + nnueNonDropPieceIndices; + pieceHandIndex[c][make_piece(~c, pt)] = (2 * i + 1) * nnuePockets + nnueNonDropPieceIndices; + } + i++; + } + + // Map king squares to enumeration of actually available squares. + // E.g., for xiangqi map from 0-89 to 0-8. + // Variants might be initialized before bitboards, so do not rely on precomputed bitboards (like SquareBB). + // Furthermore conclude() might be called on invalid configuration during validation, + // therefore skip proper initialization in case of invalid board size. + int nnueKingSquare = 0; + if (nnueKing && nnueSquares <= SQUARE_NB) + for (Square s = SQ_A1; s < nnueSquares; ++s) + { + Square bitboardSquare = Square(s + s / (maxFile + 1) * (FILE_MAX - maxFile)); + if ( !mobilityRegion[WHITE][nnueKing] || !mobilityRegion[BLACK][nnueKing] + || (mobilityRegion[WHITE][nnueKing] & make_bitboard(bitboardSquare)) + || (mobilityRegion[BLACK][nnueKing] & make_bitboard(relative_square(BLACK, bitboardSquare, maxRank)))) + { + kingSquareIndex[s] = nnueKingSquare++ * nnuePieceIndices; + } + } + else + kingSquareIndex[SQ_A1] = nnueKingSquare++ * nnuePieceIndices; + nnueDimensions = nnueKingSquare * nnuePieceIndices; + + // Determine maximum piece count + std::istringstream ss(startFen); + ss >> std::noskipws; + unsigned char token; + nnueMaxPieces = 0; + while ((ss >> token) && !isspace(token)) + { + if (pieceToChar.find(token) != std::string::npos || pieceToCharSynonyms.find(token) != std::string::npos) + nnueMaxPieces++; + } + if (twoBoards) + nnueMaxPieces *= 2; + + // For endgame evaluation to be applicable, no special win rules must apply. + // Furthermore, rules significantly changing game mechanics also invalidate it. + endgameEval = extinctionValue == VALUE_NONE + && checkmateValue == -VALUE_MATE + && stalemateValue == VALUE_DRAW + && !materialCounting + && !(flagRegion[WHITE] || flagRegion[BLACK]) + && !mustCapture + && !checkCounting + && !makpongRule + && !connectN + && !blastOnCapture + && !capturesToHand + && !twoBoards + && !restrictedMobility + && kingType == KING; + + shogiStylePromotions = false; + for (PieceType current: promotedPieceType) + if (current != NO_PIECE_TYPE) + { + shogiStylePromotions = true; + break; + } + + return this; +} + + /// VariantMap::parse_istream reads variants from an INI-style configuration input stream. template diff --git a/src/variant.h b/src/variant.h index 3e5dcb5..b342c73 100644 --- a/src/variant.h +++ b/src/variant.h @@ -211,139 +211,7 @@ struct Variant { return this; } - // Pre-calculate derived properties - Variant* conclude() { - // Enforce consistency to allow runtime optimizations - if (!doubleStep) - doubleStepRegion[WHITE] = doubleStepRegion[BLACK] = 0; - if (!doubleStepRegion[WHITE] && !doubleStepRegion[BLACK]) - doubleStep = false; - - // Determine optimizations - bool restrictedMobility = false; - for (PieceSet ps = pieceTypes; !restrictedMobility && ps;) - { - PieceType pt = pop_lsb(ps); - if (mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt]) - restrictedMobility = true; - } - fastAttacks = !(pieceTypes & ~(CHESS_PIECES | COMMON_FAIRY_PIECES)) - && kingType == KING - && !restrictedMobility - && !cambodianMoves - && !diagonalLines; - fastAttacks2 = !(pieceTypes & ~(SHOGI_PIECES | COMMON_STEP_PIECES)) - && kingType == KING - && !restrictedMobility - && !cambodianMoves - && !diagonalLines; - - // Initialize calculated NNUE properties - nnueKing = pieceTypes & KING ? KING - : extinctionPieceCount == 0 && (extinctionPieceTypes & COMMONER) ? COMMONER - : NO_PIECE_TYPE; - // The nnueKing has to present exactly once and must not change in count - if (nnueKing != NO_PIECE_TYPE) - { - // If the nnueKing is involved in promotion, count might change - if ( ((promotionPawnTypes[WHITE] | promotionPawnTypes[BLACK]) & nnueKing) - || ((promotionPieceTypes[WHITE] | promotionPieceTypes[BLACK]) & nnueKing) - || std::find(std::begin(promotedPieceType), std::end(promotedPieceType), nnueKing) != std::end(promotedPieceType)) - nnueKing = NO_PIECE_TYPE; - } - if (nnueKing != NO_PIECE_TYPE) - { - std::string fenBoard = startFen.substr(0, startFen.find(' ')); - // Switch NNUE from KA to A if there is no unique piece - if ( std::count(fenBoard.begin(), fenBoard.end(), pieceToChar[make_piece(WHITE, nnueKing)]) != 1 - || std::count(fenBoard.begin(), fenBoard.end(), pieceToChar[make_piece(BLACK, nnueKing)]) != 1) - nnueKing = NO_PIECE_TYPE; - } - // We can not use popcount here yet, as the lookup tables are initialized after the variants - int nnueSquares = (maxRank + 1) * (maxFile + 1); - nnueUsePockets = (pieceDrops && (capturesToHand || (!mustDrop && std::bitset<64>(pieceTypes).count() != 1))) || seirawanGating; - int nnuePockets = nnueUsePockets ? 2 * int(maxFile + 1) : 0; - int nnueNonDropPieceIndices = (2 * std::bitset<64>(pieceTypes).count() - (nnueKing != NO_PIECE_TYPE)) * nnueSquares; - int nnuePieceIndices = nnueNonDropPieceIndices + 2 * (std::bitset<64>(pieceTypes).count() - (nnueKing != NO_PIECE_TYPE)) * nnuePockets; - int i = 0; - for (PieceSet ps = pieceTypes; ps;) - { - // Make sure that the nnueKing type gets the last index, since the NNUE architecture relies on that - PieceType pt = lsb(ps != piece_set(nnueKing) ? ps & ~piece_set(nnueKing) : ps); - ps ^= pt; - assert(pt != nnueKing || !ps); - - for (Color c : { WHITE, BLACK}) - { - pieceSquareIndex[c][make_piece(c, pt)] = 2 * i * nnueSquares; - pieceSquareIndex[c][make_piece(~c, pt)] = (2 * i + (pt != nnueKing)) * nnueSquares; - pieceHandIndex[c][make_piece(c, pt)] = 2 * i * nnuePockets + nnueNonDropPieceIndices; - pieceHandIndex[c][make_piece(~c, pt)] = (2 * i + 1) * nnuePockets + nnueNonDropPieceIndices; - } - i++; - } - - // Map king squares to enumeration of actually available squares. - // E.g., for xiangqi map from 0-89 to 0-8. - // Variants might be initialized before bitboards, so do not rely on precomputed bitboards (like SquareBB). - // Furthermore conclude() might be called on invalid configuration during validation, - // therefore skip proper initialization in case of invalid board size. - int nnueKingSquare = 0; - if (nnueKing && nnueSquares <= SQUARE_NB) - for (Square s = SQ_A1; s < nnueSquares; ++s) - { - Square bitboardSquare = Square(s + s / (maxFile + 1) * (FILE_MAX - maxFile)); - if ( !mobilityRegion[WHITE][nnueKing] || !mobilityRegion[BLACK][nnueKing] - || (mobilityRegion[WHITE][nnueKing] & make_bitboard(bitboardSquare)) - || (mobilityRegion[BLACK][nnueKing] & make_bitboard(relative_square(BLACK, bitboardSquare, maxRank)))) - { - kingSquareIndex[s] = nnueKingSquare++ * nnuePieceIndices; - } - } - else - kingSquareIndex[SQ_A1] = nnueKingSquare++ * nnuePieceIndices; - nnueDimensions = nnueKingSquare * nnuePieceIndices; - - // Determine maximum piece count - std::istringstream ss(startFen); - ss >> std::noskipws; - unsigned char token; - nnueMaxPieces = 0; - while ((ss >> token) && !isspace(token)) - { - if (pieceToChar.find(token) != std::string::npos || pieceToCharSynonyms.find(token) != std::string::npos) - nnueMaxPieces++; - } - if (twoBoards) - nnueMaxPieces *= 2; - - // For endgame evaluation to be applicable, no special win rules must apply. - // Furthermore, rules significantly changing game mechanics also invalidate it. - endgameEval = extinctionValue == VALUE_NONE - && checkmateValue == -VALUE_MATE - && stalemateValue == VALUE_DRAW - && !materialCounting - && !(flagRegion[WHITE] || flagRegion[BLACK]) - && !mustCapture - && !checkCounting - && !makpongRule - && !connectN - && !blastOnCapture - && !capturesToHand - && !twoBoards - && !restrictedMobility - && kingType == KING; - - shogiStylePromotions = false; - for (PieceType current: promotedPieceType) - if (current != NO_PIECE_TYPE) - { - shogiStylePromotions = true; - break; - } - - return this; - } + Variant* conclude(); }; class VariantMap : public std::map {