diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 72afabb6..32c626d4 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -44,15 +44,13 @@ Bitboard BishopTable[0x1480]; // To store bishop attacks void init_magics(PieceType pt, Bitboard table[], Magic magics[]); -} - // Returns the bitboard of target square for the given step // from the given square. If the step is off the board, returns empty bitboard. -inline Bitboard safe_destination(Square s, int step) { +Bitboard safe_destination(Square s, int step) { Square to = Square(s + step); return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); } - +} // Returns an ASCII representation of a bitboard suitable // to be printed to standard output. Useful for debugging. diff --git a/src/search.cpp b/src/search.cpp index b23740d8..8901dfd1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -29,7 +29,6 @@ #include #include -#include "bitboard.h" #include "evaluate.h" #include "misc.h" #include "movegen.h" @@ -46,14 +45,6 @@ namespace Stockfish { -namespace Tablebases { - -int Cardinality; -bool RootInTB; -bool UseRule50; -Depth ProbeDepth; -} - namespace TB = Tablebases; using Eval::evaluate; @@ -237,7 +228,7 @@ void Search::Worker::start_searching() { if (bestThread != this) sync_cout << UCI::pv(*bestThread, main_manager()->tm.elapsed(threads.nodes_searched()), threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - TB::RootInTB) + tbConfig.rootInTB) << sync_endl; sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); @@ -379,7 +370,7 @@ void Search::Worker::iterative_deepening() { && mainThread->tm.elapsed(threads.nodes_searched()) > 3000) sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - TB::RootInTB) + tbConfig.rootInTB) << sync_endl; // In case of failing low/high increase aspiration window and @@ -414,7 +405,7 @@ void Search::Worker::iterative_deepening() { || mainThread->tm.elapsed(threads.nodes_searched()) > 3000)) sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - TB::RootInTB) + tbConfig.rootInTB) << sync_endl; } @@ -659,13 +650,13 @@ Value Search::Worker::search( } // Step 5. Tablebases probe - if (!rootNode && !excludedMove && TB::Cardinality) + if (!rootNode && !excludedMove && tbConfig.cardinality) { int piecesCount = pos.count(); - if (piecesCount <= TB::Cardinality - && (piecesCount < TB::Cardinality || depth >= TB::ProbeDepth) && pos.rule50_count() == 0 - && !pos.can_castle(ANY_CASTLING)) + if (piecesCount <= tbConfig.cardinality + && (piecesCount < tbConfig.cardinality || depth >= tbConfig.probeDepth) + && pos.rule50_count() == 0 && !pos.can_castle(ANY_CASTLING)) { TB::ProbeState err; TB::WDLScore wdl = Tablebases::probe_wdl(pos, &err); @@ -678,7 +669,7 @@ Value Search::Worker::search( { thisThread->tbHits.fetch_add(1, std::memory_order_relaxed); - int drawScore = TB::UseRule50 ? 1 : 0; + int drawScore = tbConfig.useRule50 ? 1 : 0; Value tbValue = VALUE_TB - ss->ply; @@ -1962,53 +1953,5 @@ bool RootMove::extract_ponder_from_tt(const TranspositionTable& tt, Position& po return pv.size() > 1; } -void Tablebases::rank_root_moves(const OptionsMap& options, - Position& pos, - Search::RootMoves& rootMoves) { - - RootInTB = false; - UseRule50 = bool(options["Syzygy50MoveRule"]); - ProbeDepth = int(options["SyzygyProbeDepth"]); - Cardinality = int(options["SyzygyProbeLimit"]); - bool dtz_available = true; - - // Tables with fewer pieces than SyzygyProbeLimit are searched with - // ProbeDepth == DEPTH_ZERO - if (Cardinality > MaxCardinality) - { - Cardinality = MaxCardinality; - ProbeDepth = 0; - } - - if (Cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) - { - // Rank moves using DTZ tables - RootInTB = root_probe(pos, rootMoves, options["Syzygy50MoveRule"]); - - if (!RootInTB) - { - // DTZ tables are missing; try to rank moves using WDL tables - dtz_available = false; - RootInTB = root_probe_wdl(pos, rootMoves, options["Syzygy50MoveRule"]); - } - } - - if (RootInTB) - { - // Sort moves according to TB rank - std::stable_sort(rootMoves.begin(), rootMoves.end(), - [](const RootMove& a, const RootMove& b) { return a.tbRank > b.tbRank; }); - - // Probe during search only if DTZ is not available and we are winning - if (dtz_available || rootMoves[0].tbScore <= VALUE_DRAW) - Cardinality = 0; - } - else - { - // Clean up if root_probe() and root_probe_wdl() have failed - for (auto& m : rootMoves) - m.tbRank = 0; - } -} } // namespace Stockfish diff --git a/src/search.h b/src/search.h index 68c9f2aa..daf0ff85 100644 --- a/src/search.h +++ b/src/search.h @@ -29,6 +29,7 @@ #include "misc.h" #include "movepick.h" #include "position.h" +#include "syzygy/tbprobe.h" #include "timeman.h" #include "types.h" @@ -48,7 +49,6 @@ class UCI; namespace Search { - // Stack struct keeps track of the information we need to remember from nodes // shallower and deeper in the tree during the search. Each search thread has // its own array of Stack objects, indexed by the current ply. @@ -238,6 +238,8 @@ class Worker { // The main thread has a SearchManager, the others have a NullSearchManager std::unique_ptr manager; + Tablebases::Config tbConfig; + const OptionsMap& options; ThreadPool& threads; TranspositionTable& tt; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 6f30bf6b..722dc9d3 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -18,7 +18,6 @@ #include "tbprobe.h" -#include #include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +42,7 @@ #include "../position.h" #include "../search.h" #include "../types.h" +#include "../ucioption.h" #ifndef _WIN32 #include @@ -1680,4 +1681,60 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, boo return true; } +Config Tablebases::rank_root_moves(const OptionsMap& options, + Position& pos, + Search::RootMoves& rootMoves) { + Config config; + + if (rootMoves.empty()) + return config; + + config.rootInTB = false; + config.useRule50 = bool(options["Syzygy50MoveRule"]); + config.probeDepth = int(options["SyzygyProbeDepth"]); + config.cardinality = int(options["SyzygyProbeLimit"]); + + bool dtz_available = true; + + // Tables with fewer pieces than SyzygyProbeLimit are searched with + // probeDepth == DEPTH_ZERO + if (config.cardinality > MaxCardinality) + { + config.cardinality = MaxCardinality; + config.probeDepth = 0; + } + + if (config.cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) + { + // Rank moves using DTZ tables + config.rootInTB = root_probe(pos, rootMoves, options["Syzygy50MoveRule"]); + + if (!config.rootInTB) + { + // DTZ tables are missing; try to rank moves using WDL tables + dtz_available = false; + config.rootInTB = root_probe_wdl(pos, rootMoves, options["Syzygy50MoveRule"]); + } + } + + if (config.rootInTB) + { + // Sort moves according to TB rank + std::stable_sort( + rootMoves.begin(), rootMoves.end(), + [](const Search::RootMove& a, const Search::RootMove& b) { return a.tbRank > b.tbRank; }); + + // Probe during search only if DTZ is not available and we are winning + if (dtz_available || rootMoves[0].tbScore <= VALUE_DRAW) + config.cardinality = 0; + } + else + { + // Clean up if root_probe() and root_probe_wdl() have failed + for (auto& m : rootMoves) + m.tbRank = 0; + } + + return config; +} } // namespace Stockfish diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index d7b412a1..e10950f4 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -20,16 +20,30 @@ #define TBPROBE_H #include +#include -#include "../search.h" namespace Stockfish { class Position; class OptionsMap; + +using Depth = int; + +namespace Search { +struct RootMove; +using RootMoves = std::vector; +} } namespace Stockfish::Tablebases { +struct Config { + int cardinality = 0; + bool rootInTB = false; + bool useRule50 = false; + Depth probeDepth = 0; +}; + enum WDLScore { WDLLoss = -2, // Loss WDLBlessedLoss = -1, // Loss, but draw under 50-move rule @@ -54,7 +68,7 @@ WDLScore probe_wdl(Position& pos, ProbeState* result); int probe_dtz(Position& pos, ProbeState* result); bool root_probe(Position& pos, Search::RootMoves& rootMoves, bool rule50); bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, bool rule50); -void rank_root_moves(const OptionsMap& options, Position& pos, Search::RootMoves& rootMoves); +Config rank_root_moves(const OptionsMap& options, Position& pos, Search::RootMoves& rootMoves); } // namespace Stockfish::Tablebases diff --git a/src/thread.cpp b/src/thread.cpp index 9dc4446f..a4bc3d67 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -181,8 +181,7 @@ void ThreadPool::start_thinking(const OptionsMap& options, || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) rootMoves.emplace_back(m); - if (!rootMoves.empty()) - Tablebases::rank_root_moves(options, pos, rootMoves); + Tablebases::Config tbConfig = Tablebases::rank_root_moves(options, pos, rootMoves); // After ownership transfer 'states' becomes empty, so if we stop the search // and call 'go' again without setting a new position states.get() == nullptr. @@ -205,6 +204,7 @@ void ThreadPool::start_thinking(const OptionsMap& options, th->worker->rootMoves = rootMoves; th->worker->rootPos.set(pos.fen(), pos.is_chess960(), &th->worker->rootState); th->worker->rootState = setupStates->back(); + th->worker->tbConfig = tbConfig; } main_thread()->start_searching();