From f53aea45e3230239d358d4d35357c9ee6bf6fb54 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 18 Jan 2015 08:05:05 +0100 Subject: [PATCH] Add syzygy support bench: 8080602 --- src/Makefile | 10 ++--- src/main.cpp | 2 + src/position.h | 5 +++ src/search.cpp | 93 ++++++++++++++++++++++++++++++++++++++++++++++- src/thread.h | 2 - src/types.h | 4 +- src/uci.cpp | 2 +- src/ucioption.cpp | 6 +++ 8 files changed, 113 insertions(+), 11 deletions(-) diff --git a/src/Makefile b/src/Makefile index f57773df..769b9d34 100644 --- a/src/Makefile +++ b/src/Makefile @@ -36,7 +36,7 @@ PGOBENCH = ./$(EXE) bench 16 1 1000 default time ### Object files OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \ material.o misc.o movegen.o movepick.o pawns.o position.o \ - search.o thread.o timeman.o tt.o uci.o ucioption.o + search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o ### ========================================================================== ### Section 2. High-level Configuration @@ -375,14 +375,14 @@ profile-build: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare) @echo "" @echo "Step 1/4. Building executable for benchmark ..." - @touch *.cpp *.h + @touch *.cpp *.h syzygy/*.cpp syzygy/*.h $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) @echo "" @echo "Step 2/4. Running benchmark for pgo-build ..." @$(PGOBENCH) > /dev/null @echo "" @echo "Step 3/4. Building final executable ..." - @touch *.cpp *.h + @touch *.cpp *.h syzygy/*.cpp syzygy/*.h $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use) @echo "" @echo "Step 4/4. Deleting profile data ..." @@ -397,7 +397,7 @@ install: -strip $(BINDIR)/$(EXE) clean: - $(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda + $(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda ./syzygy/*.o ./syzygy/*.gcda default: help @@ -462,7 +462,7 @@ gcc-profile-use: all gcc-profile-clean: - @rm -rf *.gcda *.gcno bench.txt + @rm -rf *.gcda *.gcno syzygy/*.gcda syzygy/*.gcno bench.txt icc-profile-prepare: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) icc-profile-clean diff --git a/src/main.cpp b/src/main.cpp index bc784837..8cdf9e6f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,7 @@ #include "thread.h" #include "tt.h" #include "uci.h" +#include "syzygy/tbprobe.h" int main(int argc, char* argv[]) { @@ -39,6 +40,7 @@ int main(int argc, char* argv[]) { Eval::init(); Pawns::init(); Threads.init(); + Tablebases::init(Options["SyzygyPath"]); TT.resize(Options["Hash"]); UCI::loop(argc, argv); diff --git a/src/position.h b/src/position.h index 85ea6a7a..86acd20d 100644 --- a/src/position.h +++ b/src/position.h @@ -170,6 +170,7 @@ public: uint64_t nodes_searched() const; void set_nodes_searched(uint64_t n); bool is_draw() const; + int rule50_count() const; Score psq_score() const; Value non_pawn_material(Color c) const; @@ -347,6 +348,10 @@ inline int Position::game_ply() const { return gamePly; } +inline int Position::rule50_count() const { + return st->rule50; +} + inline uint64_t Position::nodes_searched() const { return nodes; } diff --git a/src/search.cpp b/src/search.cpp index 1127df59..a15f259e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -33,6 +33,7 @@ #include "thread.h" #include "tt.h" #include "uci.h" +#include "syzygy/tbprobe.h" namespace Search { @@ -44,6 +45,18 @@ namespace Search { StateStackPtr SetupStates; } +namespace Tablebases { + + int Cardinality; + uint64_t Hits; + bool RootInTB; + bool UseRule50; + Depth ProbeDepth; + Value Score; +} + +namespace TB = Tablebases; + using std::string; using Eval::evaluate; using namespace Search; @@ -186,6 +199,19 @@ void Search::think() { DrawValue[ RootPos.side_to_move()] = VALUE_DRAW - Value(contempt); DrawValue[~RootPos.side_to_move()] = VALUE_DRAW + Value(contempt); + TB::Hits = 0; + TB::RootInTB = false; + TB::UseRule50 = Options["Syzygy50MoveRule"]; + TB::ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY; + TB::Cardinality = Options["SyzygyProbeLimit"]; + + // Skip TB probing when no TB found: !TBLargest -> !TB::Cardinality + if (TB::Cardinality > TB::MaxCardinality) + { + TB::Cardinality = TB::MaxCardinality; + TB::ProbeDepth = DEPTH_ZERO; + } + if (RootMoves.empty()) { RootMoves.push_back(MOVE_NONE); @@ -195,6 +221,37 @@ void Search::think() { } else { + if (TB::Cardinality >= RootPos.count(WHITE) + + RootPos.count(BLACK)) + { + // If the current root position is in the tablebases then RootMoves + // contains only moves that preserve the draw or win. + TB::RootInTB = Tablebases::root_probe(RootPos, RootMoves, TB::Score); + + if (TB::RootInTB) + TB::Cardinality = 0; // Do not probe tablebases during the search + + else // If DTZ tables are missing, use WDL tables as a fallback + { + // Filter out moves that do not preserve a draw or win + TB::RootInTB = Tablebases::root_probe_wdl(RootPos, RootMoves, TB::Score); + + // Only probe during search if winning + if (TB::Score <= VALUE_DRAW) + TB::Cardinality = 0; + } + + if (TB::RootInTB) + { + TB::Hits = RootMoves.size(); + + if (!TB::UseRule50) + TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1 + : TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1 + : VALUE_DRAW; + } + } + for (Thread* th : Threads) th->maxPly = 0; @@ -487,6 +544,36 @@ namespace { return ttValue; } + // Step 4a. Tablebase probe + if (!RootNode && TB::Cardinality) + { + int piecesCnt = pos.count(WHITE) + pos.count(BLACK); + + if ( piecesCnt <= TB::Cardinality + && (piecesCnt < TB::Cardinality || depth >= TB::ProbeDepth) + && pos.rule50_count() == 0) + { + int found, v = Tablebases::probe_wdl(pos, &found); + + if (found) + { + TB::Hits++; + + int drawScore = TB::UseRule50 ? 1 : 0; + + value = v < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply + : v > drawScore ? VALUE_MATE - MAX_PLY - ss->ply + : VALUE_DRAW + 2 * v * drawScore; + + tte->save(posKey, value_to_tt(value, ss->ply), BOUND_EXACT, + std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY), + MOVE_NONE, VALUE_NONE, TT.generation()); + + return value; + } + } + } + // Step 5. Evaluate the position statically and update parent's gain statistics if (inCheck) { @@ -1347,6 +1434,9 @@ moves_loop: // When in check and at SpNode search starts from here Depth d = updated ? depth : depth - ONE_PLY; Value v = updated ? RootMoves[i].score : RootMoves[i].previousScore; + bool tb = TB::RootInTB && abs(v) < VALUE_MATE - MAX_PLY; + v = tb ? TB::Score : v; + if (ss.rdbuf()->in_avail()) // Not at first line ss << "\n"; @@ -1355,11 +1445,12 @@ moves_loop: // When in check and at SpNode search starts from here << " multipv " << i + 1 << " score " << UCI::value(v); - if (i == PVIdx) + if (!tb && i == PVIdx) ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); ss << " nodes " << pos.nodes_searched() << " nps " << pos.nodes_searched() * 1000 / elapsed + << " tbhits " << TB::Hits << " time " << elapsed << " pv"; diff --git a/src/thread.h b/src/thread.h index ba9197ce..bf16ca17 100644 --- a/src/thread.h +++ b/src/thread.h @@ -37,8 +37,6 @@ struct Thread; const int MAX_THREADS = 128; const int MAX_SPLITPOINTS_PER_THREAD = 8; -struct Thread; - /// SplitPoint struct stores information shared by the threads searching in /// parallel below the same split point. It is populated at splitting time. diff --git a/src/types.h b/src/types.h index 364a4a5f..5c9b9366 100644 --- a/src/types.h +++ b/src/types.h @@ -184,8 +184,8 @@ enum Value : int { VALUE_INFINITE = 32001, VALUE_NONE = 32002, - VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, - VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY, + VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, + VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, PawnValueMg = 198, PawnValueEg = 258, KnightValueMg = 817, KnightValueEg = 846, diff --git a/src/uci.cpp b/src/uci.cpp index f82a5f0a..dfcce497 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -220,7 +220,7 @@ string UCI::value(Value v) { stringstream ss; - if (abs(v) < VALUE_MATE_IN_MAX_PLY) + if (abs(v) < VALUE_MATE - MAX_PLY) ss << "cp " << v * 100 / PawnValueEg; else ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 660981cb..28a0ad52 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -25,6 +25,7 @@ #include "thread.h" #include "tt.h" #include "uci.h" +#include "syzygy/tbprobe.h" using std::string; @@ -37,6 +38,7 @@ void on_clear_hash(const Option&) { TT.clear(); } void on_hash_size(const Option& o) { TT.resize(o); } void on_logger(const Option& o) { start_logger(o); } void on_threads(const Option&) { Threads.read_uci_options(); } +void on_tb_path(const Option& o) { Tablebases::init(o); } /// Our case insensitive less() function as required by UCI protocol @@ -66,6 +68,10 @@ void init(OptionsMap& o) { o["Minimum Thinking Time"] << Option(20, 0, 5000); o["Slow Mover"] << Option(80, 10, 1000); o["UCI_Chess960"] << Option(false); + o["SyzygyPath"] << Option("", on_tb_path); + o["SyzygyProbeDepth"] << Option(1, 1, 100); + o["Syzygy50MoveRule"] << Option(true); + o["SyzygyProbeLimit"] << Option(6, 0, 6); } -- 2.39.2