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
This commit is contained in:
IIvec
2016-10-07 18:12:19 +02:00
committed by Marco Costalba
parent d482e3a890
commit 01d97521fd
2 changed files with 31 additions and 56 deletions

View File

@@ -32,41 +32,40 @@ namespace {
enum TimeType { OptimumTime, MaxTime }; enum TimeType { OptimumTime, MaxTime };
const int MoveHorizon = 50; // Plan time management at most this many moves ahead int remaining(int myTime, int myInc, int moveOverhead,
const double MaxRatio = 7.09; // When in trouble, we can step over reserved time with this ratio int movesToGo, int ply, TimeType type) {
const double StealRatio = 0.35; // However we must not steal time from remaining moves over this ratio
if (myTime <= 0)
return 0;
// move_importance() is a skew-logistic function based on naive statistical int moveNumber = (ply + 1) / 2;
// analysis of "how many games are still undecided after n half-moves". Game double ratio; // Which ratio of myTime we are going to use. It is <= 1
// is considered "undecided" as long as neither side has >275cp advantage. double sd = 8.5;
// Data was extracted from the CCRL game database with some simple filtering criteria.
double move_importance(int ply) { // 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));
const double XScale = 7.64; // In moves-to-go we distribute time according to a quadratic function with
const double XShift = 58.4; // the maximum around move 20 for 40 moves in y time case.
const double Skew = 0.183; if (movesToGo)
{
ratio = (type == OptimumTime ? 1.0 : 6.0) / std::min(50, movesToGo);
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero 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;
} }
template<TimeType T> ratio = std::min(1.0, ratio * (1 + inc / (myTime * sd)));
int remaining(int myTime, int movesToGo, int ply, int slowMover) {
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio); return int(ratio * std::max(0, myTime - moveOverhead));
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
} }
} // namespace } // namespace
@@ -81,11 +80,9 @@ namespace {
/// inc > 0 && movestogo == 0 means: x basetime + z increment /// inc > 0 && movestogo == 0 means: x basetime + z increment
/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment /// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
{
int minThinkingTime = Options["Minimum Thinking Time"];
int moveOverhead = Options["Move Overhead"]; int moveOverhead = Options["Move Overhead"];
int slowMover = Options["Slow Mover"];
int npmsec = Options["nodestime"]; int npmsec = Options["nodestime"];
// If we have to play in 'nodes as time' mode, then convert from time // 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; startTime = limits.startTime;
optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime); 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);
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<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
int t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
optimumTime = std::min(t1, optimumTime);
maximumTime = std::min(t2, maximumTime);
}
if (Options["Ponder"]) if (Options["Ponder"])
optimumTime += optimumTime / 4; optimumTime += optimumTime / 4;

View File

@@ -66,8 +66,6 @@ void init(OptionsMap& o) {
o["MultiPV"] << Option(1, 1, 500); o["MultiPV"] << Option(1, 1, 500);
o["Skill Level"] << Option(20, 0, 20); o["Skill Level"] << Option(20, 0, 20);
o["Move Overhead"] << Option(30, 0, 5000); 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["nodestime"] << Option(0, 0, 10000);
o["UCI_Chess960"] << Option(false); o["UCI_Chess960"] << Option(false);
o["SyzygyPath"] << Option("<empty>", on_tb_path); o["SyzygyPath"] << Option("<empty>", on_tb_path);