diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index b83acf12..ee58d76c 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -505,7 +505,7 @@ public: // Given a position, produce a 64-bit material signature key. If the engine // supports such a key, it should equal the engine's key. -Key get_key(Position& pos, bool mirror) +Key get_key(const Position& pos, bool mirror) { Key key = 0; @@ -1520,7 +1520,7 @@ WDLScore probe_wdl_table(Position& pos, int* success) } } -int probe_dtz_table(Position& pos, int wdl, int *success) +int probe_dtz_table(const Position& pos, int wdl, int *success) { uint64_t idx; int i, res; @@ -1727,6 +1727,238 @@ WDLScore probe_ab(Position& pos, WDLScore alpha, WDLScore beta, int *success) } } +int probe_dtz(Position& pos, int *success); + +// This routine treats a position with en passant captures as one without. +int probe_dtz_no_ep(Position& pos, int *success) +{ + int dtz; + + WDLScore wdl = probe_ab(pos, WDLHardLoss, WDLHardWin, success); + + if (*success == 0) return 0; + + if (wdl == WDLDraw) return 0; + + if (*success == 2) + return wdl == WDLHardWin ? 1 : 101; + + ExtMove stack[MAX_MOVES]; + ExtMove *moves, *end = NULL; + StateInfo st; + CheckInfo ci(pos); + + if (wdl > 0) { + // Generate at least all legal non-capturing pawn moves + // including non-capturing promotions. + if (!pos.checkers()) + end = generate(pos, stack); + else + end = generate(pos, stack); + + for (moves = stack; moves < end; ++moves) { + Move move = moves->move; + + if ( type_of(pos.moved_piece(move)) != PAWN + || pos.capture(move) + || !pos.legal(move, ci.pinned)) + continue; + + pos.do_move(move, st, pos.gives_check(move, ci)); + WDLScore v = -probe_ab(pos, WDLHardLoss, -wdl + WDLSoftWin, success); + pos.undo_move(move); + + if (*success == 0) return 0; + + if (v == wdl) + return v == WDLHardWin ? 1 : 101; + } + } + + dtz = 1 + probe_dtz_table(pos, wdl, success); + + if (*success >= 0) { + if (wdl & 1) dtz += 100; + + return wdl >= 0 ? dtz : -dtz; + } + + if (wdl > 0) { + int best = 0xffff; + + for (moves = stack; moves < end; ++moves) { + Move move = moves->move; + + if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN + || !pos.legal(move, ci.pinned)) + continue; + + pos.do_move(move, st, pos.gives_check(move, ci)); + int v = -probe_dtz(pos, success); + pos.undo_move(move); + + if (*success == 0) + return 0; + + if (v > 0 && v + 1 < best) + best = v + 1; + } + + return best; + } else { + int best = -1; + + if (!pos.checkers()) + end = generate(pos, stack); + else + end = generate(pos, stack); + + for (moves = stack; moves < end; ++moves) { + int v; + Move move = moves->move; + + if (!pos.legal(move, ci.pinned)) + continue; + + pos.do_move(move, st, pos.gives_check(move, ci)); + + if (st.rule50 == 0) { + if (wdl == -2) v = -1; + else { + v = probe_ab(pos, WDLSoftWin, WDLHardWin, success); + v = (v == 2) ? 0 : -101; + } + } else { + v = -probe_dtz(pos, success) - 1; + } + + pos.undo_move(move); + + if (*success == 0) + return 0; + + if (v < best) + best = v; + } + + return best; + } +} + +// Probe the DTZ table for a particular position. +// If *success != 0, the probe was successful. +// The return value is from the point of view of the side to move: +// n < -100 : loss, but draw under 50-move rule +// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0) +// 0 : draw +// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0) +// 100 < n : win, but draw under 50-move rule +// +// The return value n can be off by 1: a return value -n can mean a loss +// in n+1 ply and a return value +n can mean a win in n+1 ply. This +// cannot happen for tables with positions exactly on the "edge" of +// the 50-move rule. +// +// This implies that if dtz > 0 is returned, the position is certainly +// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine +// picks moves that preserve dtz + 50-move-counter <= 99. +// +// If n = 100 immediately after a capture or pawn move, then the position +// is also certainly a win, and during the whole phase until the next +// capture or pawn move, the inequality to be preserved is +// dtz + 50-movecounter <= 100. +// +// In short, if a move is available resulting in dtz + 50-move-counter <= 99, +// then do not accept moves leading to dtz + 50-move-counter == 100. +// +int probe_dtz(Position& pos, int *success) +{ + *success = 1; + int v = probe_dtz_no_ep(pos, success); + + if (pos.ep_square() == SQ_NONE) + return v; + + if (*success == 0) + return 0; + + // Now handle en passant. + int v1 = -3; + + ExtMove stack[MAX_MOVES]; + ExtMove *moves, *end; + StateInfo st; + + if (!pos.checkers()) + end = generate(pos, stack); + else + end = generate(pos, stack); + + CheckInfo ci(pos); + + for (moves = stack; moves < end; ++moves) { + Move capture = moves->move; + + if (type_of(capture) != ENPASSANT + || !pos.legal(capture, ci.pinned)) + continue; + + pos.do_move(capture, st, pos.gives_check(capture, ci)); + WDLScore v0 = -probe_ab(pos, WDLHardLoss, WDLHardWin, success); + pos.undo_move(capture); + + if (*success == 0) + return 0; + + if (v0 > v1) v1 = v0; + } + + if (v1 > -3) { + v1 = wdl_to_dtz[v1 + 2]; + + if (v < -100) { + if (v1 >= 0) + v = v1; + } else if (v < 0) { + if (v1 >= 0 || v1 < -100) + v = v1; + } else if (v > 100) { + if (v1 > 0) + v = v1; + } else if (v > 0) { + if (v1 == 1) + v = v1; + } else if (v1 >= 0) { + v = v1; + } else { + for (moves = stack; moves < end; ++moves) { + Move move = moves->move; + + if (type_of(move) == ENPASSANT) continue; + + if (pos.legal(move, ci.pinned)) + break; + } + + if (moves == end && !pos.checkers()) { + end = generate(pos, end); + + for (; moves < end; ++moves) { + Move move = moves->move; + + if (pos.legal(move, ci.pinned)) + break; + } + } + + if (moves == end) + v = v1; + } + } + + return v; +} + } // namespace void Tablebases::free() @@ -1895,236 +2127,6 @@ WDLScore Tablebases::probe_wdl(Position& pos, int *success) return v; } -// This routine treats a position with en passant captures as one without. -static int probe_dtz_no_ep(Position& pos, int *success) -{ - int dtz; - - WDLScore wdl = probe_ab(pos, WDLHardLoss, WDLHardWin, success); - - if (*success == 0) return 0; - - if (wdl == WDLDraw) return 0; - - if (*success == 2) - return wdl == WDLHardWin ? 1 : 101; - - ExtMove stack[MAX_MOVES]; - ExtMove *moves, *end = NULL; - StateInfo st; - CheckInfo ci(pos); - - if (wdl > 0) { - // Generate at least all legal non-capturing pawn moves - // including non-capturing promotions. - if (!pos.checkers()) - end = generate(pos, stack); - else - end = generate(pos, stack); - - for (moves = stack; moves < end; ++moves) { - Move move = moves->move; - - if ( type_of(pos.moved_piece(move)) != PAWN - || pos.capture(move) - || !pos.legal(move, ci.pinned)) - continue; - - pos.do_move(move, st, pos.gives_check(move, ci)); - WDLScore v = -probe_ab(pos, WDLHardLoss, -wdl + WDLSoftWin, success); - pos.undo_move(move); - - if (*success == 0) return 0; - - if (v == wdl) - return v == WDLHardWin ? 1 : 101; - } - } - - dtz = 1 + probe_dtz_table(pos, wdl, success); - - if (*success >= 0) { - if (wdl & 1) dtz += 100; - - return wdl >= 0 ? dtz : -dtz; - } - - if (wdl > 0) { - int best = 0xffff; - - for (moves = stack; moves < end; ++moves) { - Move move = moves->move; - - if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN - || !pos.legal(move, ci.pinned)) - continue; - - pos.do_move(move, st, pos.gives_check(move, ci)); - int v = -Tablebases::probe_dtz(pos, success); - pos.undo_move(move); - - if (*success == 0) - return 0; - - if (v > 0 && v + 1 < best) - best = v + 1; - } - - return best; - } else { - int best = -1; - - if (!pos.checkers()) - end = generate(pos, stack); - else - end = generate(pos, stack); - - for (moves = stack; moves < end; ++moves) { - int v; - Move move = moves->move; - - if (!pos.legal(move, ci.pinned)) - continue; - - pos.do_move(move, st, pos.gives_check(move, ci)); - - if (st.rule50 == 0) { - if (wdl == -2) v = -1; - else { - v = probe_ab(pos, WDLSoftWin, WDLHardWin, success); - v = (v == 2) ? 0 : -101; - } - } else { - v = -Tablebases::probe_dtz(pos, success) - 1; - } - - pos.undo_move(move); - - if (*success == 0) - return 0; - - if (v < best) - best = v; - } - - return best; - } -} - -// Probe the DTZ table for a particular position. -// If *success != 0, the probe was successful. -// The return value is from the point of view of the side to move: -// n < -100 : loss, but draw under 50-move rule -// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0) -// 0 : draw -// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0) -// 100 < n : win, but draw under 50-move rule -// -// The return value n can be off by 1: a return value -n can mean a loss -// in n+1 ply and a return value +n can mean a win in n+1 ply. This -// cannot happen for tables with positions exactly on the "edge" of -// the 50-move rule. -// -// This implies that if dtz > 0 is returned, the position is certainly -// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine -// picks moves that preserve dtz + 50-move-counter <= 99. -// -// If n = 100 immediately after a capture or pawn move, then the position -// is also certainly a win, and during the whole phase until the next -// capture or pawn move, the inequality to be preserved is -// dtz + 50-movecounter <= 100. -// -// In short, if a move is available resulting in dtz + 50-move-counter <= 99, -// then do not accept moves leading to dtz + 50-move-counter == 100. -// -int Tablebases::probe_dtz(Position& pos, int *success) -{ - *success = 1; - int v = probe_dtz_no_ep(pos, success); - - if (pos.ep_square() == SQ_NONE) - return v; - - if (*success == 0) - return 0; - - // Now handle en passant. - int v1 = -3; - - ExtMove stack[MAX_MOVES]; - ExtMove *moves, *end; - StateInfo st; - - if (!pos.checkers()) - end = generate(pos, stack); - else - end = generate(pos, stack); - - CheckInfo ci(pos); - - for (moves = stack; moves < end; ++moves) { - Move capture = moves->move; - - if (type_of(capture) != ENPASSANT - || !pos.legal(capture, ci.pinned)) - continue; - - pos.do_move(capture, st, pos.gives_check(capture, ci)); - WDLScore v0 = -probe_ab(pos, WDLHardLoss, WDLHardWin, success); - pos.undo_move(capture); - - if (*success == 0) - return 0; - - if (v0 > v1) v1 = v0; - } - - if (v1 > -3) { - v1 = wdl_to_dtz[v1 + 2]; - - if (v < -100) { - if (v1 >= 0) - v = v1; - } else if (v < 0) { - if (v1 >= 0 || v1 < -100) - v = v1; - } else if (v > 100) { - if (v1 > 0) - v = v1; - } else if (v > 0) { - if (v1 == 1) - v = v1; - } else if (v1 >= 0) { - v = v1; - } else { - for (moves = stack; moves < end; ++moves) { - Move move = moves->move; - - if (type_of(move) == ENPASSANT) continue; - - if (pos.legal(move, ci.pinned)) - break; - } - - if (moves == end && !pos.checkers()) { - end = generate(pos, end); - - for (; moves < end; ++moves) { - Move move = moves->move; - - if (pos.legal(move, ci.pinned)) - break; - } - } - - if (moves == end) - v = v1; - } - } - - return v; -} - // Check whether there has been at least one repetition of positions // since the last capture or pawn move. static int has_repeated(StateInfo *st) @@ -2182,14 +2184,14 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, Value& if (!v) { if (st.rule50 != 0) { - v = -Tablebases::probe_dtz(pos, &success); + v = -probe_dtz(pos, &success); if (v > 0) ++v; else if (v < 0) --v; } else { - v = -Tablebases::probe_wdl(pos, &success); + v = -probe_wdl(pos, &success); v = wdl_to_dtz[v + 2]; } } diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index 616b2d98..23b0928a 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -18,7 +18,6 @@ extern int MaxCardinality; void init(const std::string& paths); void free(); WDLScore probe_wdl(Position& pos, int* success); -int probe_dtz(Position& pos, int* success); bool root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score); bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score);