From 01d97521fd675ed157ff7d61e6057916abbcc56c Mon Sep 17 00:00:00 2001 From: IIvec Date: Fri, 7 Oct 2016 18:12:19 +0200 Subject: [PATCH] Time management simplification STC (http://tests.stockfishchess.org/tests/view/598188a40ebc5916ff64a21b): LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 25363 W: 4658 L: 4545 D: 16160 LTC (http://tests.stockfishchess.org/tests/view/5981d59a0ebc5916ff64a229): LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 75356 W: 9690 L: 9640 D: 56026 40/10 TC (http://tests.stockfishchess.org/tests/view/5980c5780ebc5916ff64a1ed): LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 19377 W: 3650 L: 3526 D: 12201 15+0 TC (http://tests.stockfishchess.org/tests/view/5982cb730ebc5916ff64a25d): LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 5913 W: 1217 L: 1069 D: 3627 This time management handles base time and movestogo cases separatelly. One can test one case without affecting the other. Also, increment usage can be tested separately without (necessarily) affecting sudden death or x moves in y seconds performance. On stable machines there are no time losses on 0.1+0.001 time control (tested on i7 + Windows 10 platform). Bench 5608839 --- src/timeman.cpp | 99 ++++++++++++++++++----------------------------- src/ucioption.cpp | 2 - 2 files changed, 38 insertions(+), 63 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index 4ed54cdb..55d98c1f 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -32,41 +32,40 @@ namespace { enum TimeType { OptimumTime, MaxTime }; - const int MoveHorizon = 50; // Plan time management at most this many moves ahead - const double MaxRatio = 7.09; // When in trouble, we can step over reserved time with this ratio - const double StealRatio = 0.35; // However we must not steal time from remaining moves over this ratio - - - // move_importance() is a skew-logistic function based on naive statistical - // analysis of "how many games are still undecided after n half-moves". Game - // is considered "undecided" as long as neither side has >275cp advantage. - // Data was extracted from the CCRL game database with some simple filtering criteria. - - double move_importance(int ply) { - - const double XScale = 7.64; - const double XShift = 58.4; - const double Skew = 0.183; - - return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero - } - - template - int remaining(int myTime, int movesToGo, int ply, int slowMover) { - - const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio); - const double TStealRatio = (T == OptimumTime ? 0 : StealRatio); - - double moveImportance = (move_importance(ply) * slowMover) / 100; - double otherMovesImportance = 0; - - for (int i = 1; i < movesToGo; ++i) - otherMovesImportance += move_importance(ply + 2 * i); - - double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance); - double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance); - - return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast + int remaining(int myTime, int myInc, int moveOverhead, + int movesToGo, int ply, TimeType type) { + + if (myTime <= 0) + return 0; + + int moveNumber = (ply + 1) / 2; + double ratio; // Which ratio of myTime we are going to use. It is <= 1 + double sd = 8.5; + + // Usage of increment follows quadratic distribution with the maximum at move 25 + double inc = myInc * std::max(55.0, 120.0 - 0.12 * (moveNumber - 25) * (moveNumber - 25)); + + // In moves-to-go we distribute time according to a quadratic function with + // the maximum around move 20 for 40 moves in y time case. + if (movesToGo) + { + ratio = (type == OptimumTime ? 1.0 : 6.0) / std::min(50, movesToGo); + + if (moveNumber <= 40) + ratio *= 1.1 - 0.001 * (moveNumber - 20) * (moveNumber - 20); + else + ratio *= 1.5; + } + // Otherwise we increase usage of remaining time as the game goes on + else + { + sd = 1 + 20 * moveNumber / (500.0 + moveNumber); + ratio = (type == OptimumTime ? 0.017 : 0.07) * sd; + } + + ratio = std::min(1.0, ratio * (1 + inc / (myTime * sd))); + + return int(ratio * std::max(0, myTime - moveOverhead)); } } // namespace @@ -81,11 +80,9 @@ 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(Search::LimitsType& limits, Color us, int ply) { - - int minThinkingTime = Options["Minimum Thinking Time"]; +void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) +{ 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 @@ -104,28 +101,8 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { } startTime = limits.startTime; - optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime); - - const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon; - - // We calculate optimum time usage for different hypothetical "moves to go"-values - // and choose the minimum of calculated search time values. Usually the greatest - // hypMTG gives the minimum values. - for (int hypMTG = 1; hypMTG <= MaxMTG; ++hypMTG) - { - // Calculate thinking time for hypothetical "moves to go"-value - int hypMyTime = limits.time[us] - + limits.inc[us] * (hypMTG - 1) - - moveOverhead * (2 + std::min(hypMTG, 40)); - - hypMyTime = std::max(hypMyTime, 0); - - int t1 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); - int t2 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); - - optimumTime = std::min(t1, optimumTime); - maximumTime = std::min(t2, maximumTime); - } + optimumTime = remaining(limits.time[us], limits.inc[us], moveOverhead, limits.movestogo, ply, OptimumTime); + maximumTime = remaining(limits.time[us], limits.inc[us], moveOverhead, limits.movestogo, ply, MaxTime); if (Options["Ponder"]) optimumTime += optimumTime / 4; diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 0e049046..8bc4e436 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -66,8 +66,6 @@ void init(OptionsMap& o) { o["MultiPV"] << Option(1, 1, 500); o["Skill Level"] << Option(20, 0, 20); o["Move Overhead"] << Option(30, 0, 5000); - o["Minimum Thinking Time"] << Option(20, 0, 5000); - o["Slow Mover"] << Option(89, 10, 1000); o["nodestime"] << Option(0, 0, 10000); o["UCI_Chess960"] << Option(false); o["SyzygyPath"] << Option("", on_tb_path); -- 2.39.2