Collect more corrections to optimum/maximum
[stockfish] / src / timeman.cpp
1 /*
2   Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3   Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
4   Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
5   Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
6
7   Stockfish is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   Stockfish is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <algorithm>
22
23 #include "search.h"
24 #include "timeman.h"
25 #include "uci.h"
26
27 TimeManagement Time; // Our global time management object
28
29 namespace {
30
31   enum TimeType { OptimumTime, MaxTime };
32
33   int remaining(int myTime, int myInc, int moveOverhead, int movesToGo,
34                 int ply, bool ponder, TimeType type) {
35
36     if (myTime <= 0)
37         return 0;
38
39     int moveNumber = (ply + 1) / 2;
40     double ratio; // Which ratio of myTime we are going to use. It's <= 1 if not ponder
41     double sd = 8.5;
42
43     // Usage of increment follows quadratic distribution with the maximum at move 25
44     double inc = myInc * std::max(55.0, 120.0 - 0.12 * (moveNumber - 25) * (moveNumber - 25));
45
46     // In moves-to-go we distribute time according to a quadratic function with
47     // the maximum around move 20 for 40 moves in y time case.
48     if (movesToGo)
49     {
50         ratio = (type == OptimumTime ? 1.0 : 6.0) / std::min(50, movesToGo);
51
52         if (moveNumber <= 40)
53             ratio *= 1.1 - 0.001 * (moveNumber - 20) * (moveNumber - 20);
54         else
55             ratio *= 1.5;
56     }
57     // Otherwise we increase usage of remaining time as the game goes on
58     else
59     {
60         sd = 1 + 20 * moveNumber / (500.0 + moveNumber);
61         ratio = (type == OptimumTime ? 0.017 : 0.07) * sd;
62     }
63
64     ratio = std::min(1.0, ratio * (1 + inc / (myTime * sd)));
65
66     assert(ratio <= 1);
67
68     if (ponder && type == OptimumTime)
69         ratio *= 1.25;
70
71     int time = int(ratio * (myTime - moveOverhead));
72
73     if (type == OptimumTime)
74         time -= 10; // Keep always at least 10 millisecs on the clock
75
76     return std::max(0, time);
77   }
78
79 } // namespace
80
81
82 /// init() is called at the beginning of the search and calculates the allowed
83 /// thinking time out of the time control and current game ply. We support four
84 /// different kinds of time controls, passed in 'limits':
85 ///
86 ///  inc == 0 && movestogo == 0 means: x basetime  [sudden death!]
87 ///  inc == 0 && movestogo != 0 means: x moves in y minutes
88 ///  inc >  0 && movestogo == 0 means: x basetime + z increment
89 ///  inc >  0 && movestogo != 0 means: x moves in y minutes + z increment
90
91 void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
92 {
93   int moveOverhead = Options["Move Overhead"];
94   int npmsec       = Options["nodestime"];
95   bool ponder      = Options["Ponder"];
96
97   // If we have to play in 'nodes as time' mode, then convert from time
98   // to nodes, and use resulting values in time management formulas.
99   // WARNING: Given npms (nodes per millisecond) must be much lower then
100   // the real engine speed to avoid time losses.
101   if (npmsec)
102   {
103       if (!availableNodes) // Only once at game start
104           availableNodes = npmsec * limits.time[us]; // Time is in msec
105
106       // Convert from millisecs to nodes
107       limits.time[us] = (int)availableNodes;
108       limits.inc[us] *= npmsec;
109       limits.npmsec = npmsec;
110   }
111
112   startTime = limits.startTime;
113   optimumTime = remaining(limits.time[us], limits.inc[us], moveOverhead,
114                           limits.movestogo, ply, ponder, OptimumTime);
115   maximumTime = remaining(limits.time[us], limits.inc[us], moveOverhead,
116                           limits.movestogo, ply, ponder, MaxTime);
117 }