X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=14121d47af67dd234c5f04134328a9c895654717;hp=c07e6964fac76006c874ade7e2d3423b9fdd3862;hb=d39bc2efa197ba2fd55b68eced1c60bcfe2facc1;hpb=91a76331ca27b40d63f0031fbd7b9e41ead354d4 diff --git a/src/position.cpp b/src/position.cpp index c07e6964..14121d47 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,10 +36,6 @@ using std::string; -namespace PSQT { - extern Score psq[PIECE_NB][SQUARE_NB]; -} - namespace Zobrist { Key psq[PIECE_NB][SQUARE_NB]; @@ -130,13 +126,13 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { } -// Marcel van Kervink's cuckoo algorithm for fast detection of "upcoming repetition"/ -// "no progress" situations. Description of the algorithm in the following paper: +// Marcel van Kervinck's cuckoo algorithm for fast detection of "upcoming repetition" +// situations. Description of the algorithm in the following paper: // https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf // First and second hash functions for indexing the cuckoo tables -inline Key H1(Key h) { return h & 0x1fff; } -inline Key H2(Key h) { return (h >> 16) & 0x1fff; } +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 Key cuckoo[8192]; @@ -172,6 +168,8 @@ void Position::init() { Zobrist::noPawns = rng.rand(); // Prepare the cuckoo tables + std::memset(cuckoo, 0, sizeof(cuckoo)); + std::memset(cuckooMove, 0, sizeof(cuckooMove)); int count = 0; for (Piece pc : Pieces) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) @@ -180,12 +178,12 @@ void Position::init() { { Move move = make_move(s1, s2); Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side; - unsigned int i = H1(key); + int i = H1(key); while (true) { std::swap(cuckoo[i], key); std::swap(cuckooMove[i], move); - if (move == 0) // Arrived at empty slot ? + if (move == MOVE_NONE) // Arrived at empty slot? break; i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot } @@ -343,13 +341,8 @@ void Position::set_castling_right(Color c, Square rfrom) { Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1); Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1); - for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s) - if (s != kfrom && s != rfrom) - castlingPath[cr] |= s; - - for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s) - if (s != kfrom && s != rfrom) - castlingPath[cr] |= s; + castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto) + & ~(square_bb(kfrom) | rfrom); } @@ -381,7 +374,6 @@ void Position::set_state(StateInfo* si) const { si->key = si->materialKey = 0; si->pawnKey = Zobrist::noPawns; si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO; - si->psq = SCORE_ZERO; si->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); set_check_info(si); @@ -391,7 +383,12 @@ void Position::set_state(StateInfo* si) const { Square s = pop_lsb(&b); Piece pc = piece_on(s); si->key ^= Zobrist::psq[pc][s]; - si->psq += PSQT::psq[pc][s]; + + if (type_of(pc) == PAWN) + si->pawnKey ^= Zobrist::psq[pc][s]; + + else if (type_of(pc) != KING) + si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc]; } if (si->epSquare != SQ_NONE) @@ -402,20 +399,9 @@ void Position::set_state(StateInfo* si) const { si->key ^= Zobrist::castling[si->castlingRights]; - for (Bitboard b = pieces(PAWN); b; ) - { - Square s = pop_lsb(&b); - si->pawnKey ^= Zobrist::psq[piece_on(s)][s]; - } - for (Piece pc : Pieces) - { - if (type_of(pc) != PAWN && type_of(pc) != KING) - si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc]; - for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) si->materialKey ^= Zobrist::psq[pc][cnt]; - } } @@ -469,18 +455,18 @@ const string Position::fen() const { ss << (sideToMove == WHITE ? " w " : " b "); if (can_castle(WHITE_OO)) - ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | KING_SIDE))) : 'K'); + 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 | QUEEN_SIDE))) : 'Q'); + 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 | KING_SIDE))) : 'k'); + 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 | QUEEN_SIDE))) : 'q'); + ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q'); - if (!can_castle(WHITE) && !can_castle(BLACK)) + if (!can_castle(ANY_CASTLING)) ss << '-'; ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ") @@ -502,14 +488,15 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners Bitboard blockers = 0; pinners = 0; - // Snipers are sliders that attack 's' when a piece is removed + // Snipers are sliders that attack 's' when a piece and other snipers are removed Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK)) | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; + Bitboard occupancy = pieces() ^ snipers; while (snipers) { Square sniperSq = pop_lsb(&snipers); - Bitboard b = between_bb(s, sniperSq) & pieces(); + Bitboard b = between_bb(s, sniperSq) & occupancy; if (b && !more_than_one(b)) { @@ -544,6 +531,7 @@ bool Position::legal(Move m) const { Color us = sideToMove; Square from = from_sq(m); + Square to = to_sq(m); assert(color_of(moved_piece(m)) == us); assert(piece_on(square(us)) == make_piece(us, KING)); @@ -554,7 +542,6 @@ bool Position::legal(Move m) const { if (type_of(m) == ENPASSANT) { Square ksq = square(us); - Square to = to_sq(m); Square capsq = to - pawn_push(us); Bitboard occupied = (pieces() ^ from ^ capsq) | to; @@ -567,16 +554,35 @@ bool Position::legal(Move m) const { && !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); } - // If the moving piece is a king, check whether the destination - // square is attacked by the opponent. Castling moves are checked - // for legality during move generation. + // Castling moves generation does not check if the castling path is clear of + // enemy attacks, it is delayed at a later time: now! + if (type_of(m) == 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(s) & pieces(~us)) + return false; + + // In case of Chess960, verify that when moving the castling rook we do + // not discover some hidden checker. + // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. + return !chess960 + || !(attacks_bb(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN)); + } + + // 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 type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us)); + return !(attackers_to(to) & pieces(~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) - || aligned(from, to_sq(m), square(us)); + || aligned(from, to, square(us)); } @@ -613,7 +619,7 @@ bool Position::pseudo_legal(const Move m) const { { // We have already handled promotion moves, so destination // cannot be on the 8th/1st rank. - if (rank_of(to) == relative_rank(us, RANK_8)) + if ((Rank8BB | Rank1BB) & to) return false; if ( !(attacks_from(from, us) & pieces(~us) & to) // Not a capture @@ -752,7 +758,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { Square rfrom, rto; do_castling(us, from, to, rfrom, rto); - st->psq += PSQT::psq[captured][rto] - PSQT::psq[captured][rfrom]; k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; captured = NO_PIECE; } @@ -791,9 +796,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; prefetch(thisThread->materialTable[st->materialKey]); - // Update incremental scores - st->psq -= PSQT::psq[captured][capsq]; - // Reset rule 50 counter st->rule50 = 0; } @@ -847,24 +849,17 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1] ^ Zobrist::psq[pc][pieceCount[pc]]; - // Update incremental score - st->psq += PSQT::psq[promotion][to] - PSQT::psq[pc][to]; - // Update material st->nonPawnMaterial[us] += PieceValue[MG][promotion]; } // Update pawn hash key and prefetch access to pawnsTable st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; - prefetch2(thisThread->pawnsTable[st->pawnKey]); // Reset rule 50 draw counter st->rule50 = 0; } - // Update incremental scores - st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; - // Set capture piece st->capturedPiece = captured; @@ -879,6 +874,25 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Update king attacks used for fast check detection set_check_info(st); + // 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; + } + } + } + assert(pos_is_ok()); } @@ -994,6 +1008,8 @@ void Position::do_null_move(StateInfo& newSt) { set_check_info(st); + st->repetition = 0; + assert(pos_is_ok()); } @@ -1071,8 +1087,8 @@ bool Position::see_ge(Move m, Value threshold) const { stmAttackers = attackers & pieces(stm); // Don't allow pinned pieces to attack (except the king) as long as - // all pinners are on their original square. - if (!(st->pinners[~stm] & ~occupied)) + // any pinners are on their original square. + if (st->pinners[~stm] & occupied) stmAttackers &= ~st->blockersForKing[stm]; // If stm has no more attackers then give up: stm loses @@ -1117,24 +1133,10 @@ bool Position::is_draw(int ply) const { if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) return true; - int end = std::min(st->rule50, st->pliesFromNull); - - if (end < 4) - return false; - - StateInfo* stp = st->previous->previous; - int cnt = 0; - - for (int i = 4; i <= end; i += 2) - { - stp = stp->previous->previous; - - // Return a draw score if a position repeats once earlier but strictly - // after the root, or repeats twice before or at the root. - if ( stp->key == st->key - && ++cnt + (ply > i) == 2) - return true; - } + // Return a draw score if a position repeats once earlier but strictly + // after the root, or repeats twice before or at the root. + if (st->repetition && st->repetition < ply) + return true; return false; } @@ -1146,26 +1148,15 @@ bool Position::is_draw(int ply) const { bool Position::has_repeated() const { StateInfo* stc = st; - while (true) + int end = std::min(st->rule50, st->pliesFromNull); + while (end-- >= 4) { - int i = 4, e = std::min(stc->rule50, stc->pliesFromNull); - - if (e < i) - return false; - - StateInfo* stp = st->previous->previous; - - do { - stp = stp->previous->previous; - - if (stp->key == stc->key) - return true; - - i += 2; - } while (i <= e); + if (stc->repetition) + return true; stc = stc->previous; } + return false; } @@ -1174,7 +1165,7 @@ bool Position::has_repeated() const { bool Position::has_game_cycle(int ply) const { - unsigned int j; + int j; int end = std::min(st->rule50, st->pliesFromNull); @@ -1183,40 +1174,36 @@ bool Position::has_game_cycle(int ply) const { Key originalKey = st->key; StateInfo* stp = st->previous; - Key progressKey = stp->key ^ Zobrist::side; for (int i = 3; i <= end; i += 2) { - stp = stp->previous; - progressKey ^= stp->key ^ Zobrist::side; - stp = stp->previous; + stp = stp->previous->previous; - // "originalKey == " detects upcoming repetition, "progressKey == " detects no-progress - if ( originalKey == (progressKey ^ stp->key) - || progressKey == Zobrist::side) + Key moveKey = originalKey ^ stp->key; + if ( (j = H1(moveKey), cuckoo[j] == moveKey) + || (j = H2(moveKey), cuckoo[j] == moveKey)) { - Key moveKey = originalKey ^ stp->key; - if ( (j = H1(moveKey), cuckoo[j] == moveKey) - || (j = H2(moveKey), cuckoo[j] == moveKey)) - { - Move m = Move(cuckooMove[j]); - if (!(between_bb(from_sq(m), to_sq(m)) & pieces())) - { - if (ply > i) - return true; + Move move = cuckooMove[j]; + Square s1 = from_sq(move); + Square s2 = to_sq(move); - // For repetitions before or at the root, require one more - StateInfo* next_stp = stp; - for (int k = i + 2; k <= end; k += 2) - { - next_stp = next_stp->previous->previous; - if (next_stp->key == stp->key) - return true; - } - } + if (!(between_bb(s1, 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. + // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in + // the same location, so we have to select which square to check. + if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move()) + continue; + + // For repetitions before or at the root, require one more + if (stp->repetition) + return true; } } - progressKey ^= stp->key; } return false; }