}
+// 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 <bool DoCheck>
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<std::string, const Variant*> {