X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=d3afddeb4fb1df982f15604d0f5c70eafa905e23;hp=c0e2232c08dee5c92c235d24408c99866dee88a3;hb=5d7ad47c198c54e7a91876a6d8d8488a0c23a1ba;hpb=759b3c79cf94d101163f646b1eb2a9f9c64293ab diff --git a/src/position.cpp b/src/position.cpp index c0e2232c..d3afddeb 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,6 +126,19 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { } +// 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 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]; +Move cuckooMove[8192]; + + /// Position::init() initializes at startup the various arrays used to compute /// hash keys. @@ -157,6 +166,30 @@ void Position::init() { Zobrist::side = rng.rand(); 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) + for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2) + if (PseudoAttacks[type_of(pc)][s1] & s2) + { + Move move = make_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 == 0) // Arrived at empty slot ? + break; + i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot + } + count++; + } + assert(count == 3668); } @@ -285,8 +318,6 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th thisThread = th; set_state(st); - assert(pos_is_ok()); - return *this; } @@ -346,7 +377,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); @@ -356,7 +386,6 @@ 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 (si->epSquare != SQ_NONE) @@ -445,7 +474,7 @@ const string Position::fen() const { if (can_castle(BLACK_OOO)) ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q'); - if (!can_castle(WHITE) && !can_castle(BLACK)) + if (!can_castle(ANY_CASTLING)) ss << '-'; ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ") @@ -717,7 +746,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; } @@ -756,9 +784,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; } @@ -812,9 +837,6 @@ 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]; } @@ -827,9 +849,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->rule50 = 0; } - // Update incremental scores - st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; - // Set capture piece st->capturedPiece = captured; @@ -1105,6 +1124,87 @@ bool Position::is_draw(int ply) const { } +// Position::has_repeated() 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; + while (true) + { + int i = 4, end = std::min(stc->rule50, stc->pliesFromNull); + + if (end < i) + return false; + + StateInfo* stp = stc->previous->previous; + + do { + stp = stp->previous->previous; + + if (stp->key == stc->key) + return true; + + i += 2; + } while (i <= end); + + stc = stc->previous; + } +} + + +/// Position::has_game_cycle() tests if the position has a move which draws by repetition, +/// or an earlier position has a move that directly reaches the current position. + +bool Position::has_game_cycle(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; + + for (int i = 3; i <= end; i += 2) + { + stp = stp->previous->previous; + + Key moveKey = originalKey ^ stp->key; + if ( (j = H1(moveKey), cuckoo[j] == moveKey) + || (j = H2(moveKey), cuckoo[j] == moveKey)) + { + Move move = cuckooMove[j]; + Square s1 = from_sq(move); + Square s2 = to_sq(move); + + if (!(between_bb(s1, s2) & pieces())) + { + // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in the same + // location. We select the legal one by reversing the move variable if necessary. + if (empty(s1)) + move = make_move(s2, s1); + + if (ply > i) + return true; + + // 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; + } + } + } + } + return false; +} + + /// Position::flip() flips position with the white and black sides reversed. This /// is only useful for debugging e.g. for finding evaluation symmetry bugs.