Restore safety margin of 60ms
[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 moveNum, bool ponder, TimeType type) {
35
36     if (myTime <= 0)
37         return 0;
38
39     double ratio; // Which ratio of myTime we are going to use
40
41     // Usage of increment follows quadratic distribution with the maximum at move 25
42     double inc = myInc * std::max(55.0, 120 - 0.12 * (moveNum - 25) * (moveNum - 25));
43
44     // In moves-to-go we distribute time according to a quadratic function with
45     // the maximum around move 20 for 40 moves in y time case.
46     if (movesToGo)
47     {
48         ratio = (type == OptimumTime ? 1.0 : 6.0) / std::min(50, movesToGo);
49
50         if (moveNum <= 40)
51             ratio *= 1.1 - 0.001 * (moveNum - 20) * (moveNum - 20);
52         else
53             ratio *= 1.5;
54
55         ratio *= 1 + inc / (myTime * 8.5);
56     }
57     // Otherwise we increase usage of remaining time as the game goes on
58     else
59     {
60         double k = 1 + 20 * moveNum / (500.0 + moveNum);
61         ratio = (type == OptimumTime ? 0.017 : 0.07) * (k + inc / myTime);
62     }
63
64     int time = int(std::min(1.0, ratio) * std::max(0, myTime - moveOverhead));
65
66     if (type == OptimumTime && ponder)
67         time *= 1.25;
68
69     return time;
70   }
71
72 } // namespace
73
74
75 /// init() is called at the beginning of the search and calculates the allowed
76 /// thinking time out of the time control and current game ply. We support four
77 /// different kinds of time controls, passed in 'limits':
78 ///
79 ///  inc == 0 && movestogo == 0 means: x basetime  [sudden death!]
80 ///  inc == 0 && movestogo != 0 means: x moves in y minutes
81 ///  inc >  0 && movestogo == 0 means: x basetime + z increment
82 ///  inc >  0 && movestogo != 0 means: x moves in y minutes + z increment
83
84 void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
85 {
86   int moveOverhead = Options["Move Overhead"];
87   int npmsec       = Options["nodestime"];
88   bool ponder      = Options["Ponder"];
89
90   // If we have to play in 'nodes as time' mode, then convert from time
91   // to nodes, and use resulting values in time management formulas.
92   // WARNING: Given npms (nodes per millisecond) must be much lower then
93   // the real engine speed to avoid time losses.
94   if (npmsec)
95   {
96       if (!availableNodes) // Only once at game start
97           availableNodes = npmsec * limits.time[us]; // Time is in msec
98
99       // Convert from millisecs to nodes
100       limits.time[us] = (int)availableNodes;
101       limits.inc[us] *= npmsec;
102       limits.npmsec = npmsec;
103   }
104
105   int moveNum = (ply + 1) / 2;
106
107   startTime = limits.startTime;
108   optimumTime = remaining(limits.time[us], limits.inc[us], moveOverhead,
109                           limits.movestogo, moveNum, ponder, OptimumTime);
110   maximumTime = remaining(limits.time[us], limits.inc[us], moveOverhead,
111                           limits.movestogo, moveNum, ponder, MaxTime);
112 }