Line data Source code
1 : #include "timeMan.hpp"
2 :
3 : #include "dynamicConfig.hpp"
4 : #include "logging.hpp"
5 : #include "moveApply.hpp"
6 : #include "position.hpp"
7 : #include "searcher.hpp"
8 : #include "threading.hpp"
9 :
10 : namespace TimeMan {
11 :
12 : TimeType msecPerMove = 777, msecInTC = -1, msecInc = -1, msecUntilNextTC = -1, overHead = 0;
13 : TimeType targetTime = 0, maxTime = 0;
14 : int moveToGo = -1, nbMoveInTC = -1;
15 : uint64_t maxNodes = 0;
16 : bool isDynamic = false;
17 :
18 2 : void init() {
19 2 : Logging::LogIt(Logging::logInfo) << "Init timeman";
20 : ///@todo shall be reset some stuff here ?
21 2 : }
22 :
23 455 : TimeType getNextMSecPerMove(const Position& p) {
24 :
25 455 : const TimeType msecMarginMin = DynamicConfig::moveOverHead; // minimum margin
26 455 : const TimeType msecMarginMax = 3000; // maximum margin
27 455 : const float msecMarginCoef = 0.01f; // part of the remaining time use as margin
28 : auto getMargin = [&](TimeType remainingTime){
29 94 : return std::max(std::min(msecMarginMax, static_cast<TimeType>(msecMarginCoef * static_cast<float>(remainingTime))), msecMarginMin);
30 : };
31 :
32 : TimeType ms = -1; // this is what we will compute here
33 :
34 455 : Logging::LogIt(Logging::logInfo) << "msecPerMove " << msecPerMove;
35 455 : Logging::LogIt(Logging::logInfo) << "msecInTC " << msecInTC;
36 455 : Logging::LogIt(Logging::logInfo) << "msecInc " << msecInc;
37 455 : Logging::LogIt(Logging::logInfo) << "nbMoveInTC " << nbMoveInTC;
38 455 : Logging::LogIt(Logging::logInfo) << "msecUntilNextTC " << msecUntilNextTC;
39 910 : Logging::LogIt(Logging::logInfo) << "currentNbMoves " << static_cast<int>(p.moves);
40 910 : Logging::LogIt(Logging::logInfo) << "moveToGo " << static_cast<int>(moveToGo);
41 455 : Logging::LogIt(Logging::logInfo) << "maxNodes " << maxNodes;
42 :
43 455 : const TimeType msecIncLoc = (msecInc > 0) ? msecInc : 0;
44 :
45 : TimeType msecMargin = msecMarginMin;
46 :
47 455 : if (maxNodes > 0) {
48 0 : Logging::LogIt(Logging::logInfo) << "Fixed nodes per move";
49 0 : targetTime = INFINITETIME;
50 0 : maxTime = INFINITETIME;
51 0 : return targetTime;
52 : }
53 :
54 455 : else if (msecPerMove > 0) {
55 408 : Logging::LogIt(Logging::logInfo) << "Fixed time per move";
56 408 : targetTime = msecPerMove;
57 408 : maxTime = msecPerMove;
58 408 : return targetTime;
59 : }
60 :
61 47 : else if (nbMoveInTC > 0) { // mps is given (xboard style)
62 : assert(nbMoveInTC > 0);
63 1 : Logging::LogIt(Logging::logInfo) << "Xboard style TC";
64 1 : if (!isDynamic){
65 0 : assert(msecInTC > 0);
66 : // we have all static information here : nbMoveInTC in a nbMoveInTC TC with msecIncLoc of increment at each move.
67 : // so we simply split time in equal parts
68 : // we use the minimal margin
69 : // WARNING note that this might be a bit stupid when used with a book
70 0 : const TimeType totalIncr = nbMoveInTC * msecIncLoc;
71 0 : ms = static_cast<TimeType>(static_cast<float>(msecInTC + totalIncr - msecMargin) / static_cast<float>(nbMoveInTC));
72 : }
73 : else {
74 1 : assert(msecUntilNextTC > 0);
75 : // we have some dynamic information here :
76 : // - the next TC will be in : (nbMoveInTC - ((p.moves - 1) % nbMoveInTC)) moves
77 : // - current remaining time is msecUntilNextTC
78 : // remainings increment sum is computed
79 : // a margin will be used
80 : // WARNING using this late moves in current TC can be a little longer depending on the margin used
81 1 : const int moveUntilNextTC = nbMoveInTC - ((p.moves - 1) % nbMoveInTC); // this modulus is a bit slow probably
82 1 : const TimeType remainingIncr = moveUntilNextTC * msecIncLoc;
83 : msecMargin = getMargin(msecUntilNextTC);
84 1 : ms = static_cast<TimeType>(static_cast<float>(msecUntilNextTC + remainingIncr - msecMargin) / static_cast<float>(moveUntilNextTC));
85 : }
86 : }
87 :
88 46 : else if (moveToGo > 0) { // moveToGo is given (uci style)
89 0 : assert(msecUntilNextTC > 0);
90 0 : Logging::LogIt(Logging::logInfo) << "UCI style TC";
91 0 : if (!isDynamic){
92 0 : Logging::LogIt(Logging::logFatal) << "bad timing configuration ... (missing dynamic UCI time info)";
93 : }
94 : else {
95 : // we have dynamic information here :
96 : // - move to go before next TC
97 : // - current remaining time
98 : // remainings increment sum is computed
99 : // a margin will be used
100 : // a correction factor is applied for UCI pondering in order to search longer ///@todo why only 3/2 ...
101 0 : const TimeType remainingIncr = moveToGo * msecIncLoc;
102 0 : const float ponderingCorrection = (ThreadPool::instance().main().getData().isPondering ? 3 : 2) / 2.f;
103 0 : msecMargin = getMargin(msecUntilNextTC);
104 0 : ms = static_cast<TimeType>((static_cast<float>(msecUntilNextTC + remainingIncr - msecMargin) / static_cast<float>(moveToGo) ) * ponderingCorrection);
105 : }
106 : }
107 :
108 : else { // sudden death style
109 46 : assert(msecUntilNextTC >= 0);
110 46 : Logging::LogIt(Logging::logInfo) << "Suddendeath style";
111 46 : if (!isDynamic) Logging::LogIt(Logging::logFatal) << "bad timing configuration ... (missing dynamic time info for sudden death style TC)";
112 : // we have only remaining time available
113 : // we will do "as if" nmoves still need to be played
114 : // we will take into account the time needed to parse input and get current position (overhead)
115 : // in situation where increment is smaller than minimum margin we will be a lot more conservative
116 : // a margin will be used
117 : // a correction factor is applied for UCI pondering in order to search longer ///@todo why only 3/2 ...
118 : // be carefull here !, using xboard (for instance under cutechess), we won't receive an increment
119 : // moreover, we won't assume that receiving an increment once is enough to be sure we will receive it next time
120 : // so we won't try to live on increment too soon. This will only happens when the game goes on and "nmoves" gets smaller...
121 :
122 : // if increment is smaller than the minimal lost by move, this is a "risky TC situation"
123 46 : const bool riskySituation = msecInc < msecMinimal;
124 : // in case of a non risky situation, we can make some correction
125 : // if game is long we will hope the end is near (between 0 and 15)
126 92 : const int gameLengthContrib = riskySituation ? static_cast<int>(std::min(15.f, p.halfmoves / 20.f))
127 0 : : static_cast<int>(std::min(15.f, p.halfmoves / 10.f));
128 : // if material is low we will hope the end is near (between 0 and 15)
129 46 : ScoreType sw = 0;
130 46 : ScoreType sb = 0;
131 46 : const float gp = gamePhase(p.mat, sw, sb);
132 46 : const int gamePhaseContrib = riskySituation ? 0
133 0 : : static_cast<int>((1.f-gp)*15);
134 : // the applied correction will be the bigger one
135 46 : const int nmovesCorrection = std::max(gameLengthContrib, gamePhaseContrib);
136 46 : const int nmoves = riskySituation ? 28 : (16 - nmovesCorrection);
137 :
138 46 : if (nmoves * msecMinimal > msecUntilNextTC){
139 56 : Logging::LogIt(Logging::logGUI) << Logging::_protocolComment[Logging::ct] << "Minic is in time trouble ...";
140 : }
141 :
142 46 : msecMargin = getMargin(msecUntilNextTC);
143 46 : const float frac = (static_cast<float>(msecUntilNextTC - msecMargin) / static_cast<float>(nmoves));
144 46 : const float incrBonus = riskySituation ? 0 : std::min(0.9f * msecIncLoc, msecUntilNextTC - msecMargin - frac);
145 46 : const float ponderingCorrection = (ThreadPool::instance().main().getData().isPondering ? 3 : 2) / 2.f;
146 46 : ms = static_cast<TimeType>( (frac + incrBonus) * ponderingCorrection);
147 :
148 46 : Logging::LogIt(Logging::logInfo) << "risky ? " << riskySituation;
149 46 : Logging::LogIt(Logging::logInfo) << "msecIncLoc " << msecIncLoc;
150 46 : Logging::LogIt(Logging::logInfo) << "msecUntilNextTC " << msecUntilNextTC;
151 46 : Logging::LogIt(Logging::logInfo) << "gameLengthContrib " << gameLengthContrib;
152 46 : Logging::LogIt(Logging::logInfo) << "gamePhaseContrib " << gamePhaseContrib;
153 46 : Logging::LogIt(Logging::logInfo) << "nmoves " << nmoves;
154 92 : Logging::LogIt(Logging::logInfo) << "p.moves " << static_cast<int>(p.moves);
155 92 : Logging::LogIt(Logging::logInfo) << "p.halfmoves " << static_cast<int>(p.halfmoves);
156 46 : Logging::LogIt(Logging::logInfo) << "frac " << frac;
157 46 : Logging::LogIt(Logging::logInfo) << "incrBonus " << incrBonus;
158 46 : Logging::LogIt(Logging::logInfo) << "ms " << ms;
159 : }
160 :
161 : // take overhead into account
162 : // and if not much time left, let's try to return msecMinimal and hope for a friendly GUI...
163 47 : targetTime = std::max(ms - overHead, msecMinimal);
164 : // maxTime will be our limit even in case of trouble
165 47 : maxTime = std::min(msecUntilNextTC - msecMargin, targetTime * 7);
166 47 : return targetTime;
167 : }
168 :
169 1 : void simulate(const TCType tcType, const TimeType initialTime, const TimeType increment, const int movesInTC, const TimeType guiLag){
170 :
171 1 : Logging::LogIt(Logging::logInfo) << "Start of TC simulation";
172 :
173 : // init TC parameter
174 1 : isDynamic = tcType == TC_suddendeath;
175 1 : nbMoveInTC = tcType == TC_repeating ? movesInTC : -1;
176 1 : msecPerMove = -1;
177 1 : msecInTC = tcType == TC_repeating ? initialTime : static_cast<TimeType>(-1);
178 1 : msecInc = increment;
179 1 : msecUntilNextTC = tcType == TC_suddendeath ? initialTime : static_cast<TimeType>(-1);
180 1 : moveToGo = -1;
181 :
182 : // and check configuration
183 1 : switch(tcType){
184 1 : case TC_suddendeath:
185 1 : assert(msecUntilNextTC >= 0);
186 : break;
187 0 : case TC_repeating:
188 0 : assert(msecInTC > 0);
189 0 : assert(nbMoveInTC > 0);
190 : break;
191 0 : case TC_fixed:
192 0 : assert(false); // not used here
193 : break;
194 : }
195 :
196 2 : RootPosition p{std::string(startPosition)};
197 1 : Position p2 = p;
198 : #ifdef WITH_NNUE
199 1 : NNUEEvaluator evaluator;
200 : p2.associateEvaluator(evaluator);
201 1 : p2.resetNNUEEvaluator(p2.evaluator());
202 : #endif
203 :
204 1 : TimeType remaining = msecUntilNextTC;
205 : TimeType currentMoveMs = 0;
206 :
207 47 : while( remaining > 0 && p2.moves < 300){
208 46 : currentMoveMs = getNextMSecPerMove(p2);
209 46 : Logging::LogIt(Logging::logInfo) << "currentMoveMs " << currentMoveMs;
210 46 : remaining -= currentMoveMs;
211 46 : remaining += msecInc;
212 46 : remaining -= guiLag;
213 46 : Logging::LogIt(Logging::logInfo) << "remaining " << remaining;
214 46 : switch(tcType){
215 46 : case TC_suddendeath:
216 46 : msecUntilNextTC = remaining;
217 46 : break;
218 : case TC_repeating:
219 : break;
220 : case TC_fixed:
221 : break;
222 : }
223 46 : const bool isInCheck = isPosInCheck(p2);
224 46 : ThreadData _data;
225 46 : _data.score = randomMover(p2, _data.pv, isInCheck);
226 46 : if ( _data.pv.empty() ) break;
227 46 : if (const MoveInfo moveInfo(p2,_data.pv[0]); ! applyMove(p2, moveInfo)) break;
228 92 : std::cout << GetFEN(p2) << std::endl;
229 : }
230 2 : Logging::LogIt(Logging::logInfo) << "End of TC simulation : " << (remaining > 0 ? "success" : "failed");
231 1 : }
232 :
233 : } // namespace TimeMan
|