Spaces:
Running
Running
| /* | |
| Stockfish, a UCI chess playing engine derived from Glaurung 2.1 | |
| Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file) | |
| Stockfish is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation, either version 3 of the License, or | |
| (at your option) any later version. | |
| Stockfish is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | |
| using std::string; | |
| namespace Stockfish { | |
| namespace Zobrist { | |
| Key psq[PIECE_NB][SQUARE_NB]; | |
| Key enpassant[FILE_NB]; | |
| Key castling[CASTLING_RIGHT_NB]; | |
| Key side, noPawns; | |
| } | |
| namespace { | |
| constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); | |
| static constexpr Piece Pieces[] = {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, | |
| B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING}; | |
| } // namespace | |
| // Returns an ASCII representation of the position | |
| std::ostream& operator<<(std::ostream& os, const Position& pos) { | |
| os << "\n +---+---+---+---+---+---+---+---+\n"; | |
| for (Rank r = RANK_8;; --r) | |
| { | |
| for (File f = FILE_A; f <= FILE_H; ++f) | |
| os << " | " << PieceToChar[pos.piece_on(make_square(f, r))]; | |
| os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n"; | |
| if (r == RANK_1) | |
| break; | |
| } | |
| os << " a b c d e f g h\n" | |
| << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase << std::setfill('0') | |
| << std::setw(16) << pos.key() << std::setfill(' ') << std::dec << "\nCheckers: "; | |
| for (Bitboard b = pos.checkers(); b;) | |
| os << UCIEngine::square(pop_lsb(b)) << " "; | |
| if (Tablebases::MaxCardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) | |
| { | |
| StateInfo st; | |
| Position p; | |
| p.set(pos.fen(), pos.is_chess960(), &st); | |
| Tablebases::ProbeState s1, s2; | |
| Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1); | |
| int dtz = Tablebases::probe_dtz(p, &s2); | |
| os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")" | |
| << "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")"; | |
| } | |
| return os; | |
| } | |
| // Implements Marcel van Kervinck's cuckoo algorithm to detect repetition of positions | |
| // for 3-fold repetition draws. The algorithm uses two hash tables with Zobrist hashes | |
| // to allow fast detection of recurring positions. For details see: | |
| // http://web.archive.org/web/20201107002606/https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf | |
| // First and second hash functions for indexing the cuckoo tables | |
| inline int H1(Key h) { return h & 0x1fff; } | |
| inline int H2(Key h) { return (h >> 16) & 0x1fff; } | |
| // Cuckoo tables with Zobrist hashes of valid reversible moves, and the moves themselves | |
| std::array<Key, 8192> cuckoo; | |
| std::array<Move, 8192> cuckooMove; | |
| // Initializes at startup the various arrays used to compute hash keys | |
| void Position::init() { | |
| PRNG rng(1070372); | |
| for (Piece pc : Pieces) | |
| for (Square s = SQ_A1; s <= SQ_H8; ++s) | |
| Zobrist::psq[pc][s] = rng.rand<Key>(); | |
| // pawns on these squares will promote | |
| std::fill_n(Zobrist::psq[W_PAWN] + SQ_A8, 8, 0); | |
| std::fill_n(Zobrist::psq[B_PAWN], 8, 0); | |
| for (File f = FILE_A; f <= FILE_H; ++f) | |
| Zobrist::enpassant[f] = rng.rand<Key>(); | |
| for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr) | |
| Zobrist::castling[cr] = rng.rand<Key>(); | |
| Zobrist::side = rng.rand<Key>(); | |
| Zobrist::noPawns = rng.rand<Key>(); | |
| // Prepare the cuckoo tables | |
| cuckoo.fill(0); | |
| cuckooMove.fill(Move::none()); | |
| [[maybe_unused]] int count = 0; | |
| for (Piece pc : Pieces) | |
| for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) | |
| for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2) | |
| if ((type_of(pc) != PAWN) && (attacks_bb(type_of(pc), s1, 0) & s2)) | |
| { | |
| Move move = Move(s1, s2); | |
| Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side; | |
| int i = H1(key); | |
| while (true) | |
| { | |
| std::swap(cuckoo[i], key); | |
| std::swap(cuckooMove[i], move); | |
| if (move == Move::none()) // Arrived at empty slot? | |
| break; | |
| i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot | |
| } | |
| count++; | |
| } | |
| assert(count == 3668); | |
| } | |
| // Initializes the position object with the given FEN string. | |
| // This function is not very robust - make sure that input FENs are correct, | |
| // this is assumed to be the responsibility of the GUI. | |
| Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si) { | |
| /* | |
| A FEN string defines a particular position using only the ASCII character set. | |
| A FEN string contains six fields separated by a space. The fields are: | |
| 1) Piece placement (from white's perspective). Each rank is described, starting | |
| with rank 8 and ending with rank 1. Within each rank, the contents of each | |
| square are described from file A through file H. Following the Standard | |
| Algebraic Notation (SAN), each piece is identified by a single letter taken | |
| from the standard English names. White pieces are designated using upper-case | |
| letters ("PNBRQK") whilst Black uses lowercase ("pnbrqk"). Blank squares are | |
| noted using digits 1 through 8 (the number of blank squares), and "/" | |
| separates ranks. | |
| 2) Active color. "w" means white moves next, "b" means black. | |
| 3) Castling availability. If neither side can castle, this is "-". Otherwise, | |
| this has one or more letters: "K" (White can castle kingside), "Q" (White | |
| can castle queenside), "k" (Black can castle kingside), and/or "q" (Black | |
| can castle queenside). | |
| 4) En passant target square (in algebraic notation). If there's no en passant | |
| target square, this is "-". If a pawn has just made a 2-square move, this | |
| is the position "behind" the pawn. Following X-FEN standard, this is recorded | |
| only if there is a pawn in position to make an en passant capture, and if | |
| there really is a pawn that might have advanced two squares. | |
| 5) Halfmove clock. This is the number of halfmoves since the last pawn advance | |
| or capture. This is used to determine if a draw can be claimed under the | |
| fifty-move rule. | |
| 6) Fullmove number. The number of the full move. It starts at 1, and is | |
| incremented after Black's move. | |
| */ | |
| unsigned char col, row, token; | |
| size_t idx; | |
| Square sq = SQ_A8; | |
| std::istringstream ss(fenStr); | |
| std::memset(reinterpret_cast<char*>(this), 0, sizeof(Position)); | |
| std::memset(si, 0, sizeof(StateInfo)); | |
| st = si; | |
| ss >> std::noskipws; | |
| // 1. Piece placement | |
| while ((ss >> token) && !isspace(token)) | |
| { | |
| if (isdigit(token)) | |
| sq += (token - '0') * EAST; // Advance the given number of files | |
| else if (token == '/') | |
| sq += 2 * SOUTH; | |
| else if ((idx = PieceToChar.find(token)) != string::npos) | |
| { | |
| put_piece(Piece(idx), sq); | |
| ++sq; | |
| } | |
| } | |
| // 2. Active color | |
| ss >> token; | |
| sideToMove = (token == 'w' ? WHITE : BLACK); | |
| 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)) | |
| { | |
| Square rsq; | |
| Color c = islower(token) ? BLACK : WHITE; | |
| Piece rook = make_piece(c, ROOK); | |
| token = char(toupper(token)); | |
| if (token == 'K') | |
| for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) | |
| {} | |
| else if (token == 'Q') | |
| for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) | |
| {} | |
| else if (token >= 'A' && token <= 'H') | |
| rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1)); | |
| else | |
| continue; | |
| set_castling_right(c, rsq); | |
| } | |
| // 4. En passant square. | |
| // Ignore if square is invalid or not on side to move relative rank 6. | |
| bool enpassant = false, legalEP = false; | |
| if (((ss >> col) && (col >= 'a' && col <= 'h')) | |
| && ((ss >> row) && (row == (sideToMove == WHITE ? '6' : '3')))) | |
| { | |
| st->epSquare = make_square(File(col - 'a'), Rank(row - '1')); | |
| Bitboard pawns = attacks_bb<PAWN>(st->epSquare, ~sideToMove) & pieces(sideToMove, PAWN); | |
| Bitboard target = (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))); | |
| Bitboard occ = pieces() ^ target ^ st->epSquare; | |
| // 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 = | |
| pawns && target && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))); | |
| // If no pawn can execute the en passant capture without leaving the king in check, don't record the epSquare | |
| while (pawns) | |
| legalEP |= !(attackers_to(square<KING>(sideToMove), occ ^ pop_lsb(pawns)) | |
| & pieces(~sideToMove) & ~target); | |
| } | |
| if (!enpassant || !legalEP) | |
| st->epSquare = SQ_NONE; | |
| // 5-6. Halfmove clock and fullmove number | |
| ss >> std::skipws >> st->rule50 >> gamePly; | |
| // 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); | |
| chess960 = isChess960; | |
| set_state(); | |
| assert(pos_is_ok()); | |
| return *this; | |
| } | |
| // Helper function used to set castling | |
| // rights given the corresponding color and the rook starting square. | |
| void Position::set_castling_right(Color c, Square rfrom) { | |
| Square kfrom = square<KING>(c); | |
| CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE : QUEEN_SIDE); | |
| st->castlingRights |= cr; | |
| castlingRightsMask[kfrom] |= cr; | |
| castlingRightsMask[rfrom] |= cr; | |
| castlingRookSquare[cr] = rfrom; | |
| Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1); | |
| Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1); | |
| castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto)) & ~(kfrom | rfrom); | |
| } | |
| // Sets king attacks to detect if a move gives check | |
| void Position::set_check_info() const { | |
| update_slider_blockers(WHITE); | |
| update_slider_blockers(BLACK); | |
| Square ksq = square<KING>(~sideToMove); | |
| st->checkSquares[PAWN] = attacks_bb<PAWN>(ksq, ~sideToMove); | |
| st->checkSquares[KNIGHT] = attacks_bb<KNIGHT>(ksq); | |
| st->checkSquares[BISHOP] = attacks_bb<BISHOP>(ksq, pieces()); | |
| st->checkSquares[ROOK] = attacks_bb<ROOK>(ksq, pieces()); | |
| st->checkSquares[QUEEN] = st->checkSquares[BISHOP] | st->checkSquares[ROOK]; | |
| st->checkSquares[KING] = 0; | |
| } | |
| // Computes the hash keys of the position, and other | |
| // data that once computed is updated incrementally as moves are made. | |
| // The function is only used when a new position is set up | |
| void Position::set_state() const { | |
| st->key = 0; | |
| st->minorPieceKey = 0; | |
| st->nonPawnKey[WHITE] = st->nonPawnKey[BLACK] = 0; | |
| st->pawnKey = Zobrist::noPawns; | |
| st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO; | |
| st->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove); | |
| set_check_info(); | |
| for (Bitboard b = pieces(); b;) | |
| { | |
| Square s = pop_lsb(b); | |
| Piece pc = piece_on(s); | |
| st->key ^= Zobrist::psq[pc][s]; | |
| if (type_of(pc) == PAWN) | |
| st->pawnKey ^= Zobrist::psq[pc][s]; | |
| else | |
| { | |
| st->nonPawnKey[color_of(pc)] ^= Zobrist::psq[pc][s]; | |
| if (type_of(pc) != KING) | |
| { | |
| st->nonPawnMaterial[color_of(pc)] += PieceValue[pc]; | |
| if (type_of(pc) <= BISHOP) | |
| st->minorPieceKey ^= Zobrist::psq[pc][s]; | |
| } | |
| } | |
| } | |
| if (st->epSquare != SQ_NONE) | |
| st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; | |
| if (sideToMove == BLACK) | |
| st->key ^= Zobrist::side; | |
| st->key ^= Zobrist::castling[st->castlingRights]; | |
| st->materialKey = compute_material_key(); | |
| } | |
| Key Position::compute_material_key() const { | |
| Key k = 0; | |
| for (Piece pc : Pieces) | |
| for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) | |
| k ^= Zobrist::psq[pc][8 + cnt]; | |
| return k; | |
| } | |
| // Overload to initialize the position object with the given endgame code string | |
| // like "KBPKN". It's mainly a helper to get the material key out of an endgame code. | |
| Position& Position::set(const string& code, Color c, StateInfo* si) { | |
| assert(code[0] == 'K'); | |
| string sides[] = {code.substr(code.find('K', 1)), // Weak | |
| code.substr(0, std::min(code.find('v'), code.find('K', 1)))}; // Strong | |
| assert(sides[0].length() > 0 && sides[0].length() < 8); | |
| assert(sides[1].length() > 0 && sides[1].length() < 8); | |
| std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); | |
| string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/" + sides[1] | |
| + char(8 - sides[1].length() + '0') + "/8 w - - 0 10"; | |
| return set(fenStr, false, si); | |
| } | |
| // Returns a FEN representation of the position. In case of | |
| // Chess960 the Shredder-FEN notation is used. This is mainly a debugging function. | |
| string Position::fen() const { | |
| int emptyCnt; | |
| std::ostringstream ss; | |
| for (Rank r = RANK_8;; --r) | |
| { | |
| for (File f = FILE_A; f <= FILE_H; ++f) | |
| { | |
| for (emptyCnt = 0; f <= FILE_H && empty(make_square(f, r)); ++f) | |
| ++emptyCnt; | |
| if (emptyCnt) | |
| ss << emptyCnt; | |
| if (f <= FILE_H) | |
| ss << PieceToChar[piece_on(make_square(f, r))]; | |
| } | |
| if (r == RANK_1) | |
| break; | |
| ss << '/'; | |
| } | |
| ss << (sideToMove == WHITE ? " w " : " b "); | |
| if (can_castle(WHITE_OO)) | |
| ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OO))) : 'K'); | |
| if (can_castle(WHITE_OOO)) | |
| ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q'); | |
| if (can_castle(BLACK_OO)) | |
| ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OO))) : 'k'); | |
| if (can_castle(BLACK_OOO)) | |
| ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q'); | |
| if (!can_castle(ANY_CASTLING)) | |
| ss << '-'; | |
| ss << (ep_square() == SQ_NONE ? " - " : " " + UCIEngine::square(ep_square()) + " ") | |
| << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; | |
| return ss.str(); | |
| } | |
| // Calculates st->blockersForKing[c] and st->pinners[~c], | |
| // which store respectively the pieces preventing king of color c from being in check | |
| // and the slider pieces of color ~c pinning pieces of color c to the king. | |
| void Position::update_slider_blockers(Color c) const { | |
| Square ksq = square<KING>(c); | |
| st->blockersForKing[c] = 0; | |
| st->pinners[~c] = 0; | |
| // Snipers are sliders that attack 's' when a piece and other snipers are removed | |
| Bitboard snipers = ((attacks_bb<ROOK>(ksq) & pieces(QUEEN, ROOK)) | |
| | (attacks_bb<BISHOP>(ksq) & pieces(QUEEN, BISHOP))) | |
| & pieces(~c); | |
| Bitboard occupancy = pieces() ^ snipers; | |
| while (snipers) | |
| { | |
| Square sniperSq = pop_lsb(snipers); | |
| Bitboard b = between_bb(ksq, sniperSq) & occupancy; | |
| if (b && !more_than_one(b)) | |
| { | |
| st->blockersForKing[c] |= b; | |
| if (b & pieces(c)) | |
| st->pinners[~c] |= sniperSq; | |
| } | |
| } | |
| } | |
| // Computes a bitboard of all pieces which attack a given square. | |
| // Slider attacks use the occupied bitboard to indicate occupancy. | |
| Bitboard Position::attackers_to(Square s, Bitboard occupied) const { | |
| return (attacks_bb<ROOK>(s, occupied) & pieces(ROOK, QUEEN)) | |
| | (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN)) | |
| | (attacks_bb<PAWN>(s, BLACK) & pieces(WHITE, PAWN)) | |
| | (attacks_bb<PAWN>(s, WHITE) & pieces(BLACK, PAWN)) | |
| | (attacks_bb<KNIGHT>(s) & pieces(KNIGHT)) | (attacks_bb<KING>(s) & pieces(KING)); | |
| } | |
| bool Position::attackers_to_exist(Square s, Bitboard occupied, Color c) const { | |
| return (attacks_bb<ROOK>(s, occupied) & pieces(c, ROOK, QUEEN)) | |
| || (attacks_bb<BISHOP>(s, occupied) & pieces(c, BISHOP, QUEEN)) | |
| || (attacks_bb<PAWN>(s, ~c) & pieces(c, PAWN)) | |
| || (attacks_bb<KNIGHT>(s) & pieces(c, KNIGHT)) || (attacks_bb<KING>(s) & pieces(c, KING)); | |
| } | |
| // Tests whether a pseudo-legal move is legal | |
| bool Position::legal(Move m) const { | |
| assert(m.is_ok()); | |
| Color us = sideToMove; | |
| Square from = m.from_sq(); | |
| Square to = m.to_sq(); | |
| assert(color_of(moved_piece(m)) == us); | |
| assert(piece_on(square<KING>(us)) == make_piece(us, KING)); | |
| // En passant captures are a tricky special case. Because they are rather | |
| // uncommon, we do it simply by testing whether the king is attacked after | |
| // the move is made. | |
| if (m.type_of() == EN_PASSANT) | |
| { | |
| Square ksq = square<KING>(us); | |
| Square capsq = to - pawn_push(us); | |
| Bitboard occupied = (pieces() ^ from ^ capsq) | to; | |
| assert(to == ep_square()); | |
| assert(moved_piece(m) == make_piece(us, PAWN)); | |
| assert(piece_on(capsq) == make_piece(~us, PAWN)); | |
| assert(piece_on(to) == NO_PIECE); | |
| return !(attacks_bb<ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK)) | |
| && !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); | |
| } | |
| // Castling moves generation does not check if the castling path is clear of | |
| // enemy attacks, it is delayed at a later time: now! | |
| if (m.type_of() == CASTLING) | |
| { | |
| // After castling, the rook and king final positions are the same in | |
| // Chess960 as they would be in standard chess. | |
| to = relative_square(us, to > from ? SQ_G1 : SQ_C1); | |
| Direction step = to > from ? WEST : EAST; | |
| for (Square s = to; s != from; s += step) | |
| if (attackers_to_exist(s, pieces(), ~us)) | |
| return false; | |
| // In case of Chess960, verify if the Rook blocks some checks. | |
| // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. | |
| return !chess960 || !(blockers_for_king(us) & m.to_sq()); | |
| } | |
| // If the moving piece is a king, check whether the destination square is | |
| // attacked by the opponent. | |
| if (type_of(piece_on(from)) == KING) | |
| return !(attackers_to_exist(to, pieces() ^ from, ~us)); | |
| // A non-king move is legal if and only if it is not pinned or it | |
| // is moving along the ray towards or away from the king. | |
| return !(blockers_for_king(us) & from) || line_bb(from, to) & pieces(us, KING); | |
| } | |
| // Takes a random move and tests whether the move is | |
| // pseudo-legal. It is used to validate moves from TT that can be corrupted | |
| // due to SMP concurrent access or hash position key aliasing. | |
| bool Position::pseudo_legal(const Move m) const { | |
| Color us = sideToMove; | |
| Square from = m.from_sq(); | |
| Square to = m.to_sq(); | |
| Piece pc = moved_piece(m); | |
| // Use a slower but simpler function for uncommon cases | |
| // yet we skip the legality check of MoveList<LEGAL>(). | |
| if (m.type_of() != NORMAL) | |
| return checkers() ? MoveList<EVASIONS>(*this).contains(m) | |
| : MoveList<NON_EVASIONS>(*this).contains(m); | |
| // Is not a promotion, so the promotion piece must be empty | |
| assert(m.promotion_type() - KNIGHT == NO_PIECE_TYPE); | |
| // If the 'from' square is not occupied by a piece belonging to the side to | |
| // move, the move is obviously not legal. | |
| if (pc == NO_PIECE || color_of(pc) != us) | |
| return false; | |
| // The destination square cannot be occupied by a friendly piece | |
| if (pieces(us) & to) | |
| return false; | |
| // Handle the special case of a pawn move | |
| if (type_of(pc) == PAWN) | |
| { | |
| // We have already handled promotion moves, so destination cannot be on the 8th/1st rank | |
| if ((Rank8BB | Rank1BB) & to) | |
| return false; | |
| // Check if it's a valid capture, single push, or double push | |
| const bool isCapture = bool(attacks_bb<PAWN>(from, us) & pieces(~us) & to); | |
| const bool isSinglePush = (from + pawn_push(us) == to) && empty(to); | |
| const bool isDoublePush = (from + 2 * pawn_push(us) == to) | |
| && (relative_rank(us, from) == RANK_2) && empty(to) | |
| && empty(to - pawn_push(us)); | |
| if (!(isCapture || isSinglePush || isDoublePush)) | |
| return false; | |
| } | |
| else if (!(attacks_bb(type_of(pc), from, pieces()) & to)) | |
| return false; | |
| // Evasions generator already takes care to avoid some kind of illegal moves | |
| // and legal() relies on this. We therefore have to take care that the same | |
| // kind of moves are filtered out here. | |
| if (checkers()) | |
| { | |
| if (type_of(pc) != KING) | |
| { | |
| // Double check? In this case, a king move is required | |
| if (more_than_one(checkers())) | |
| return false; | |
| // Our move must be a blocking interposition or a capture of the checking piece | |
| if (!(between_bb(square<KING>(us), lsb(checkers())) & to)) | |
| return false; | |
| } | |
| // In case of king moves under check we have to remove the king so as to catch | |
| // invalid moves like b1a1 when opposite queen is on c1. | |
| else if (attackers_to_exist(to, pieces() ^ from, ~us)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| // Tests whether a pseudo-legal move gives a check | |
| bool Position::gives_check(Move m) const { | |
| assert(m.is_ok()); | |
| assert(color_of(moved_piece(m)) == sideToMove); | |
| Square from = m.from_sq(); | |
| Square to = m.to_sq(); | |
| // Is there a direct check? | |
| if (check_squares(type_of(piece_on(from))) & to) | |
| return true; | |
| // Is there a discovered check? | |
| if (blockers_for_king(~sideToMove) & from) | |
| return !(line_bb(from, to) & pieces(~sideToMove, KING)) || m.type_of() == CASTLING; | |
| switch (m.type_of()) | |
| { | |
| case NORMAL : | |
| return false; | |
| case PROMOTION : | |
| return attacks_bb(m.promotion_type(), to, pieces() ^ from) & pieces(~sideToMove, KING); | |
| // En passant capture with check? We have already handled the case of direct | |
| // checks and ordinary discovered check, so the only case we need to handle | |
| // is the unusual case of a discovered check through the captured pawn. | |
| case EN_PASSANT : { | |
| Square capsq = make_square(file_of(to), rank_of(from)); | |
| Bitboard b = (pieces() ^ from ^ capsq) | to; | |
| return (attacks_bb<ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) | |
| | (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) | |
| & pieces(sideToMove, QUEEN, BISHOP)); | |
| } | |
| default : //CASTLING | |
| { | |
| // Castling is encoded as 'king captures the rook' | |
| Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1); | |
| return check_squares(ROOK) & rto; | |
| } | |
| } | |
| } | |
| // Makes a move, and saves all information necessary | |
| // to a StateInfo object. The move is assumed to be legal. Pseudo-legal | |
| // moves should be filtered out before this function is called. | |
| // If a pointer to the TT table is passed, the entry for the new position | |
| // will be prefetched, and likewise for shared history. | |
| void Position::do_move(Move m, | |
| StateInfo& newSt, | |
| bool givesCheck, | |
| DirtyPiece& dp, | |
| DirtyThreats& dts, | |
| const TranspositionTable* tt = nullptr, | |
| const SharedHistories* history = nullptr) { | |
| assert(m.is_ok()); | |
| assert(&newSt != st); | |
| Key k = st->key ^ Zobrist::side; | |
| // Copy some fields of the old state to our new StateInfo object except the | |
| // ones which are going to be recalculated from scratch anyway and then switch | |
| // our state pointer to point to the new (ready to be updated) state. | |
| std::memcpy(&newSt, st, offsetof(StateInfo, key)); | |
| newSt.previous = st; | |
| st = &newSt; | |
| // Increment ply counters. In particular, rule50 will be reset to zero later on | |
| // in case of a capture or a pawn move. | |
| ++gamePly; | |
| ++st->rule50; | |
| ++st->pliesFromNull; | |
| Color us = sideToMove; | |
| Color them = ~us; | |
| Square from = m.from_sq(); | |
| Square to = m.to_sq(); | |
| Piece pc = piece_on(from); | |
| Piece captured = m.type_of() == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to); | |
| dp.pc = pc; | |
| dp.from = from; | |
| dp.to = to; | |
| dp.add_sq = SQ_NONE; | |
| dts.us = us; | |
| dts.prevKsq = square<KING>(us); | |
| dts.threatenedSqs = dts.threateningSqs = 0; | |
| assert(color_of(pc) == us); | |
| assert(captured == NO_PIECE || color_of(captured) == (m.type_of() != CASTLING ? them : us)); | |
| assert(type_of(captured) != KING); | |
| if (m.type_of() == CASTLING) | |
| { | |
| assert(pc == make_piece(us, KING)); | |
| assert(captured == make_piece(us, ROOK)); | |
| Square rfrom, rto; | |
| do_castling<true>(us, from, to, rfrom, rto, &dts, &dp); | |
| k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; | |
| st->nonPawnKey[us] ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; | |
| captured = NO_PIECE; | |
| } | |
| else if (captured) | |
| { | |
| Square capsq = to; | |
| // If the captured piece is a pawn, update pawn hash key, otherwise | |
| // update non-pawn material. | |
| if (type_of(captured) == PAWN) | |
| { | |
| if (m.type_of() == EN_PASSANT) | |
| { | |
| capsq -= pawn_push(us); | |
| assert(pc == make_piece(us, PAWN)); | |
| assert(to == st->epSquare); | |
| assert(relative_rank(us, to) == RANK_6); | |
| assert(piece_on(to) == NO_PIECE); | |
| assert(piece_on(capsq) == make_piece(them, PAWN)); | |
| // Update board and piece lists in ep case, normal captures are updated later | |
| remove_piece(capsq, &dts); | |
| } | |
| st->pawnKey ^= Zobrist::psq[captured][capsq]; | |
| } | |
| else | |
| { | |
| st->nonPawnMaterial[them] -= PieceValue[captured]; | |
| st->nonPawnKey[them] ^= Zobrist::psq[captured][capsq]; | |
| if (type_of(captured) <= BISHOP) | |
| st->minorPieceKey ^= Zobrist::psq[captured][capsq]; | |
| } | |
| dp.remove_pc = captured; | |
| dp.remove_sq = capsq; | |
| k ^= Zobrist::psq[captured][capsq]; | |
| st->materialKey ^= | |
| Zobrist::psq[captured][8 + pieceCount[captured] - (m.type_of() != EN_PASSANT)]; | |
| // Reset rule 50 counter | |
| st->rule50 = 0; | |
| } | |
| else | |
| dp.remove_sq = SQ_NONE; | |
| // Update hash key | |
| k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; | |
| // Reset en passant square | |
| if (st->epSquare != SQ_NONE) | |
| { | |
| k ^= Zobrist::enpassant[file_of(st->epSquare)]; | |
| st->epSquare = SQ_NONE; | |
| } | |
| // Update castling rights. | |
| k ^= Zobrist::castling[st->castlingRights]; | |
| st->castlingRights &= ~(castlingRightsMask[from] | castlingRightsMask[to]); | |
| k ^= Zobrist::castling[st->castlingRights]; | |
| // Move the piece. The tricky Chess960 castling is handled earlier | |
| if (m.type_of() != CASTLING) | |
| { | |
| if (captured && m.type_of() != EN_PASSANT) | |
| { | |
| remove_piece(from, &dts); | |
| swap_piece(to, pc, &dts); | |
| } | |
| else | |
| move_piece(from, to, &dts); | |
| } | |
| // If the moving piece is a pawn do some special extra work | |
| if (type_of(pc) == PAWN) | |
| { | |
| // Check if the en passant square needs to be set. Accurate e.p. info is needed | |
| // for correct zobrist key generation and 3-fold checking. | |
| if ((int(to) ^ int(from)) == 16) | |
| { | |
| Square epSquare = to - pawn_push(us); | |
| Bitboard pawns = attacks_bb<PAWN>(epSquare, us) & pieces(them, PAWN); | |
| // If there are no pawns attacking the ep square, ep is not possible. | |
| if (pawns) | |
| { | |
| Square ksq = square<KING>(them); | |
| Bitboard notBlockers = ~st->previous->blockersForKing[them]; | |
| bool noDiscovery = (from & notBlockers) || file_of(from) == file_of(ksq); | |
| // If the pawn gives discovered check, ep is never legal. Else, if at least one | |
| // pawn was not a blocker for the enemy king or lies on the same line as the | |
| // enemy king and en passant square, a legal capture exists. | |
| if (noDiscovery && (pawns & (notBlockers | line_bb(epSquare, ksq)))) | |
| { | |
| st->epSquare = epSquare; | |
| k ^= Zobrist::enpassant[file_of(epSquare)]; | |
| } | |
| } | |
| } | |
| else if (m.type_of() == PROMOTION) | |
| { | |
| Piece promotion = make_piece(us, m.promotion_type()); | |
| PieceType promotionType = type_of(promotion); | |
| assert(relative_rank(us, to) == RANK_8); | |
| assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); | |
| swap_piece(to, promotion, &dts); | |
| dp.add_pc = promotion; | |
| dp.add_sq = to; | |
| dp.to = SQ_NONE; | |
| // Update hash keys | |
| // Zobrist::psq[pc][to] is zero, so we don't need to clear it | |
| k ^= Zobrist::psq[promotion][to]; | |
| st->materialKey ^= Zobrist::psq[promotion][8 + pieceCount[promotion] - 1] | |
| ^ Zobrist::psq[pc][8 + pieceCount[pc]]; | |
| st->nonPawnKey[us] ^= Zobrist::psq[promotion][to]; | |
| if (promotionType <= BISHOP) | |
| st->minorPieceKey ^= Zobrist::psq[promotion][to]; | |
| // Update material | |
| st->nonPawnMaterial[us] += PieceValue[promotion]; | |
| } | |
| // Update pawn hash key | |
| st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; | |
| // Reset rule 50 draw counter | |
| st->rule50 = 0; | |
| } | |
| else | |
| { | |
| st->nonPawnKey[us] ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; | |
| if (type_of(pc) <= BISHOP) | |
| st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; | |
| } | |
| // Update the key with the final value | |
| st->key = k; | |
| if (tt) | |
| prefetch(tt->first_entry(key())); | |
| if (history) | |
| { | |
| prefetch(&history->pawn_entry(*this)[pc][to]); | |
| prefetch(&history->pawn_correction_entry(*this)); | |
| prefetch(&history->minor_piece_correction_entry(*this)); | |
| prefetch(&history->nonpawn_correction_entry<WHITE>(*this)); | |
| prefetch(&history->nonpawn_correction_entry<BLACK>(*this)); | |
| } | |
| // Set capture piece | |
| st->capturedPiece = captured; | |
| // Calculate checkers bitboard (if move gives check) | |
| st->checkersBB = givesCheck ? attackers_to(square<KING>(them)) & pieces(us) : 0; | |
| sideToMove = ~sideToMove; | |
| // Update king attacks used for fast check detection | |
| set_check_info(); | |
| // Calculate the repetition info. It is the ply distance from the previous | |
| // occurrence of the same position, negative in the 3-fold case, or zero | |
| // if the position was not repeated. | |
| st->repetition = 0; | |
| int end = std::min(st->rule50, st->pliesFromNull); | |
| if (end >= 4) | |
| { | |
| StateInfo* stp = st->previous->previous; | |
| for (int i = 4; i <= end; i += 2) | |
| { | |
| stp = stp->previous->previous; | |
| if (stp->key == st->key) | |
| { | |
| st->repetition = stp->repetition ? -i : i; | |
| break; | |
| } | |
| } | |
| } | |
| dts.ksq = square<KING>(us); | |
| assert(pos_is_ok()); | |
| assert(dp.pc != NO_PIECE); | |
| assert(!(bool(captured) || m.type_of() == CASTLING) ^ (dp.remove_sq != SQ_NONE)); | |
| assert(dp.from != SQ_NONE); | |
| assert(!(dp.add_sq != SQ_NONE) ^ (m.type_of() == PROMOTION || m.type_of() == CASTLING)); | |
| } | |
| // Unmakes a move. When it returns, the position should | |
| // be restored to exactly the same state as before the move was made. | |
| void Position::undo_move(Move m) { | |
| assert(m.is_ok()); | |
| sideToMove = ~sideToMove; | |
| Color us = sideToMove; | |
| Square from = m.from_sq(); | |
| Square to = m.to_sq(); | |
| Piece pc = piece_on(to); | |
| assert(empty(from) || m.type_of() == CASTLING); | |
| assert(type_of(st->capturedPiece) != KING); | |
| if (m.type_of() == PROMOTION) | |
| { | |
| assert(relative_rank(us, to) == RANK_8); | |
| assert(type_of(pc) == m.promotion_type()); | |
| assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN); | |
| remove_piece(to); | |
| pc = make_piece(us, PAWN); | |
| put_piece(pc, to); | |
| } | |
| if (m.type_of() == CASTLING) | |
| { | |
| Square rfrom, rto; | |
| do_castling<false>(us, from, to, rfrom, rto); | |
| } | |
| else | |
| { | |
| move_piece(to, from); // Put the piece back at the source square | |
| if (st->capturedPiece) | |
| { | |
| Square capsq = to; | |
| if (m.type_of() == EN_PASSANT) | |
| { | |
| capsq -= pawn_push(us); | |
| assert(type_of(pc) == PAWN); | |
| assert(to == st->previous->epSquare); | |
| assert(relative_rank(us, to) == RANK_6); | |
| assert(piece_on(capsq) == NO_PIECE); | |
| assert(st->capturedPiece == make_piece(~us, PAWN)); | |
| } | |
| put_piece(st->capturedPiece, capsq); // Restore the captured piece | |
| } | |
| } | |
| // Finally point our state pointer back to the previous state | |
| st = st->previous; | |
| --gamePly; | |
| assert(pos_is_ok()); | |
| } | |
| template<bool PutPiece> | |
| inline void add_dirty_threat( | |
| DirtyThreats* const dts, Piece pc, Piece threatened, Square s, Square threatenedSq) { | |
| if (PutPiece) | |
| { | |
| dts->threatenedSqs |= threatenedSq; | |
| dts->threateningSqs |= s; | |
| } | |
| dts->list.push_back({pc, threatened, s, threatenedSq, PutPiece}); | |
| } | |
| // Given a DirtyThreat template and bit offsets to insert the piece type and square, write the threats | |
| // present at the given bitboard. | |
| template<int SqShift, int PcShift> | |
| void write_multiple_dirties(const Position& p, | |
| Bitboard mask, | |
| DirtyThreat dt_template, | |
| DirtyThreats* dts) { | |
| static_assert(sizeof(DirtyThreat) == 4); | |
| const __m512i board = _mm512_loadu_si512(p.piece_array().data()); | |
| const __m512i AllSquares = _mm512_set_epi8( | |
| 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, | |
| 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, | |
| 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); | |
| const int dt_count = popcount(mask); | |
| assert(dt_count <= 16); | |
| const __m512i template_v = _mm512_set1_epi32(dt_template.raw()); | |
| auto* write = dts->list.make_space(dt_count); | |
| // Extract the list of squares and upconvert to 32 bits. There are never more than 16 | |
| // incoming threats so this is sufficient. | |
| __m512i threat_squares = _mm512_maskz_compress_epi8(mask, AllSquares); | |
| threat_squares = _mm512_cvtepi8_epi32(_mm512_castsi512_si128(threat_squares)); | |
| __m512i threat_pieces = | |
| _mm512_maskz_permutexvar_epi8(0x1111111111111111ULL, threat_squares, board); | |
| // Shift the piece and square into place | |
| threat_squares = _mm512_slli_epi32(threat_squares, SqShift); | |
| threat_pieces = _mm512_slli_epi32(threat_pieces, PcShift); | |
| const __m512i dirties = | |
| _mm512_ternarylogic_epi32(template_v, threat_squares, threat_pieces, 254 /* A | B | C */); | |
| _mm512_storeu_si512(write, dirties); | |
| } | |
| template<bool PutPiece, bool ComputeRay> | |
| void Position::update_piece_threats(Piece pc, | |
| Square s, | |
| DirtyThreats* const dts, | |
| [[maybe_unused]] Bitboard noRaysContaining) const { | |
| const Bitboard occupied = pieces(); | |
| const Bitboard rookQueens = pieces(ROOK, QUEEN); | |
| const Bitboard bishopQueens = pieces(BISHOP, QUEEN); | |
| const Bitboard rAttacks = attacks_bb<ROOK>(s, occupied); | |
| const Bitboard bAttacks = attacks_bb<BISHOP>(s, occupied); | |
| const Bitboard kings = pieces(KING); | |
| Bitboard occupiedNoK = occupied ^ kings; | |
| Bitboard sliders = (rookQueens & rAttacks) | (bishopQueens & bAttacks); | |
| auto process_sliders = [&](bool addDirectAttacks) { | |
| while (sliders) | |
| { | |
| Square sliderSq = pop_lsb(sliders); | |
| Piece slider = piece_on(sliderSq); | |
| const Bitboard ray = RayPassBB[sliderSq][s]; | |
| const Bitboard discovered = ray & (rAttacks | bAttacks) & occupiedNoK; | |
| assert(!more_than_one(discovered)); | |
| if (discovered && (RayPassBB[sliderSq][s] & noRaysContaining) != noRaysContaining) | |
| { | |
| const Square threatenedSq = lsb(discovered); | |
| const Piece threatenedPc = piece_on(threatenedSq); | |
| add_dirty_threat<!PutPiece>(dts, slider, threatenedPc, sliderSq, threatenedSq); | |
| } | |
| if (addDirectAttacks) | |
| add_dirty_threat<PutPiece>(dts, slider, pc, sliderSq, s); | |
| } | |
| }; | |
| if (type_of(pc) == KING) | |
| { | |
| if constexpr (ComputeRay) | |
| process_sliders(false); | |
| return; | |
| } | |
| const Bitboard knights = pieces(KNIGHT); | |
| const Bitboard whitePawns = pieces(WHITE, PAWN); | |
| const Bitboard blackPawns = pieces(BLACK, PAWN); | |
| Bitboard threatened = attacks_bb(pc, s, occupied) & occupiedNoK; | |
| Bitboard incoming_threats = | |
| (PseudoAttacks[KNIGHT][s] & knights) | (attacks_bb<PAWN>(s, WHITE) & blackPawns) | |
| | (attacks_bb<PAWN>(s, BLACK) & whitePawns) | (PseudoAttacks[KING][s] & kings); | |
| if constexpr (PutPiece) | |
| { | |
| dts->threatenedSqs |= threatened; | |
| // A bit may only be set if that square actually produces a threat, so we | |
| // must guard setting the square accordingly | |
| dts->threateningSqs |= Bitboard(bool(threatened)) << s; | |
| } | |
| DirtyThreat dt_template{pc, NO_PIECE, s, Square(0), PutPiece}; | |
| write_multiple_dirties<DirtyThreat::ThreatenedSqOffset, DirtyThreat::ThreatenedPcOffset>( | |
| *this, threatened, dt_template, dts); | |
| Bitboard all_attackers = sliders | incoming_threats; | |
| if constexpr (PutPiece) | |
| { | |
| dts->threatenedSqs |= Bitboard(bool(all_attackers)) << s; // same as above | |
| dts->threateningSqs |= all_attackers; | |
| } | |
| dt_template = {NO_PIECE, pc, Square(0), s, PutPiece}; | |
| write_multiple_dirties<DirtyThreat::PcSqOffset, DirtyThreat::PcOffset>(*this, all_attackers, | |
| dt_template, dts); | |
| while (threatened) | |
| { | |
| Square threatenedSq = pop_lsb(threatened); | |
| Piece threatenedPc = piece_on(threatenedSq); | |
| assert(threatenedSq != s); | |
| assert(threatenedPc); | |
| add_dirty_threat<PutPiece>(dts, pc, threatenedPc, s, threatenedSq); | |
| } | |
| if constexpr (ComputeRay) | |
| { | |
| process_sliders(true); | |
| process_sliders(false); | |
| } | |
| else | |
| { | |
| incoming_threats |= sliders; | |
| } | |
| while (incoming_threats) | |
| { | |
| Square srcSq = pop_lsb(incoming_threats); | |
| Piece srcPc = piece_on(srcSq); | |
| assert(srcSq != s); | |
| assert(srcPc != NO_PIECE); | |
| add_dirty_threat<PutPiece>(dts, srcPc, pc, srcSq, s); | |
| } | |
| } | |
| // Helper used to do/undo a castling move. This is a bit | |
| // tricky in Chess960 where from/to squares can overlap. | |
| template<bool Do> | |
| void Position::do_castling(Color us, | |
| Square from, | |
| Square& to, | |
| Square& rfrom, | |
| Square& rto, | |
| DirtyThreats* const dts, | |
| DirtyPiece* const dp) { | |
| bool kingSide = to > from; | |
| rfrom = to; // Castling is encoded as "king captures friendly rook" | |
| rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); | |
| to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); | |
| assert(!Do || dp); | |
| if (Do) | |
| { | |
| dp->to = to; | |
| dp->remove_pc = dp->add_pc = make_piece(us, ROOK); | |
| dp->remove_sq = rfrom; | |
| dp->add_sq = rto; | |
| } | |
| // Remove both pieces first since squares could overlap in Chess960 | |
| remove_piece(Do ? from : to, dts); | |
| remove_piece(Do ? rfrom : rto, dts); | |
| put_piece(make_piece(us, KING), Do ? to : from, dts); | |
| put_piece(make_piece(us, ROOK), Do ? rto : rfrom, dts); | |
| } | |
| // Used to do a "null move": it flips | |
| // the side to move without executing any move on the board. | |
| void Position::do_null_move(StateInfo& newSt) { | |
| assert(!checkers()); | |
| assert(&newSt != st); | |
| std::memcpy(&newSt, st, sizeof(StateInfo)); | |
| newSt.previous = st; | |
| st = &newSt; | |
| if (st->epSquare != SQ_NONE) | |
| { | |
| st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; | |
| st->epSquare = SQ_NONE; | |
| } | |
| st->key ^= Zobrist::side; | |
| st->pliesFromNull = 0; | |
| sideToMove = ~sideToMove; | |
| set_check_info(); | |
| st->repetition = 0; | |
| assert(pos_is_ok()); | |
| } | |
| // Must be used to undo a "null move" | |
| void Position::undo_null_move() { | |
| assert(!checkers()); | |
| st = st->previous; | |
| sideToMove = ~sideToMove; | |
| } | |
| // Tests if the SEE (Static Exchange Evaluation) | |
| // value of move is greater or equal to the given threshold. We'll use an | |
| // algorithm similar to alpha-beta pruning with a null window. | |
| bool Position::see_ge(Move m, int threshold) const { | |
| assert(m.is_ok()); | |
| // Only deal with normal moves, assume others pass a simple SEE | |
| if (m.type_of() != NORMAL) | |
| return VALUE_ZERO >= threshold; | |
| Square from = m.from_sq(), to = m.to_sq(); | |
| assert(piece_on(from) != NO_PIECE); | |
| int swap = PieceValue[piece_on(to)] - threshold; | |
| if (swap < 0) | |
| return false; | |
| swap = PieceValue[piece_on(from)] - swap; | |
| if (swap <= 0) | |
| return true; | |
| assert(color_of(piece_on(from)) == sideToMove); | |
| Bitboard occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic | |
| Color stm = sideToMove; | |
| Bitboard attackers = attackers_to(to, occupied); | |
| Bitboard stmAttackers, bb; | |
| int res = 1; | |
| while (true) | |
| { | |
| stm = ~stm; | |
| attackers &= occupied; | |
| // If stm has no more attackers then give up: stm loses | |
| if (!(stmAttackers = attackers & pieces(stm))) | |
| break; | |
| // Don't allow pinned pieces to attack as long as there are | |
| // pinners on their original square. | |
| if (pinners(~stm) & occupied) | |
| { | |
| stmAttackers &= ~blockers_for_king(stm); | |
| if (!stmAttackers) | |
| break; | |
| } | |
| res ^= 1; | |
| // Locate and remove the next least valuable attacker, and add to | |
| // the bitboard 'attackers' any X-ray attackers behind it. | |
| if ((bb = stmAttackers & pieces(PAWN))) | |
| { | |
| if ((swap = PawnValue - swap) < res) | |
| break; | |
| occupied ^= least_significant_square_bb(bb); | |
| attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN); | |
| } | |
| else if ((bb = stmAttackers & pieces(KNIGHT))) | |
| { | |
| if ((swap = KnightValue - swap) < res) | |
| break; | |
| occupied ^= least_significant_square_bb(bb); | |
| } | |
| else if ((bb = stmAttackers & pieces(BISHOP))) | |
| { | |
| if ((swap = BishopValue - swap) < res) | |
| break; | |
| occupied ^= least_significant_square_bb(bb); | |
| attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN); | |
| } | |
| else if ((bb = stmAttackers & pieces(ROOK))) | |
| { | |
| if ((swap = RookValue - swap) < res) | |
| break; | |
| occupied ^= least_significant_square_bb(bb); | |
| attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN); | |
| } | |
| else if ((bb = stmAttackers & pieces(QUEEN))) | |
| { | |
| swap = QueenValue - swap; | |
| // implies that the previous recapture was done by a higher rated piece than a Queen (King is excluded) | |
| assert(swap >= res); | |
| occupied ^= least_significant_square_bb(bb); | |
| attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN)) | |
| | (attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN)); | |
| } | |
| else // KING | |
| // If we "capture" with the king but the opponent still has attackers, | |
| // reverse the result. | |
| return (attackers & ~pieces(stm)) ? res ^ 1 : res; | |
| } | |
| return bool(res); | |
| } | |
| // Tests whether the position is drawn by 50-move rule | |
| // or by repetition. It does not detect stalemates. | |
| bool Position::is_draw(int ply) const { | |
| if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size())) | |
| return true; | |
| return is_repetition(ply); | |
| } | |
| // Return a draw score if a position repeats once earlier but strictly | |
| // after the root, or repeats twice before or at the root. | |
| bool Position::is_repetition(int ply) const { return st->repetition && st->repetition < ply; } | |
| // Tests whether there has been at least one repetition | |
| // of positions since the last capture or pawn move. | |
| bool Position::has_repeated() const { | |
| StateInfo* stc = st; | |
| int end = std::min(st->rule50, st->pliesFromNull); | |
| while (end-- >= 4) | |
| { | |
| if (stc->repetition) | |
| return true; | |
| stc = stc->previous; | |
| } | |
| return false; | |
| } | |
| // Tests if the position has a move which draws by repetition. | |
| // This function accurately matches the outcome of is_draw() over all legal moves. | |
| bool Position::upcoming_repetition(int ply) const { | |
| int j; | |
| int end = std::min(st->rule50, st->pliesFromNull); | |
| if (end < 3) | |
| return false; | |
| Key originalKey = st->key; | |
| StateInfo* stp = st->previous; | |
| Key other = originalKey ^ stp->key ^ Zobrist::side; | |
| for (int i = 3; i <= end; i += 2) | |
| { | |
| stp = stp->previous; | |
| other ^= stp->key ^ stp->previous->key ^ Zobrist::side; | |
| stp = stp->previous; | |
| if (other != 0) | |
| continue; | |
| Key moveKey = originalKey ^ stp->key; | |
| if ((j = H1(moveKey), cuckoo[j] == moveKey) || (j = H2(moveKey), cuckoo[j] == moveKey)) | |
| { | |
| Move move = cuckooMove[j]; | |
| Square s1 = move.from_sq(); | |
| Square s2 = move.to_sq(); | |
| if (!((between_bb(s1, s2) ^ s2) & pieces())) | |
| { | |
| if (ply > i) | |
| return true; | |
| // For nodes before or at the root, check that the move is a | |
| // repetition rather than a move to the current position. | |
| if (stp->repetition) | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| // Flips position with the white and black sides reversed. This | |
| // is only useful for debugging e.g. for finding evaluation symmetry bugs. | |
| void Position::flip() { | |
| string f, token; | |
| std::stringstream ss(fen()); | |
| for (Rank r = RANK_8;; --r) // Piece placement | |
| { | |
| std::getline(ss, token, r > RANK_1 ? '/' : ' '); | |
| f.insert(0, token + (f.empty() ? " " : "/")); | |
| if (r == RANK_1) | |
| break; | |
| } | |
| ss >> token; // Active color | |
| f += (token == "w" ? "B " : "W "); // Will be lowercased later | |
| ss >> token; // Castling availability | |
| f += token + " "; | |
| std::transform(f.begin(), f.end(), f.begin(), | |
| [](char c) { return char(islower(c) ? toupper(c) : tolower(c)); }); | |
| ss >> token; // En passant square | |
| f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3")); | |
| std::getline(ss, token); // Half and full moves | |
| f += token; | |
| set(f, is_chess960(), st); | |
| assert(pos_is_ok()); | |
| } | |
| bool Position::material_key_is_ok() const { return compute_material_key() == st->materialKey; } | |
| // Performs some consistency checks for the position object | |
| // and raise an assert if something wrong is detected. | |
| // This is meant to be helpful when debugging. | |
| bool Position::pos_is_ok() const { | |
| constexpr bool Fast = true; // Quick (default) or full check? | |
| if ((sideToMove != WHITE && sideToMove != BLACK) || piece_on(square<KING>(WHITE)) != W_KING | |
| || piece_on(square<KING>(BLACK)) != B_KING | |
| || (ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6)) | |
| assert(0 && "pos_is_ok: Default"); | |
| if (Fast) | |
| return true; | |
| if (pieceCount[W_KING] != 1 || pieceCount[B_KING] != 1 | |
| || attackers_to_exist(square<KING>(~sideToMove), pieces(), sideToMove)) | |
| assert(0 && "pos_is_ok: Kings"); | |
| if ((pieces(PAWN) & (Rank1BB | Rank8BB)) || pieceCount[W_PAWN] > 8 || pieceCount[B_PAWN] > 8) | |
| assert(0 && "pos_is_ok: Pawns"); | |
| if (ep_square() != SQ_NONE) | |
| { | |
| Square ksq = square<KING>(sideToMove); | |
| Bitboard captured = (ep_square() + pawn_push(~sideToMove)) & pieces(~sideToMove, PAWN); | |
| Bitboard pawns = attacks_bb<PAWN>(ep_square(), ~sideToMove) & pieces(sideToMove, PAWN); | |
| Bitboard potentialCheckers = pieces(~sideToMove) ^ captured; | |
| if (!captured || !pawns | |
| || ((attackers_to(ksq, pieces() ^ captured ^ ep_square() ^ lsb(pawns)) | |
| & potentialCheckers) | |
| && (attackers_to(ksq, pieces() ^ captured ^ ep_square() ^ msb(pawns)) | |
| & potentialCheckers))) | |
| assert(0 && "pos_is_ok: En passant square"); | |
| } | |
| if ((pieces(WHITE) & pieces(BLACK)) || (pieces(WHITE) | pieces(BLACK)) != pieces() | |
| || popcount(pieces(WHITE)) > 16 || popcount(pieces(BLACK)) > 16) | |
| assert(0 && "pos_is_ok: Bitboards"); | |
| for (PieceType p1 = PAWN; p1 <= KING; ++p1) | |
| for (PieceType p2 = PAWN; p2 <= KING; ++p2) | |
| if (p1 != p2 && (pieces(p1) & pieces(p2))) | |
| assert(0 && "pos_is_ok: Bitboards"); | |
| for (Piece pc : Pieces) | |
| if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc))) | |
| || pieceCount[pc] != std::count(board.begin(), board.end(), pc)) | |
| assert(0 && "pos_is_ok: Pieces"); | |
| for (Color c : {WHITE, BLACK}) | |
| for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE}) | |
| { | |
| if (!can_castle(cr)) | |
| continue; | |
| if (piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK) | |
| || castlingRightsMask[castlingRookSquare[cr]] != cr | |
| || (castlingRightsMask[square<KING>(c)] & cr) != cr) | |
| assert(0 && "pos_is_ok: Castling"); | |
| } | |
| assert(material_key_is_ok() && "pos_is_ok: materialKey"); | |
| return true; | |
| } | |
| } // namespace Stockfish | |