// 2. Active color
ss >> token;
- sideToMove = (token == 'w' ? WHITE : BLACK);
+ sideToMove = (token != (sfen ? 'w' : 'b') ? WHITE : BLACK); // Invert colors for SFEN
ss >> token;
- // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
- // Shredder-FEN that uses the letters of the columns on which the rooks began
- // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
- // if an inner rook is associated with the castling right, the castling tag is
- // replaced by the file letter of the involved rook, as for the Shredder-FEN.
- while ((ss >> token) && !isspace(token))
+ // 3-4. Skip parsing castling and en passant flags if not present
+ st->epSquare = SQ_NONE;
+ st->castlingKingSquare[WHITE] = st->castlingKingSquare[BLACK] = SQ_NONE;
+ if (!isdigit(ss.peek()) && !sfen)
{
- Square rsq;
- Color c = islower(token) ? BLACK : WHITE;
- Piece rook = make_piece(c, ROOK);
+ // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
+ // Shredder-FEN that uses the letters of the columns on which the rooks began
+ // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
+ // if an inner rook is associated with the castling right, the castling tag is
+ // replaced by the file letter of the involved rook, as for the Shredder-FEN.
+ while ((ss >> token) && !isspace(token))
+ {
+ Square rsq;
+ Color c = islower(token) ? BLACK : WHITE;
+ Piece rook = make_piece(c, castling_rook_piece());
- token = char(toupper(token));
+ token = char(toupper(token));
- if (token == 'K')
- for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {}
+ if (token == 'K')
+ for (rsq = make_square(FILE_MAX, castling_rank(c)); piece_on(rsq) != rook; --rsq) {}
- else if (token == 'Q')
- for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {}
+ else if (token == 'Q')
+ for (rsq = make_square(FILE_A, castling_rank(c)); piece_on(rsq) != rook; ++rsq) {}
- else if (token >= 'A' && token <= 'H')
- rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
+ else if (token >= 'A' && token <= 'A' + max_file())
+ rsq = make_square(File(token - 'A'), castling_rank(c));
- else
- continue;
+ else
+ continue;
+
+ // Determine castling "king" position
+ if (castling_enabled() && st->castlingKingSquare[c] == SQ_NONE)
+ {
+ Bitboard castlingKings = pieces(c, castling_king_piece()) & rank_bb(castling_rank(c));
+ // Ambiguity resolution for 960 variants with more than one "king"
+ // e.g., EAH means that an e-file king can castle with a- and h-file rooks
+ st->castlingKingSquare[c] = isChess960 && piece_on(rsq) == make_piece(c, castling_king_piece()) ? rsq
+ : castlingKings && (!more_than_one(castlingKings) || isChess960) ? lsb(castlingKings)
+ : make_square(castling_king_file(), castling_rank(c));
+ }
- set_castling_right(c, rsq);
+ // Set gates (and skip castling rights)
+ if (gating())
+ {
+ st->gatesBB[c] |= rsq;
+ if (token == 'K' || token == 'Q')
+ st->gatesBB[c] |= st->castlingKingSquare[c];
+ // Do not set castling rights for gates unless there are no pieces in hand,
+ // which means that the file is referring to a chess960 castling right.
+ else if (!seirawan_gating() || count_in_hand(c, ALL_PIECES) > 0 || captures_to_hand())
+ continue;
+ }
+
+ if (castling_enabled() && piece_on(rsq) == rook)
+ set_castling_right(c, rsq);
+ }
+
+ // Set castling rights for 960 gating variants
+ if (gating() && castling_enabled())
+ for (Color c : {WHITE, BLACK})
+ if ((gates(c) & pieces(castling_king_piece())) && !castling_rights(c) && (!seirawan_gating() || count_in_hand(c, ALL_PIECES) > 0 || captures_to_hand()))
+ {
+ Bitboard castling_rooks = gates(c) & pieces(castling_rook_piece());
+ while (castling_rooks)
+ set_castling_right(c, pop_lsb(castling_rooks));
+ }
+
+ // counting limit
+ if (counting_rule() && isdigit(ss.peek()))
+ ss >> st->countingLimit;
+
+ // 4. En passant square.
+ // Ignore if square is invalid or not on side to move relative rank 6.
+ else if ( ((ss >> col) && (col >= 'a' && col <= 'a' + max_file()))
+ && ((ss >> row) && (row >= '1' && row <= '1' + max_rank())))
+ {
+ st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
+#ifdef LARGEBOARDS
+ // Consider different rank numbering in CECP
+ if (max_rank() == RANK_10 && Options["Protocol"] == "xboard")
+ st->epSquare += NORTH;
+#endif
+
+ // En passant square will be considered only if
+ // a) side to move have a pawn threatening epSquare
+ // b) there is an enemy pawn in front of epSquare
+ // c) there is no piece on epSquare or behind epSquare
+ bool enpassant;
+ enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
+ && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
+ && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
+ if (!enpassant)
+ st->epSquare = SQ_NONE;
+ }
}
- // 4. En passant square.
- // Ignore if square is invalid or not on side to move relative rank 6.
- bool enpassant = false;
+ // Check counter for nCheck
+ ss >> std::skipws >> token >> std::noskipws;
- if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
- && ((ss >> row) && (row == (sideToMove == WHITE ? '6' : '3'))))
+ if (check_counting())
{
- st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
+ if (ss.peek() == '+')
+ {
+ st->checksRemaining[WHITE] = CheckCount(std::max(token - '0', 0));
+ ss >> token >> token;
+ st->checksRemaining[BLACK] = CheckCount(std::max(token - '0', 0));
+ }
+ else
+ {
+ // If check count is not provided, assume that the next check wins
+ st->checksRemaining[WHITE] = CheckCount(1);
+ st->checksRemaining[BLACK] = CheckCount(1);
+ ss.putback(token);
+ }
+ }
+ else
+ ss.putback(token);
- // En passant square will be considered only if
- // a) side to move have a pawn threatening epSquare
- // b) there is an enemy pawn in front of epSquare
- // c) there is no piece on epSquare or behind epSquare
- enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
- && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
- && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
+ // 5-6. Halfmove clock and fullmove number
+ if (sfen)
+ {
+ // Pieces in hand for SFEN
+ int handCount = 1;
+ while ((ss >> token) && !isspace(token))
+ {
+ if (token == '-')
+ continue;
+ else if (isdigit(token))
+ {
+ handCount = token - '0';
+ while (isdigit(ss.peek()) && ss >> token)
+ handCount = 10 * handCount + (token - '0');
+ }
+ else if ((idx = piece_to_char().find(token)) != string::npos)
+ {
+ for (int i = 0; i < handCount; i++)
+ add_to_hand(Piece(idx));
+ handCount = 1;
+ }
+ }
+ // Move count is in ply for SFEN
+ ss >> std::skipws >> gamePly;
+ gamePly = std::max(gamePly - 1, 0);
}
+ else
+ {
+ ss >> std::skipws >> st->rule50 >> gamePly;
- if (!enpassant)
- st->epSquare = SQ_NONE;
+ // Convert from fullmove starting from 1 to gamePly starting from 0,
+ // handle also common incorrect FEN with fullmove = 0.
+ gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
+ }
- // 5-6. Halfmove clock and fullmove number
- ss >> std::skipws >> st->rule50 >> gamePly;
+ // counting rules
+ if (st->countingLimit && st->rule50)
+ {
+ st->countingPly = st->rule50;
+ st->rule50 = 0;
+ }
- // Convert from fullmove starting from 1 to gamePly starting from 0,
- // handle also common incorrect FEN with fullmove = 0.
- gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
+ // Lichess-style counter for 3check
+ if (check_counting())
+ {
+ if (ss >> token && token == '+')
+ {
+ ss >> token;
+ st->checksRemaining[WHITE] = CheckCount(std::max(3 - (token - '0'), 0));
+ ss >> token >> token;
+ st->checksRemaining[BLACK] = CheckCount(std::max(3 - (token - '0'), 0));
+ }
+ }
- chess960 = isChess960;
+ chess960 = isChess960 || v->chess960;
+ tsumeMode = Options["TsumeMode"];
thisThread = th;
set_state(st);
- st->accumulator.state[WHITE] = Eval::NNUE::INIT;
- st->accumulator.state[BLACK] = Eval::NNUE::INIT;
assert(pos_is_ok());
++gamePly;
++st->rule50;
++st->pliesFromNull;
+ if (st->countingLimit)
+ ++st->countingPly;
// Used by NNUE
- st->accumulator.state[WHITE] = Eval::NNUE::EMPTY;
- st->accumulator.state[BLACK] = Eval::NNUE::EMPTY;
+ st->accumulator.computed[WHITE] = false;
+ st->accumulator.computed[BLACK] = false;
auto& dp = st->dirtyPiece;
dp.dirty_num = 1;
int game_ply() const;
bool is_chess960() const;
Thread* this_thread() const;
+ bool is_immediate_game_end() const;
+ bool is_immediate_game_end(Value& result, int ply = 0) const;
+ bool is_optional_game_end() const;
+ bool is_optional_game_end(Value& result, int ply = 0, int countStarted = 0) const;
+ bool is_game_end(Value& result, int ply = 0) const;
+ Value material_counting_result() const;
+ bool is_draw(int ply) const;
bool has_game_cycle(int ply) const;
bool has_repeated() const;
+ int counting_limit() const;
+ int counting_ply(int countStarted) const;
int rule50_count() const;
Score psq_score() const;
Value non_pawn_material(Color c) const;
extern std::ostream& operator<<(std::ostream& os, const Position& pos);
+inline const Variant* Position::variant() const {
+ assert(var != nullptr);
+ return var;
+}
+
+inline Rank Position::max_rank() const {
+ assert(var != nullptr);
+ return var->maxRank;
+}
+
+inline File Position::max_file() const {
+ assert(var != nullptr);
+ return var->maxFile;
+}
+
+inline bool Position::two_boards() const {
+ assert(var != nullptr);
+ return var->twoBoards;
+}
+
+inline Bitboard Position::board_bb() const {
+ assert(var != nullptr);
+ return board_size_bb(var->maxFile, var->maxRank);
+}
+
+inline Bitboard Position::board_bb(Color c, PieceType pt) const {
+ assert(var != nullptr);
+ return var->mobilityRegion[c][pt] ? var->mobilityRegion[c][pt] & board_bb() : board_bb();
+}
+
+inline const std::set<PieceType>& Position::piece_types() const {
+ assert(var != nullptr);
+ return var->pieceTypes;
+}
+
+inline const std::string& Position::piece_to_char() const {
+ assert(var != nullptr);
+ return var->pieceToChar;
+}
+
+inline const std::string& Position::piece_to_char_synonyms() const {
+ assert(var != nullptr);
+ return var->pieceToCharSynonyms;
+}
+
+inline Rank Position::promotion_rank() const {
+ assert(var != nullptr);
+ return var->promotionRank;
+}
+
+inline const std::set<PieceType, std::greater<PieceType> >& Position::promotion_piece_types() const {
+ assert(var != nullptr);
+ return var->promotionPieceTypes;
+}
+
+inline bool Position::sittuyin_promotion() const {
+ assert(var != nullptr);
+ return var->sittuyinPromotion;
+}
+
+inline int Position::promotion_limit(PieceType pt) const {
+ assert(var != nullptr);
+ return var->promotionLimit[pt];
+}
+
+inline PieceType Position::promoted_piece_type(PieceType pt) const {
+ assert(var != nullptr);
+ return var->promotedPieceType[pt];
+}
+
+inline bool Position::piece_promotion_on_capture() const {
+ assert(var != nullptr);
+ return var->piecePromotionOnCapture;
+}
+
+inline bool Position::mandatory_pawn_promotion() const {
+ assert(var != nullptr);
+ return var->mandatoryPawnPromotion;
+}
+
+inline bool Position::mandatory_piece_promotion() const {
+ assert(var != nullptr);
+ return var->mandatoryPiecePromotion;
+}
+
+inline bool Position::piece_demotion() const {
+ assert(var != nullptr);
+ return var->pieceDemotion;
+}
+
+inline bool Position::blast_on_capture() const {
+ assert(var != nullptr);
+ return var->blastOnCapture;
+}
+
+inline bool Position::endgame_eval() const {
+ assert(var != nullptr);
+ return var->endgameEval && !count_in_hand(ALL_PIECES) && count<KING>() == 2;
+}
+
+inline bool Position::double_step_enabled() const {
+ assert(var != nullptr);
+ return var->doubleStep;
+}
+
+inline Rank Position::double_step_rank_max() const {
+ assert(var != nullptr);
+ return var->doubleStepRank;
+}
+
+inline Rank Position::double_step_rank_min() const {
+ assert(var != nullptr);
+ return var->doubleStepRankMin;
+}
+
+inline bool Position::castling_enabled() const {
+ assert(var != nullptr);
+ return var->castling;
+}
+
+inline bool Position::castling_dropped_piece() const {
+ assert(var != nullptr);
+ return var->castlingDroppedPiece;
+}
+
+inline File Position::castling_kingside_file() const {
+ assert(var != nullptr);
+ return var->castlingKingsideFile;
+}
+
+inline File Position::castling_queenside_file() const {
+ assert(var != nullptr);
+ return var->castlingQueensideFile;
+}
+
+inline Rank Position::castling_rank(Color c) const {
+ assert(var != nullptr);
+ return relative_rank(c, var->castlingRank, max_rank());
+}
+
+inline File Position::castling_king_file() const {
+ assert(var != nullptr);
+ return var->castlingKingFile;
+}
+
+inline PieceType Position::castling_king_piece() const {
+ assert(var != nullptr);
+ return var->castlingKingPiece;
+}
+
+inline PieceType Position::castling_rook_piece() const {
+ assert(var != nullptr);
+ return var->castlingRookPiece;
+}
+
+inline PieceType Position::king_type() const {
+ assert(var != nullptr);
+ return var->kingType;
+}
+
+inline PieceType Position::nnue_king() const {
+ assert(var != nullptr);
+ return var->nnueKing;
+}
+
+inline bool Position::checking_permitted() const {
+ assert(var != nullptr);
+ return var->checking;
+}
+
+inline bool Position::drop_checks() const {
+ assert(var != nullptr);
+ return var->dropChecks;
+}
+
+inline bool Position::must_capture() const {
+ assert(var != nullptr);
+ return var->mustCapture;
+}
+
+inline bool Position::has_capture() const {
+ // Check for cached value
+ if (st->legalCapture != NO_VALUE)
+ return st->legalCapture == VALUE_TRUE;
+ if (checkers())
+ {
+ for (const auto& mevasion : MoveList<EVASIONS>(*this))
+ if (capture(mevasion) && legal(mevasion))
+ {
+ st->legalCapture = VALUE_TRUE;
+ return true;
+ }
+ }
+ else
+ {
+ for (const auto& mcap : MoveList<CAPTURES>(*this))
+ if (capture(mcap) && legal(mcap))
+ {
+ st->legalCapture = VALUE_TRUE;
+ return true;
+ }
+ }
+ st->legalCapture = VALUE_FALSE;
+ return false;
+}
+
+inline bool Position::must_drop() const {
+ assert(var != nullptr);
+ return var->mustDrop;
+}
+
+inline bool Position::piece_drops() const {
+ assert(var != nullptr);
+ return var->pieceDrops;
+}
+
+inline bool Position::drop_loop() const {
+ assert(var != nullptr);
+ return var->dropLoop;
+}
+
+inline bool Position::captures_to_hand() const {
+ assert(var != nullptr);
+ return var->capturesToHand;
+}
+
+inline bool Position::first_rank_pawn_drops() const {
+ assert(var != nullptr);
+ return var->firstRankPawnDrops;
+}
+
+inline bool Position::drop_on_top() const {
+ assert(var != nullptr);
+ return var->dropOnTop;
+}
+
+inline EnclosingRule Position::enclosing_drop() const {
+ assert(var != nullptr);
+ return var->enclosingDrop;
+}
+
+inline Bitboard Position::drop_region(Color c) const {
+ assert(var != nullptr);
+ return c == WHITE ? var->whiteDropRegion : var->blackDropRegion;
+}
+
+inline Bitboard Position::drop_region(Color c, PieceType pt) const {
+ Bitboard b = drop_region(c) & board_bb(c, pt);
+
+ // Connect4-style drops
+ if (drop_on_top())
+ b &= shift<NORTH>(pieces()) | Rank1BB;
+ // Pawns on back ranks
+ if (pt == PAWN)
+ {
+ if (!var->promotionZonePawnDrops)
+ b &= ~zone_bb(c, promotion_rank(), max_rank());
+ if (!first_rank_pawn_drops())
+ b &= ~rank_bb(relative_rank(c, RANK_1, max_rank()));
+ }
+ // Doubled shogi pawns
+ if (pt == drop_no_doubled())
+ for (File f = FILE_A; f <= max_file(); ++f)
+ if (popcount(file_bb(f) & pieces(c, pt)) >= var->dropNoDoubledCount)
+ b &= ~file_bb(f);
+ // Sittuyin rook drops
+ if (pt == ROOK && sittuyin_rook_drop())
+ b &= rank_bb(relative_rank(c, RANK_1, max_rank()));
+
+ // Filter out squares where the drop does not enclose at least one opponent's piece
+ if (enclosing_drop())
+ {
+ // Reversi start
+ if (var->enclosingDropStart & ~pieces())
+ b &= var->enclosingDropStart;
+ else
+ {
+ if (enclosing_drop() == REVERSI)
+ {
+ Bitboard theirs = pieces(~c);
+ b &= shift<NORTH >(theirs) | shift<SOUTH >(theirs)
+ | shift<NORTH_EAST>(theirs) | shift<SOUTH_WEST>(theirs)
+ | shift<EAST >(theirs) | shift<WEST >(theirs)
+ | shift<SOUTH_EAST>(theirs) | shift<NORTH_WEST>(theirs);
+ Bitboard b2 = b;
+ while (b2)
+ {
+ Square s = pop_lsb(b2);
+ if (!(attacks_bb(c, QUEEN, s, board_bb() & ~pieces(~c)) & ~PseudoAttacks[c][KING][s] & pieces(c)))
+ b ^= s;
+ }
+ }
+ else
+ {
+ assert(enclosing_drop() == ATAXX);
+ Bitboard ours = pieces(c);
+ b &= shift<NORTH >(ours) | shift<SOUTH >(ours)
+ | shift<NORTH_EAST>(ours) | shift<SOUTH_WEST>(ours)
+ | shift<EAST >(ours) | shift<WEST >(ours)
+ | shift<SOUTH_EAST>(ours) | shift<NORTH_WEST>(ours);
+ }
+ }
+ }
+
+ return b;
+}
+
+inline bool Position::sittuyin_rook_drop() const {
+ assert(var != nullptr);
+ return var->sittuyinRookDrop;
+}
+
+inline bool Position::drop_opposite_colored_bishop() const {
+ assert(var != nullptr);
+ return var->dropOppositeColoredBishop;
+}
+
+inline bool Position::drop_promoted() const {
+ assert(var != nullptr);
+ return var->dropPromoted;
+}
+
+inline PieceType Position::drop_no_doubled() const {
+ assert(var != nullptr);
+ return var->dropNoDoubled;
+}
+
+inline bool Position::immobility_illegal() const {
+ assert(var != nullptr);
+ return var->immobilityIllegal;
+}
+
+inline bool Position::gating() const {
+ assert(var != nullptr);
+ return var->gating;
+}
+
+inline bool Position::arrow_gating() const {
+ assert(var != nullptr);
+ return var->arrowGating;
+}
+
+inline bool Position::seirawan_gating() const {
+ assert(var != nullptr);
+ return var->seirawanGating;
+}
+
+inline bool Position::cambodian_moves() const {
+ assert(var != nullptr);
+ return var->cambodianMoves;
+}
+
+inline Bitboard Position::diagonal_lines() const {
+ assert(var != nullptr);
+ return var->diagonalLines;
+}
+
+inline bool Position::pass() const {
+ assert(var != nullptr);
+ return var->pass || var->passOnStalemate;
+}
+
+inline bool Position::pass_on_stalemate() const {
+ assert(var != nullptr);
+ return var->passOnStalemate;
+}
+
+inline Bitboard Position::promoted_soldiers(Color c) const {
+ assert(var != nullptr);
+ return pieces(c, SOLDIER) & zone_bb(c, var->soldierPromotionRank, max_rank());
+}
+
+inline bool Position::makpong() const {
+ assert(var != nullptr);
+ return var->makpongRule;
+}
+
+inline int Position::n_move_rule() const {
+ assert(var != nullptr);
+ return var->nMoveRule;
+}
+
+inline int Position::n_fold_rule() const {
+ assert(var != nullptr);
+ return var->nFoldRule;
+}
+
+inline EnclosingRule Position::flip_enclosed_pieces() const {
+ assert(var != nullptr);
+ return var->flipEnclosedPieces;
+}
+
+inline Value Position::stalemate_value(int ply) const {
+ assert(var != nullptr);
+ if (var->stalematePieceCount)
+ {
+ int c = count<ALL_PIECES>(sideToMove) - count<ALL_PIECES>(~sideToMove);
+ return c == 0 ? VALUE_DRAW : convert_mate_value(c < 0 ? var->stalemateValue : -var->stalemateValue, ply);
+ }
+ // Check for checkmate of pseudo-royal pieces
+ if (var->extinctionPseudoRoyal)
+ {
+ Bitboard pseudoRoyals = st->pseudoRoyals & pieces(sideToMove);
+ Bitboard pseudoRoyalsTheirs = st->pseudoRoyals & pieces(~sideToMove);
+ while (pseudoRoyals)
+ {
+ Square sr = pop_lsb(pseudoRoyals);
+ if ( !(blast_on_capture() && (pseudoRoyalsTheirs & attacks_bb<KING>(sr)))
+ && attackers_to(sr, ~sideToMove))
+ return convert_mate_value(var->checkmateValue, ply);
+ }
+ }
+ return convert_mate_value(var->stalemateValue, ply);
+}
+
+inline Value Position::checkmate_value(int ply) const {
+ assert(var != nullptr);
+ // Check for illegal mate by shogi pawn drop
+ if ( var->shogiPawnDropMateIllegal
+ && !(checkers() & ~pieces(SHOGI_PAWN))
+ && !st->capturedPiece
+ && st->pliesFromNull > 0
+ && (st->materialKey != st->previous->materialKey))
+ {
+ return mate_in(ply);
+ }
+ // Check for shatar mate rule
+ if (var->shatarMateRule)
+ {
+ // Mate by knight is illegal
+ if (!(checkers() & ~pieces(KNIGHT)))
+ return mate_in(ply);
+
+ StateInfo* stp = st;
+ while (stp->checkersBB)
+ {
+ // Return mate score if there is at least one shak in series of checks
+ if (stp->shak)
+ return convert_mate_value(var->checkmateValue, ply);
+
+ if (stp->pliesFromNull < 2)
+ break;
+
+ stp = stp->previous->previous;
+ }
+ // Niol
+ return VALUE_DRAW;
+ }
+ // Checkmate using virtual pieces
+ if (two_boards() && var->checkmateValue < VALUE_ZERO)
+ {
+ Value virtualMaterial = VALUE_ZERO;
+ for (PieceType pt : piece_types())
+ virtualMaterial += std::max(-count_in_hand(~sideToMove, pt), 0) * PieceValue[MG][pt];
+
+ if (virtualMaterial > 0)
+ return -VALUE_VIRTUAL_MATE + virtualMaterial / 20 + ply;
+ }
+ // Return mate value
+ return convert_mate_value(var->checkmateValue, ply);
+}
+
+inline Value Position::extinction_value(int ply) const {
+ assert(var != nullptr);
+ return convert_mate_value(var->extinctionValue, ply);
+}
+
+inline bool Position::extinction_claim() const {
+ assert(var != nullptr);
+ return var->extinctionClaim;
+}
+
+inline const std::set<PieceType>& Position::extinction_piece_types() const {
+ assert(var != nullptr);
+ return var->extinctionPieceTypes;
+}
+
+inline bool Position::extinction_single_piece() const {
+ assert(var != nullptr);
+ return var->extinctionValue == -VALUE_MATE
+ && std::any_of(var->extinctionPieceTypes.begin(),
+ var->extinctionPieceTypes.end(),
+ [](PieceType pt) { return pt != ALL_PIECES; });
+}
+
+inline int Position::extinction_piece_count() const {
+ assert(var != nullptr);
+ return var->extinctionPieceCount;
+}
+
+inline int Position::extinction_opponent_piece_count() const {
+ assert(var != nullptr);
+ return var->extinctionOpponentPieceCount;
+}
+
+inline PieceType Position::capture_the_flag_piece() const {
+ assert(var != nullptr);
+ return var->flagPiece;
+}
+
+inline Bitboard Position::capture_the_flag(Color c) const {
+ assert(var != nullptr);
+ return c == WHITE ? var->whiteFlag : var->blackFlag;
+}
+
+inline bool Position::flag_move() const {
+ assert(var != nullptr);
+ return var->flagMove;
+}
+
+inline bool Position::check_counting() const {
+ assert(var != nullptr);
+ return var->checkCounting;
+}
+
+inline int Position::connect_n() const {
+ assert(var != nullptr);
+ return var->connectN;
+}
+
+inline CheckCount Position::checks_remaining(Color c) const {
+ return st->checksRemaining[c];
+}
+
+inline MaterialCounting Position::material_counting() const {
+ assert(var != nullptr);
+ return var->materialCounting;
+}
+
+inline CountingRule Position::counting_rule() const {
+ assert(var != nullptr);
+ return var->countingRule;
+}
+
+inline bool Position::is_immediate_game_end() const {
+ Value result;
+ return is_immediate_game_end(result);
+}
+
+inline bool Position::is_optional_game_end() const {
+ Value result;
+ return is_optional_game_end(result);
+}
+
++inline bool Position::is_draw(int ply) const {
++ Value result;
++ return is_optional_game_end(result, ply);
++}
++
+inline bool Position::is_game_end(Value& result, int ply) const {
+ return is_immediate_game_end(result, ply) || is_optional_game_end(result, ply);
+}
+
inline Color Position::side_to_move() const {
return sideToMove;
}