From: Fabian Fichter Date: Sun, 18 Jul 2021 13:56:13 +0000 (+0200) Subject: Merge official-stockfish/master X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=2660f55243d2a291fa6c6bda8cf813c9d4cc8013;p=fairystockfish.git Merge official-stockfish/master bench: 6603046 --- 2660f55243d2a291fa6c6bda8cf813c9d4cc8013 diff --cc src/nnue/nnue_feature_transformer.h index 338ca53,59a965a..b4eaab3 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@@ -403,17 -403,17 +403,17 @@@ namespace Stockfish::Eval::NNUE // accumulator. Then, we update the current accumulator (pos.state()). // Gather all features to be updated. - const Square ksq = pos.square(perspective); + const Square ksq = pos.square(perspective, pos.nnue_king()); IndexList removed[2], added[2]; FeatureSet::append_changed_indices( - ksq, next, perspective, removed[0], added[0]); + ksq, next, perspective, removed[0], added[0], pos); for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) FeatureSet::append_changed_indices( - ksq, st2, perspective, removed[1], added[1]); + ksq, st2, perspective, removed[1], added[1], pos); // Mark the accumulators as computed. - next->accumulator.state[perspective] = COMPUTED; - pos.state()->accumulator.state[perspective] = COMPUTED; + next->accumulator.computed[perspective] = true; + pos.state()->accumulator.computed[perspective] = true; // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. StateInfo *states_to_update[3] = diff --cc src/position.cpp index 3649240,ba015d3..efcba0e --- a/src/position.cpp +++ b/src/position.cpp @@@ -309,187 -220,68 +309,185 @@@ Position& Position::set(const Variant* // 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()); @@@ -1337,12 -700,10 +1335,12 @@@ void Position::do_move(Move m, StateInf ++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; diff --cc src/position.h index 7655aa9,e6b072b..9127709 --- a/src/position.h +++ b/src/position.h @@@ -282,16 -156,9 +282,17 @@@ public 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; @@@ -346,554 -202,6 +347,559 @@@ private 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& 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 >& 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() == 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(*this)) + if (capture(mevasion) && legal(mevasion)) + { + st->legalCapture = VALUE_TRUE; + return true; + } + } + else + { + for (const auto& mcap : MoveList(*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(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(theirs) | shift(theirs) + | shift(theirs) | shift(theirs) + | shift(theirs) | shift(theirs) + | shift(theirs) | shift(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(ours) | shift(ours) + | shift(ours) | shift(ours) + | shift(ours) | shift(ours) + | shift(ours) | shift(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(sideToMove) - count(~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(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& 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; }