Reformat time manager code
[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     if (type == MaxTime)
70         time -= 10; // Keep always at least 10 millisecs on the clock
71
72     return std::max(0, time);
73   }
74
75 } // namespace
76
77
78 /// init() is called at the beginning of the search and calculates the allowed
79 /// thinking time out of the time control and current game ply. We support four
80 /// different kinds of time controls, passed in 'limits':
81 ///
82 ///  inc == 0 && movestogo == 0 means: x basetime  [sudden death!]
83 ///  inc == 0 && movestogo != 0 means: x moves in y minutes
84 ///  inc >  0 && movestogo == 0 means: x basetime + z increment
85 ///  inc >  0 && movestogo != 0 means: x moves in y minutes + z increment
86
87 void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
88 {
89   int moveOverhead = Options["Move Overhead"];
90   int npmsec       = Options["nodestime"];
91   bool ponder      = Options["Ponder"];
92
93   // If we have to play in 'nodes as time' mode, then convert from time
94   // to nodes, and use resulting values in time management formulas.
95   // WARNING: Given npms (nodes per millisecond) must be much lower then
96   // the real engine speed to avoid time losses.
97   if (npmsec)
98   {
99       if (!availableNodes) // Only once at game start
100           availableNodes = npmsec * limits.time[us]; // Time is in msec
101
102       // Convert from millisecs to nodes
103       limits.time[us] = (int)availableNodes;
104       limits.inc[us] *= npmsec;
105       limits.npmsec = npmsec;
106   }
107
108   int moveNum = (ply + 1) / 2;
109
110   startTime = limits.startTime;
111   optimumTime = remaining(limits.time[us], limits.inc[us], moveOverhead,
112                           limits.movestogo, moveNum, ponder, OptimumTime);
113   maximumTime = remaining(limits.time[us], limits.inc[us], moveOverhead,
114                           limits.movestogo, moveNum, ponder, MaxTime);
115 }