From: Fabian Fichter Date: Mon, 10 Aug 2020 20:35:47 +0000 (+0200) Subject: Merge official-stockfish/master X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=41c227fd850a75cfe882e9e886fac32394ad56e5;p=fairystockfish.git Merge official-stockfish/master Merge new time management. --- 41c227fd850a75cfe882e9e886fac32394ad56e5 diff --cc src/timeman.cpp index 9435b17,45e9db5..c83859b --- a/src/timeman.cpp +++ b/src/timeman.cpp @@@ -29,60 -28,12 +29,12 @@@ TimeManagement Time; // Our global time management object - namespace { - - enum TimeType { OptimumTime, MaxTime }; - - constexpr int MoveHorizon = 50; // Plan time management at most this many moves ahead - constexpr double MaxRatio = 7.3; // When in trouble, we can step over reserved time with this ratio - constexpr double StealRatio = 0.34; // 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(const Position& pos, int ply) { - - constexpr double XScale = 6.85; - double XShift = (pos.max_rank() + 1) * (pos.max_file() + 1) / (1 + pos.must_capture()) + 0.5; - constexpr double Skew = 0.171; - - return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero - } - - template - TimePoint remaining(const Position& pos, TimePoint myTime, int movesToGo, int ply, TimePoint slowMover) { - - constexpr double TMaxRatio = (T == OptimumTime ? 1.0 : MaxRatio); - constexpr double TStealRatio = (T == OptimumTime ? 0.0 : StealRatio); - - double moveImportance = (move_importance(pos, ply) * slowMover) / 100.0; - double otherMovesImportance = 0.0; - - for (int i = 1; i < movesToGo; ++i) - otherMovesImportance += move_importance(pos, ply + 2 * i); - - double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance); - double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance); - - return TimePoint(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast - } - - } // namespace - - - /// init() is called at the beginning of the search and calculates the allowed - /// thinking time out of the time control and current game ply. We support four - /// different kinds of time controls, passed in 'limits': - /// - /// inc == 0 && movestogo == 0 means: x basetime [sudden death!] - /// inc == 0 && movestogo != 0 means: x moves in y minutes - /// inc > 0 && movestogo == 0 means: x basetime + z increment - /// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment + /// init() is called at the beginning of the search and calculates the bounds + /// of time allowed for the current game ply. We currently support: + // 1) x basetime (+z increment) + // 2) x moves in y seconds (+z increment) -void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { +void TimeManagement::init(const Position& pos, Search::LimitsType& limits, Color us, int ply) { TimePoint minThinkingTime = Options["Minimum Thinking Time"]; TimePoint moveOverhead = Options["Move Overhead"]; @@@ -106,42 -60,40 +61,53 @@@ } startTime = limits.startTime; - optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime); - const int maxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon; + //Maximum move horizon of 50 moves + int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; - // 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 - hypMyTime = limits.time[us] - + limits.inc[us] * (hypMTG - 1) - - moveOverhead * (2 + std::min(hypMTG, 40)); + // Make sure timeLeft is > 0 since we may use it as a divisor + TimePoint timeLeft = std::max(TimePoint(1), + limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); - // Adjust time management for four-player variants - if (pos.two_boards()) ++ // Adjust time management for four-player variants ++ if (pos.two_boards()) ++ { ++ if (Partner.partnerDead && Partner.opptime) ++ timeLeft -= Partner.opptime * 10; ++ else + { - if (Partner.partnerDead && Partner.opptime) - hypMyTime -= Partner.opptime * 10; - else - { - hypMyTime = std::min(hypMyTime, 5000 + std::min(std::abs(limits.time[us] - Partner.opptime * 10), TimePoint(Partner.opptime * 10))); - if (Partner.fast || Partner.partnerDead) - hypMyTime /= 4; - } ++ timeLeft = std::min(timeLeft, 5000 + std::min(std::abs(limits.time[us] - Partner.opptime * 10), TimePoint(Partner.opptime * 10))); ++ if (Partner.fast || Partner.partnerDead) ++ timeLeft /= 4; + } ++ } + - hypMyTime = std::max(hypMyTime, TimePoint(0)); + // A user may scale time usage by setting UCI option "Slow Mover" + // Default is 100 and changing this value will probably lose elo. + timeLeft = slowMover * timeLeft / 100; - TimePoint t1 = minThinkingTime + remaining(pos, hypMyTime, hypMTG, ply, slowMover); - TimePoint t2 = minThinkingTime + remaining(pos, hypMyTime, hypMTG, ply, slowMover); + // x basetime (+ z increment) + // If there is a healthy increment, timeLeft can exceed actual available + // game time for the current move, so also cap to 20% of available game time. + if (limits.movestogo == 0) + { + opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0, + 0.2 * limits.time[us] / double(timeLeft)); + max_scale = 4 + std::min(36, ply) / 12.0; + } - optimumTime = std::min(t1, optimumTime); - maximumTime = std::min(t2, maximumTime); + // x moves in y seconds (+ z increment) + else + { + opt_scale = std::min((0.8 + ply / 128.0) / mtg, + 0.8 * limits.time[us] / double(timeLeft)); + max_scale = std::min(6.3, 1.5 + 0.11 * mtg); } + // Never use more than 80% of the available time for this move + optimumTime = std::max(minThinkingTime, opt_scale * timeLeft); + maximumTime = std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime); + if (Options["Ponder"]) optimumTime += optimumTime / 4; } diff --cc src/ucioption.cpp index c2be249,66fd42d..7cdbb44 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@@ -148,13 -67,12 +148,13 @@@ void init(OptionsMap& o) o["Clear Hash"] << Option(on_clear_hash); o["Ponder"] << Option(false); o["MultiPV"] << Option(1, 1, 500); - o["Skill Level"] << Option(20, 0, 20); + o["Skill Level"] << Option(20, -20, 20); - o["Move Overhead"] << Option(30, 0, 5000); - o["Minimum Thinking Time"] << Option(20, 0, 5000); - o["Slow Mover"] << Option(84, 10, 1000); + o["Move Overhead"] << Option(10, 0, 5000); + o["Minimum Thinking Time"] << Option( 0, 0, 5000); + o["Slow Mover"] << Option(100, 10, 1000); o["nodestime"] << Option(0, 0, 10000); o["UCI_Chess960"] << Option(false); + o["UCI_Variant"] << Option("chess", variants.get_keys(), on_variant_change); o["UCI_AnalyseMode"] << Option(false); o["UCI_LimitStrength"] << Option(false); o["UCI_Elo"] << Option(1350, 1350, 2850);