1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-29 16:23:09 +00:00

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 };
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
int remaining(int myTime, int myInc, int moveOverhead,
int movesToGo, int ply, TimeType type) {
if (myTime <= 0)
return 0;
// 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.
int moveNumber = (ply + 1) / 2;
double ratio; // Which ratio of myTime we are going to use. It is <= 1
double sd = 8.5;
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;
const double XShift = 58.4;
const double Skew = 0.183;
// 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);
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>
int remaining(int myTime, int movesToGo, int ply, int slowMover) {
ratio = std::min(1.0, ratio * (1 + inc / (myTime * sd)));
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
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<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
int t2 = minThinkingTime + remaining<MaxTime >(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;

View file

@ -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("<empty>", on_tb_path);