Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/Stockfish
Path: blob/master/src/timeman.cpp
376 views
1
/*
2
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
4
5
Stockfish is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
10
Stockfish is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
14
15
You should have received a copy of the GNU General Public License
16
along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#include "timeman.h"
20
21
#include <algorithm>
22
#include <cassert>
23
#include <cmath>
24
#include <cstdint>
25
26
#include "search.h"
27
#include "ucioption.h"
28
29
namespace Stockfish {
30
31
TimePoint TimeManagement::optimum() const { return optimumTime; }
32
TimePoint TimeManagement::maximum() const { return maximumTime; }
33
34
void TimeManagement::clear() {
35
availableNodes = -1; // When in 'nodes as time' mode
36
}
37
38
void TimeManagement::advance_nodes_time(std::int64_t nodes) {
39
assert(useNodesTime);
40
availableNodes = std::max(int64_t(0), availableNodes - nodes);
41
}
42
43
// Called at the beginning of the search and calculates
44
// the bounds of time allowed for the current game ply. We currently support:
45
// 1) x basetime (+ z increment)
46
// 2) x moves in y seconds (+ z increment)
47
void TimeManagement::init(Search::LimitsType& limits,
48
Color us,
49
int ply,
50
const OptionsMap& options,
51
double& originalTimeAdjust) {
52
TimePoint npmsec = TimePoint(options["nodestime"]);
53
54
// If we have no time, we don't need to fully initialize TM.
55
// startTime is used by movetime and useNodesTime is used in elapsed calls.
56
startTime = limits.startTime;
57
useNodesTime = npmsec != 0;
58
59
if (limits.time[us] == 0)
60
return;
61
62
TimePoint moveOverhead = TimePoint(options["Move Overhead"]);
63
64
// optScale is a percentage of available time to use for the current move.
65
// maxScale is a multiplier applied to optimumTime.
66
double optScale, maxScale;
67
68
// If we have to play in 'nodes as time' mode, then convert from time
69
// to nodes, and use resulting values in time management formulas.
70
// WARNING: to avoid time losses, the given npmsec (nodes per millisecond)
71
// must be much lower than the real engine speed.
72
if (useNodesTime)
73
{
74
if (availableNodes == -1) // Only once at game start
75
availableNodes = npmsec * limits.time[us]; // Time is in msec
76
77
// Convert from milliseconds to nodes
78
limits.time[us] = TimePoint(availableNodes);
79
limits.inc[us] *= npmsec;
80
limits.npmsec = npmsec;
81
moveOverhead *= npmsec;
82
}
83
84
// These numbers are used where multiplications, divisions or comparisons
85
// with constants are involved.
86
const int64_t scaleFactor = useNodesTime ? npmsec : 1;
87
const TimePoint scaledTime = limits.time[us] / scaleFactor;
88
89
// Maximum move horizon
90
int centiMTG = limits.movestogo ? std::min(limits.movestogo * 100, 5000) : 5051;
91
92
// If less than one second, gradually reduce mtg
93
if (scaledTime < 1000)
94
centiMTG = scaledTime * 5.051;
95
96
// Make sure timeLeft is > 0 since we may use it as a divisor
97
TimePoint timeLeft =
98
std::max(TimePoint(1),
99
limits.time[us]
100
+ (limits.inc[us] * (centiMTG - 100) - moveOverhead * (200 + centiMTG)) / 100);
101
102
// x basetime (+ z increment)
103
// If there is a healthy increment, timeLeft can exceed the actual available
104
// game time for the current move, so also cap to a percentage of available game time.
105
if (limits.movestogo == 0)
106
{
107
// Extra time according to timeLeft
108
if (originalTimeAdjust < 0)
109
originalTimeAdjust = 0.3128 * std::log10(timeLeft) - 0.4354;
110
111
// Calculate time constants based on current time left.
112
double logTimeInSec = std::log10(scaledTime / 1000.0);
113
double optConstant = std::min(0.0032116 + 0.000321123 * logTimeInSec, 0.00508017);
114
double maxConstant = std::max(3.3977 + 3.03950 * logTimeInSec, 2.94761);
115
116
optScale = std::min(0.0121431 + std::pow(ply + 2.94693, 0.461073) * optConstant,
117
0.213035 * limits.time[us] / timeLeft)
118
* originalTimeAdjust;
119
120
maxScale = std::min(6.67704, maxConstant + ply / 11.9847);
121
}
122
123
// x moves in y seconds (+ z increment)
124
else
125
{
126
optScale =
127
std::min((0.88 + ply / 116.4) / (centiMTG / 100.0), 0.88 * limits.time[us] / timeLeft);
128
maxScale = 1.3 + 0.11 * (centiMTG / 100.0);
129
}
130
131
// Limit the maximum possible time for this move
132
optimumTime = TimePoint(optScale * timeLeft);
133
maximumTime =
134
TimePoint(std::min(0.825179 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10;
135
136
if (options["Ponder"])
137
optimumTime += optimumTime / 4;
138
}
139
140
} // namespace Stockfish
141
142