Belofte version 2.1.9
A promising chess program using the UCI or Winboard interface
level.cpp
Go to the documentation of this file.
1/*---------------------------------------------------------------------+
2 * File: level.cpp
3 * Project: part of belofte - A Promising Chess Program
4 * Author: yves
5 * SPDX-License-Identifier: GPL-2.0-only
6+----------------------------------------------------------------------*/
7
8#include "belofte.h"
9
10// --------------------------------------------------------------------
11
13{
14 m_oType = LevelType::Infinite;
15
19
20 recalibrateTime();
21}
22
23void bLevel::setGameTime(int const msPerGame)
24{
25 m_oType = LevelType::SuddenDeath;
26 if (hasLevelChanged()) {
27 m_timeForGame = msPerGame;
29 }
30
31 m_movesPerPeriod = 0;
32 m_incrementPerMove = 0;
33 m_nTimeLeftForGame = msPerGame;
34
37
38 recalibrateTime();
39}
40
41void bLevel::setGameTime(int const msPerGame, int const msIncrementPerMove)
42{
44 if (hasLevelChanged()) {
45 m_timeForGame = msPerGame;
47 }
48 m_movesPerPeriod = 0;
49 m_incrementPerMove = msIncrementPerMove;
50
51 m_nTimeLeftForGame = msPerGame;
52
55
56 recalibrateTime();
57}
58
59void bLevel::setMoveTime(int const msPerMove)
60{
62 if (hasLevelChanged()) {
64 }
65 m_movesPerPeriod = 0;
66
67 m_nTimeForMove = msPerMove;
68
71
72 // pre-calibration
73 m_nTimeForMove -= TIME_LOSTINENGINE;
74 m_nMaxTimeForMove = m_nTimeForMove;
75 m_nTimeLeftForGame = m_nMaxTimeForMove + TIME_LOSTINENGINE;
76
77 recalibrateTime();
78}
79
80void bLevel::setMoveTime(int const msPerGame, int const nMoves)
81{
82 m_oType = LevelType::TimeControl;
83 if (hasLevelChanged()) {
84 m_timeForGame = msPerGame;
85 m_movesPerPeriod = nMoves;
87 }
88
89 m_incrementPerMove = 0;
90 m_nRemainingMovesForPeriod = nMoves;
91 m_nTimeLeftForGame = msPerGame;
92
95
96 recalibrateTime();
97}
98
99void bLevel::setMoveTime(int const msPerGame,
100 int const nMoves, int const msIncrementPerMove)
101{
103 if (hasLevelChanged()) {
104 m_movesPerPeriod = nMoves;
105 m_timeForGame = msPerGame;
107 }
108 m_incrementPerMove = msIncrementPerMove;
109
110 m_nRemainingMovesForPeriod = nMoves;
111 m_nTimeLeftForGame = msPerGame;
112
115
116 recalibrateTime();
117}
118
120{
121 m_oType = LevelType::MateSearch;
122
124 setQSDepth(d);
125 setMaxDepth(d);
126
127 recalibrateTime();
128}
129
130/**
131 * xboard issues time command to update available time
132 */
133void bLevel::setRemainingTime(const int msPerGame)
134{
135 m_nTimeLeftForGame = msPerGame;
136
137 int nInc = m_incrementPerMove;
138 m_incrementPerMove = 0; // UCI adds time per move before move, so put to 0
139 recalibrateTime();
140 m_incrementPerMove = nInc;
141}
142
144{
145 --m_nRemainingMovesForPeriod;
146 if (m_nRemainingMovesForPeriod < 1)
147 m_nRemainingMovesForPeriod = m_movesPerPeriod;
148
149 recalibrateTime();
150}
151
152/**
153 * used for recalibrating time in case for undo move in
154 * xboard moves per period
155 */
157{
158 ++m_nRemainingMovesForPeriod;
159
160 recalibrateTime();
161}
162
163void bLevel::recalibrateTime()
164{
165 /// @todo more intelligent number of moves left
166 /// 35 at game start with 32 pieces on board, 20 in endgame
167 if (m_oType == LevelType::SuddenDeath) {
168 m_nTimeForMove = m_nTimeLeftForGame / 24;
169 m_nMaxTimeForMove = m_nTimeLeftForGame / 12;
170 } else if (m_oType == LevelType::FicherTimeControl) {
171 if (m_movesPerPeriod) {
172 // FC control with moves per period, meaning it can repeat after this
173 if (!m_nRemainingMovesForPeriod) {
174 // avoid divide by zero
175 m_nRemainingMovesForPeriod = 1;
176 }
177 m_nTimeForMove = (m_nTimeLeftForGame / (m_nRemainingMovesForPeriod + 1)) + m_incrementPerMove;
178 if (m_nRemainingMovesForPeriod > 3) {
179 m_nMaxTimeForMove = (m_nTimeLeftForGame / (m_nRemainingMovesForPeriod/3)) + m_incrementPerMove / 2;
180 } else {
181 m_nMaxTimeForMove = m_nTimeForMove;
182 }
183 } else {
184 // FC without period cut-off, so we need to be less generous at giving time
185 m_nTimeForMove = (m_nTimeLeftForGame / 20) + m_incrementPerMove;
186 m_nMaxTimeForMove = (m_nTimeLeftForGame / 10) + m_incrementPerMove;
187 }
188 } else if ((m_oType == LevelType::TimeControl) && m_movesPerPeriod) {
189 if (!m_nRemainingMovesForPeriod) {
190 // avoid divide by zero
191 m_nRemainingMovesForPeriod = 1;
192 }
193 if (m_nRemainingMovesForPeriod > 10) {
194 m_nTimeForMove = m_nTimeLeftForGame / m_nRemainingMovesForPeriod;
195 m_nMaxTimeForMove = m_nTimeLeftForGame / (m_nRemainingMovesForPeriod - 5);
196 } else {
197 m_nTimeForMove = m_nTimeLeftForGame / m_nRemainingMovesForPeriod;
198 m_nMaxTimeForMove = m_nTimeForMove;
199 }
200 }
201
202 // make sure that position is not lost because of overhead outside search
203 if (m_nMaxTimeForMove > m_nTimeLeftForGame - TIME_LOSTINENGINE)
204 m_nMaxTimeForMove = m_nTimeLeftForGame - TIME_LOSTINENGINE;
205
206 if (m_nTimeForMove > m_nMaxTimeForMove)
207 m_nTimeForMove = m_nMaxTimeForMove;
208
209 m_nEstAllowNextIterationTime = TIME_UNDERFLOWMULTIPLYER
210 * m_nTimeForMove / TIME_UNDERFLOWDEVIDER;
211
212 m_nAbsoluteAbortTime = m_nTimeForMove * TIME_OVERFLOWMULTIPLYER;
213 if (m_nAbsoluteAbortTime < m_nMaxTimeForMove) m_nAbsoluteAbortTime = m_nMaxTimeForMove;
214
215 if (m_nRemainingMovesForPeriod <= 1) {
216 if (m_nAbsoluteAbortTime > m_nTimeLeftForGame - TIME_LOSTINENGINE - TIME_LASTMOVEMARGIN)
217 m_nAbsoluteAbortTime = m_nTimeLeftForGame - TIME_LOSTINENGINE - TIME_LASTMOVEMARGIN;
218 } else {
219 if (m_nAbsoluteAbortTime > m_nTimeLeftForGame - TIME_LOSTINENGINE)
220 m_nAbsoluteAbortTime = m_nTimeLeftForGame - TIME_LOSTINENGINE;
221 }
222}
223
224// --------------------------------------------------------------------
225
227{
229 setMaxDepth(d);
230}
231
233{
234 m_nSearchDepth = d;
235 adjustQSDepth();
236}
237
238void bLevel::adjustQSDepth()
239{
241}
242
243// --------------------------------------------------------------------
244
246{
247 return d >= getSearchDepth();
248}
249
251{
252 return d >= getQSDepth();
253}
254
255/**
256 * Stop search required ?
257 * @param nTimeElapsed time elapsed until now
258 * @return true if need to stop search
259 */
260bool bLevel::stoppingSearch(long const nTimeElapsed)
261 const
262{
263 if ((m_oType == LevelType::TimeControl)
264 || (m_oType == LevelType::FicherTimeControl)
265 || (m_oType == LevelType::SecondsPerMove)
266 || (m_oType == LevelType::SuddenDeath)) {
267 if (nTimeElapsed > m_nTimeForMove) return true; /// stopping
268 if (nTimeElapsed > m_nAbsoluteAbortTime) return true; /// avoid getting flagged
269 }
270
271 return false;
272}
273
274/**
275 * Do we still have time to do another iteration ?
276 * @param d current depth searched
277 * @param nTimeElapsed time elapsed until now
278 * @return true if another iteration could be started
279 */
280bool bLevel::stillTimeLeft(depth_t const d, long const nTimeElapsed) const
281{
282 if (d < MINIMAL_DEPTH_COMPLETED) return true;
283
284 if ((m_oType == LevelType::TimeControl)
285 || (m_oType == LevelType::FicherTimeControl)
286 || (m_oType == LevelType::SuddenDeath)) {
287 if (nTimeElapsed > m_nEstAllowNextIterationTime) return false;
288 }
289
290 return true;
291}
292
293/**
294 * increase gradually the allowed time for move, first
295 * move to max time for move, then move to abort time
296 */
298{
299 if (m_nTimeForMove == m_nMaxTimeForMove) {
300 // second increase, also move next iteration
301 m_nTimeForMove = m_nAbsoluteAbortTime - TIME_LOSTINENGINE;
302 m_nEstAllowNextIterationTime = m_nAbsoluteAbortTime - TIME_LASTMOVEMARGIN;
303 } else {
304 m_nTimeForMove = m_nMaxTimeForMove;
305 }
306}
307
308// --------------------------------------------------------------------
309
310std::string bLevel::prettyDepth(depth_t const d) const
311{
312 if (d == INFINITE_DEPTH) return "∞";
313 if (d) return belofte::to_string(static_cast<int16_t>(d));
314 return "-";
315}
316
317std::string bLevel::prettyMoves(int const d) const
318{
319 if (d) return belofte::to_string(d);
320 return "∞";
321}
322
323// --------------------------------------------------------------------
324
325bLevel::operator std::string() const
326{
327 std::string s;
328 if (m_oType == LevelType::SuddenDeath) {
329 s = "SD " + belofte::prettyTime(m_timeForGame)
330 + " Left for game = " + belofte::prettyTime(m_nTimeLeftForGame)
331 + " for move est/max/abt "
332 + belofte::prettyTime(m_nTimeForMove)
333 + "/" + belofte::prettyTime(m_nMaxTimeForMove)
334 + "/" + belofte::prettyTime(m_nAbsoluteAbortTime);
335 } else if ((m_oType == LevelType::FicherTimeControl) && m_movesPerPeriod) {
336 s = "FC " + belofte::prettyTime(m_timeForGame)
337 + "/" + prettyMoves(m_movesPerPeriod)
338 + "+" + belofte::prettyTime(m_incrementPerMove)
339 + " Left for #" + prettyMoves(m_nRemainingMovesForPeriod)
340 + " moves = " + belofte::prettyTime(m_nTimeLeftForGame)
341 + " for move est/max/abt "
342 + belofte::prettyTime(m_nTimeForMove)
343 + "/" + belofte::prettyTime(m_nMaxTimeForMove)
344 + "/" + belofte::prettyTime(m_nAbsoluteAbortTime);
345 } else if ((m_oType == LevelType::FicherTimeControl) && !m_movesPerPeriod) {
346 s = "FC " + belofte::prettyTime(m_timeForGame)
347 + "+" + belofte::prettyTime(m_incrementPerMove)
348 + " Left for game = " + belofte::prettyTime(m_nTimeLeftForGame)
349 + " for move est/max/abt "
350 + belofte::prettyTime(m_nTimeForMove)
351 + "/" + belofte::prettyTime(m_nMaxTimeForMove)
352 + "/" + belofte::prettyTime(m_nAbsoluteAbortTime);
353 } else if (m_oType == LevelType::TimeControl) {
354 s = "TC " + belofte::prettyTime(m_timeForGame)
355 + "/" + prettyMoves(m_movesPerPeriod)
356 + " Left for #" + prettyMoves(m_nRemainingMovesForPeriod)
357 + " moves = " + belofte::prettyTime(m_nTimeLeftForGame)
358 + " for move est/max/abt "
359 + belofte::prettyTime(m_nTimeForMove)
360 + "/" + belofte::prettyTime(m_nMaxTimeForMove)
361 + "/" + belofte::prettyTime(m_nAbsoluteAbortTime);
362 } else if (m_oType == LevelType::SecondsPerMove) {
363 s = "ST " + belofte::prettyTime(m_nTimeLeftForGame)
364 + " Time left for move = " + belofte::prettyTime(m_nTimeForMove);
365 } else if (m_oType == LevelType::MateSearch) {
366 s = "Search mate in " + belofte::to_string(static_cast<int16_t>(getSearchDepth()));
367 } else if (m_oType == LevelType::Infinite) {
368 s = "∞ search";
369 } else if (m_oType == LevelType::Unset) {
370 s = "No level set";
371 } else {
372 s = "To be implemented";
373 }
374
375 if (m_oType != LevelType::MateSearch) {
376 if (getMaxDepth() != INFINITE_DEPTH) {
377 s += ", depth limited to " + belofte::to_string(static_cast<int16_t>(getMaxDepth()));
378 }
379 }
380
381 return s;
382}
383
384// eof
This is the main include file, needs to be included before any other include.
int_fast8_t depth_t
Definition belofte.h:103
bool searchDepthReached(depth_t const d) const
Definition level.cpp:245
void setGameTime(int const msPerGame)
Definition level.cpp:23
constexpr depth_t getSearchDepth() const
Definition level.h:98
void setMoreTimeRequired()
increase gradually the allowed time for move, first move to max time for move, then move to abort tim...
Definition level.cpp:297
void setQSDepth(depth_t const d)
Definition level.h:100
void setInfinite()
Definition level.cpp:12
void setRemainingTime(int const msPerGame)
xboard issues time command to update available time
Definition level.cpp:133
void setMovePlayed()
Definition level.cpp:143
void setMoveTime(int const msPerMove)
Definition level.cpp:59
void setSearchDepth(depth_t const d)
Definition level.cpp:232
constexpr bool hasLevelChanged() const
Definition level.h:66
void setMateSearch(depth_t const d)
Definition level.cpp:119
constexpr depth_t getQSDepth() const
Definition level.h:102
bool qsDepthReached(depth_t const d) const
Definition level.cpp:250
bool stoppingSearch(long const nTimeElapsed) const
Stop search required ?
Definition level.cpp:260
void clearLevelChanged()
new level or new game
Definition level.h:70
void setMaxDepth(depth_t const d)
Definition level.h:104
bool stillTimeLeft(depth_t const d, long const nTimeElapsed) const
Do we still have time to do another iteration ?
Definition level.cpp:280
void setDepthCommand(depth_t const d)
Definition level.cpp:226
void undoMovePlayed()
used for recalibrating time in case for undo move in xboard moves per period
Definition level.cpp:156
constexpr depth_t getMaxDepth() const
Definition level.h:106
constexpr auto TIME_OVERFLOWMULTIPLYER
Definition level.h:15
constexpr auto TIME_UNDERFLOWDEVIDER
Definition level.h:20
constexpr depth_t QS_DEPTHEXTENSION
Definition level.h:13
constexpr auto TIME_UNDERFLOWMULTIPLYER
Definition level.h:17
constexpr auto MINIMAL_DEPTH_COMPLETED
Definition level.h:14
constexpr auto TIME_LOSTINENGINE
time lost in between UI go command and bestmove return
Definition level.h:24
constexpr depth_t DEFAULT_DEPTH
Definition level.h:12
constexpr depth_t INFINITE_DEPTH
Definition level.h:11
@ TimeControl
Definition level.h:46
@ SuddenDeath
Definition level.h:46
@ SecondsPerMove
Definition level.h:45
@ FicherTimeControl
Definition level.h:46
@ MateSearch
Definition level.h:46
@ Infinite
Definition level.h:46
@ Unset
Definition level.h:45
constexpr auto TIME_LASTMOVEMARGIN
Definition level.h:25
std::string to_string(int16_t value)
std::to_string not compatible on Mac OS (Apple LLVM version 5.0) provide generic utility function
Definition util.cpp:171
std::string prettyTime(long const t)
Definition util.cpp:289