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