diff --git a/src/search.cpp b/src/search.cpp index 4590bd4a..f0c1bdd9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -129,7 +129,6 @@ namespace { }; size_t PVIdx; - TimeManagement Time; EasyMoveManager EasyMove; double BestMoveChanges; Value DrawValue[COLOR_NB]; @@ -218,11 +217,12 @@ template uint64_t Search::perft(Position& pos, Depth depth); void Search::think() { - Time.init(Limits, RootPos.side_to_move(), RootPos.game_ply(), now()); + Color us = RootPos.side_to_move(); + Time.init(Limits, us, RootPos.game_ply(), now()); int contempt = Options["Contempt"] * PawnValueEg / 100; // From centipawns - DrawValue[ RootPos.side_to_move()] = VALUE_DRAW - Value(contempt); - DrawValue[~RootPos.side_to_move()] = VALUE_DRAW + Value(contempt); + DrawValue[ us] = VALUE_DRAW - Value(contempt); + DrawValue[~us] = VALUE_DRAW + Value(contempt); TB::Hits = 0; TB::RootInTB = false; @@ -291,6 +291,11 @@ void Search::think() { Threads.timer->run = false; } + // When playing in 'nodes as time' mode, subtract the searched nodes from + // the available ones before to exit. + if (Limits.npmsec) + Time.availableNodes += Limits.inc[us] - RootPos.nodes_searched(); + // When we reach the maximum depth, we can arrive here without a raise of // Signals.stop. However, if we are pondering or in an infinite search, // the UCI protocol states that we shouldn't print the best move before the diff --git a/src/search.h b/src/search.h index ed2c1e18..5e17b829 100644 --- a/src/search.h +++ b/src/search.h @@ -76,7 +76,7 @@ typedef std::vector RootMoveVector; struct LimitsType { LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC - nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = movestogo = + nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movestogo = depth = movetime = mate = infinite = ponder = 0; } @@ -85,7 +85,7 @@ struct LimitsType { } std::vector searchmoves; - int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, movetime, mate, infinite, ponder; + int time[COLOR_NB], inc[COLOR_NB], npmsec, movestogo, depth, movetime, mate, infinite, ponder; int64_t nodes; }; diff --git a/src/timeman.cpp b/src/timeman.cpp index 4763671a..7a5db255 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -25,6 +25,8 @@ #include "timeman.h" #include "uci.h" +TimeManagement Time; // Our global time management object + namespace { enum TimeType { OptimumTime, MaxTime }; @@ -78,11 +80,27 @@ namespace { /// inc > 0 && movestogo == 0 means: x basetime + z increment /// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment -void TimeManagement::init(const Search::LimitsType& limits, Color us, int ply, TimePoint now) +void TimeManagement::init(Search::LimitsType& limits, Color us, int ply, TimePoint now) { int minThinkingTime = Options["Minimum Thinking Time"]; int moveOverhead = Options["Move Overhead"]; int slowMover = Options["Slow Mover"]; + int npmsec = Options["nodestime"]; + + // If we have to play in 'nodes as time' mode, then convert from time + // to nodes, and use resulting values in time management formulas. + // WARNING: Given npms (nodes per millisecond) must be much lower then + // real engine speed to avoid time losses. + if (npmsec) + { + if (!availableNodes) // Only once at game start + availableNodes = npmsec * limits.time[us]; // Time is in msec + + // Convert from millisecs to nodes + limits.time[us] = (int)availableNodes; + limits.inc[us] *= npmsec; + limits.npmsec = npmsec; + } start = now; unstablePvFactor = 1; diff --git a/src/timeman.h b/src/timeman.h index 3cb59bba..f2c3663b 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -27,12 +27,14 @@ class TimeManagement { public: - void init(const Search::LimitsType& limits, Color us, int ply, TimePoint now); + void init(Search::LimitsType& limits, Color us, int ply, TimePoint now); void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; } int available() const { return int(optimumTime * unstablePvFactor * 0.76); } int maximum() const { return maximumTime; } int elapsed() const { return now() - start; } + int64_t availableNodes; // When in 'nodes as time' mode + private: TimePoint start; int optimumTime; @@ -40,4 +42,6 @@ private: double unstablePvFactor; }; +extern TimeManagement Time; + #endif // #ifndef TIMEMAN_H_INCLUDED diff --git a/src/uci.cpp b/src/uci.cpp index 2c07807c..a9fe4f20 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -26,6 +26,7 @@ #include "position.h" #include "search.h" #include "thread.h" +#include "timeman.h" #include "tt.h" #include "uci.h" @@ -178,8 +179,12 @@ void UCI::loop(int argc, char* argv[]) { << "\n" << Options << "\nuciok" << sync_endl; + else if (token == "ucinewgame") + { + TT.clear(); + Time.availableNodes = 0; + } else if (token == "isready") sync_cout << "readyok" << sync_endl; - else if (token == "ucinewgame") TT.clear(); else if (token == "go") go(pos, is); else if (token == "position") position(pos, is); else if (token == "setoption") setoption(is); diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 06641cf5..35c907ae 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -67,6 +67,7 @@ void init(OptionsMap& o) { o["Move Overhead"] << Option(30, 0, 5000); o["Minimum Thinking Time"] << Option(20, 0, 5000); o["Slow Mover"] << Option(80, 10, 1000); + o["nodestime"] << Option(0, 0, 10000); o["UCI_Chess960"] << Option(false); o["SyzygyPath"] << Option("", on_tb_path); o["SyzygyProbeDepth"] << Option(1, 1, 100);