Belofte version 2.1.8
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 : m_oType{LevelType::Unset}
14 , m_nSearchDepth{DEFAULT_DEPTH}
16 , m_nMaxDepth{INFINITE_DEPTH}
17{
19}
20
24
25// --------------------------------------------------------------------
26
28{
29 m_levelchanged = true;
30}
31
33{
34 m_oType = LevelType::Infinite;
35
36 m_nSearchDepth = 1;
37 m_nQSDepth = INFINITE_DEPTH;
38 m_nMaxDepth = INFINITE_DEPTH;
39
40 recalibrateTime();
41}
42
43void bLevel::setGameTime(int const msPerGame)
44{
45 m_oType = LevelType::SuddenDeath;
46 if (m_levelchanged) {
47 m_timeForGame = msPerGame;
48 m_levelchanged = false;
49 }
50
51 m_movesPerPeriod = 0;
52 m_incrementPerMove = 0;
53 m_nTimeLeftForGame = msPerGame;
54
55 m_nSearchDepth = DEFAULT_DEPTH;
57 m_nMaxDepth = INFINITE_DEPTH;
58
59 recalibrateTime();
60}
61
62void bLevel::setGameTime(int const msPerGame, int const msIncrementPerMove)
63{
65 if (m_levelchanged) {
66 m_timeForGame = msPerGame;
67 m_levelchanged = false;
68 }
69 m_movesPerPeriod = 0;
70 m_incrementPerMove = msIncrementPerMove;
71
72 m_nTimeLeftForGame = msPerGame;
73
74 m_nSearchDepth = DEFAULT_DEPTH;
76 m_nMaxDepth = INFINITE_DEPTH;
77
78 recalibrateTime();
79}
80
81void bLevel::setMoveTime(int const msPerMove)
82{
84 //if (m_levelchanged) {
85 m_levelchanged = false;
86 //}
87 m_movesPerPeriod = 0;
88
89 m_nTimeForMove = msPerMove;
90
91 m_nSearchDepth = DEFAULT_DEPTH;
93 m_nMaxDepth = INFINITE_DEPTH;
94
95 // pre-calibration
96 m_nTimeForMove -= TIME_LOSTINENGINE;
97 m_nMaxTimeForMove = m_nTimeForMove;
98 m_nTimeLeftForGame = m_nMaxTimeForMove + TIME_LOSTINENGINE;
99
100 recalibrateTime();
101}
102
103void bLevel::setMoveTime(int const msPerGame, int const nMoves)
104{
105 m_oType = LevelType::TimeControl;
106 if (m_levelchanged) {
107 m_timeForGame = msPerGame;
108 m_movesPerPeriod = nMoves;
109 m_levelchanged = false;
110 }
111
112 m_incrementPerMove = 0;
113 m_nRemainingMovesForPeriod = nMoves;
114 m_nTimeLeftForGame = msPerGame;
115
116 m_nSearchDepth = DEFAULT_DEPTH;
118 m_nMaxDepth = INFINITE_DEPTH;
119
120 recalibrateTime();
121}
122
123void bLevel::setMoveTime(int const msPerGame,
124 int const nMoves, int const msIncrementPerMove)
125{
127 if (m_levelchanged) {
128 m_movesPerPeriod = nMoves;
129 m_timeForGame = msPerGame;
130 m_levelchanged = false;
131 }
132 m_incrementPerMove = msIncrementPerMove;
133
134 m_nRemainingMovesForPeriod = nMoves;
135 m_nTimeLeftForGame = msPerGame;
136
137 m_nSearchDepth = DEFAULT_DEPTH;
139 m_nMaxDepth = INFINITE_DEPTH;
140
141 recalibrateTime();
142}
143
145{
146 m_oType = LevelType::MateSearch;
147
148 m_nSearchDepth = d;
149 m_nQSDepth = d;
150 m_nMaxDepth = d;
151
152 recalibrateTime();
153}
154
155/** xboard issues time command to update available time
156 */
157void bLevel::setRemainingTime(const int msPerGame)
158{
159 m_nTimeLeftForGame = msPerGame;
160
161 int nInc = m_incrementPerMove;
162 m_incrementPerMove = 0; // UCI adds time per move before move, so put to 0
163 recalibrateTime();
164 m_incrementPerMove = nInc;
165}
166
168{
169 m_nRemainingMovesForPeriod--;
170 if (m_nRemainingMovesForPeriod < 1)
171 m_nRemainingMovesForPeriod = m_movesPerPeriod;
172
173 recalibrateTime();
174}
175
176/** used for recalibrating time in case for undo move in
177 * xboard moves per period
178 */
180{
181 m_nRemainingMovesForPeriod++;
182
183 recalibrateTime();
184}
185
186void bLevel::recalibrateTime()
187{
188 // TODO: more intelligent number of moves left
189 // 35 at game start with 32 pieces on board, 20 in endgame
190 if (m_oType == LevelType::SuddenDeath) {
191 m_nTimeForMove = m_nTimeLeftForGame / 20;
192 m_nMaxTimeForMove = m_nTimeLeftForGame / 15;
193 } else if ((m_oType == LevelType::FicherTimeControl) && m_movesPerPeriod) {
194 // avoid divide by zero (TODO consider catching error)
195 // TOCHECK code should be avoidable
196 if (!m_nRemainingMovesForPeriod) m_nRemainingMovesForPeriod = 1;
197 if (m_nRemainingMovesForPeriod <= 4) {
198 m_nTimeForMove = m_nTimeLeftForGame / m_nRemainingMovesForPeriod;
199 m_nMaxTimeForMove = m_nTimeForMove;
200 } else {
201 m_nTimeForMove = (m_nTimeLeftForGame / m_nRemainingMovesForPeriod) + m_incrementPerMove;
202 m_nMaxTimeForMove = (m_nTimeLeftForGame / (m_nRemainingMovesForPeriod - 3)) + m_incrementPerMove;
203 }
204 } else if ((m_oType == LevelType::FicherTimeControl) && !m_movesPerPeriod) {
205 if (m_nTimeLeftForGame > m_incrementPerMove * 5) {
206 m_nTimeForMove = (m_nTimeLeftForGame / 20) + m_incrementPerMove;
207 m_nMaxTimeForMove = (m_nTimeLeftForGame / 10) + m_incrementPerMove;
208 } else {
209 m_nTimeForMove = m_nTimeLeftForGame / 20;
210 m_nMaxTimeForMove = m_nTimeLeftForGame / 10;
211 }
212 } else if ((m_oType == LevelType::TimeControl) && m_movesPerPeriod) {
213 // avoid divide by zero (TODO consider catching error)
214 // TOCHECK code should be avoidable
215 if (!m_nRemainingMovesForPeriod) m_nRemainingMovesForPeriod = 1;
216 if (m_nRemainingMovesForPeriod > 10) {
217 m_nTimeForMove = m_nTimeLeftForGame / m_nRemainingMovesForPeriod;
218 m_nMaxTimeForMove = m_nTimeLeftForGame / (m_nRemainingMovesForPeriod - 5);
219 } else {
220 m_nTimeForMove = m_nTimeLeftForGame / m_nRemainingMovesForPeriod;
221 m_nMaxTimeForMove = m_nTimeForMove;
222 }
223 }
224
225 // make sure that position is not lost because of overhead outside search
226 if (m_nMaxTimeForMove > m_nTimeLeftForGame - TIME_LOSTINENGINE)
227 m_nMaxTimeForMove = m_nTimeLeftForGame - TIME_LOSTINENGINE;
228
229 // temporarily adjust
230 m_nTimeForMove = (m_nTimeForMove + m_nMaxTimeForMove) / 2;
231
232 if (m_nTimeForMove > m_nMaxTimeForMove)
233 m_nTimeForMove = m_nMaxTimeForMove;
234
235 m_nEstAllowNextIterationTime = TIME_UNDERFLOWMULTIPLYER
236 * m_nTimeForMove / TIME_UNDERFLOWDEVIDER;
237
238 m_nAbsoluteAbortTime = m_nTimeForMove * TIME_OVERFLOWMULTIPLYER;
239 if (m_nRemainingMovesForPeriod < 1) {
240 if (m_nAbsoluteAbortTime > m_nTimeLeftForGame - TIME_LOSTINENGINE - TIME_LASTMOVEMARGIN)
241 m_nAbsoluteAbortTime = m_nTimeLeftForGame - TIME_LOSTINENGINE - TIME_LASTMOVEMARGIN;
242 } else {
243 if (m_nAbsoluteAbortTime > m_nTimeLeftForGame - TIME_LOSTINENGINE)
244 m_nAbsoluteAbortTime = m_nTimeLeftForGame - TIME_LOSTINENGINE;
245 }
246}
247
248// --------------------------------------------------------------------
249
251{
252 m_nSearchDepth = d;
253 m_nQSDepth = d * MAXDEPTH_MULTIPLYER + QS_DEPTHEXTENSION;
256 m_nMaxDepth = d;
257}
258
260{
261 m_nSearchDepth = d;
262}
263
265{
266 m_nMaxDepth = d;
267}
268
269void bLevel::setNodes(int64_t d)
270{
271 m_nNodes = d;
272}
273
275{
276 return m_oType;
277}
278
280{
281 return m_nSearchDepth;
282}
283
285{
286 return m_nMaxDepth;
287}
288
290{
291 m_pondering = true;
292}
293
295{
296 m_pondering = false;
297}
298
300{
301 return m_pondering;
302}
303
304// --------------------------------------------------------------------
305
307{
308 return d >= m_nSearchDepth;
309}
310
312{
313 return d >= m_nQSDepth;
314}
315
316/**
317 * Stop search required ?
318 * @param nTimeElapsed time elapsed until now
319 * @return true if need to stop search
320 */
321bool bLevel::stoppingSearch(long const nTimeElapsed)
322 const
323{
324 if ((m_oType == LevelType::TimeControl)
325 || (m_oType == LevelType::FicherTimeControl)
326 || (m_oType == LevelType::SecondsPerMove)
327 || (m_oType == LevelType::SuddenDeath)) {
328 if (nTimeElapsed > m_nTimeForMove) return true; /// stopping
329 if (nTimeElapsed > m_nMaxTimeForMove) return true; /// aborting
330 if (nTimeElapsed > m_nAbsoluteAbortTime) return true; /// avoid getting flagged
331 }
332
333 return false;
334}
335
336/**
337 * Do we still have time to do another iteration ?
338 * @param d current depth searched
339 * @param nTimeElapsed time elapsed until now
340 * @return true if another iteration could be started
341 */
342bool bLevel::stillTimeLeft(depth_t const d, long const nTimeElapsed) const
343{
344 if (d < MINIMAL_DEPTH_COMPLETED) return true;
345
346 if ((m_oType == LevelType::TimeControl)
347 || (m_oType == LevelType::FicherTimeControl)
348 || (m_oType == LevelType::SuddenDeath)) {
349 if (nTimeElapsed > m_nEstAllowNextIterationTime) return false;
350 }
351
352 return true;
353}
354
355// --------------------------------------------------------------------
356
357std::string bLevel::prettyDepth(depth_t const d) const
358{
359 if (d == INFINITE_DEPTH) return "∞";
360 if (d) return belofte::to_string(static_cast<int>(d));
361 return "-";
362}
363
364std::string bLevel::prettyMoves(int const d) const
365{
366 if (d) return belofte::to_string(d);
367 return "∞";
368}
369
370// --------------------------------------------------------------------
371
372bLevel::operator std::string() const
373{
374 std::string s;
375 if (m_oType == LevelType::SuddenDeath) {
376 s = "SD " + belofte::prettyTime(m_timeForGame)
377 + " Left for game = " + belofte::prettyTime(m_nTimeLeftForGame)
378 + " for move est/max "
379 + belofte::prettyTime(m_nTimeForMove)
380 + "/" + belofte::prettyTime(m_nAbsoluteAbortTime);
381 } else if ((m_oType == LevelType::FicherTimeControl) && m_movesPerPeriod) {
382 s = "FC " + belofte::prettyTime(m_timeForGame)
383 + "/" + prettyMoves(m_movesPerPeriod)
384 + "+" + belofte::prettyTime(m_incrementPerMove)
385 + " Left for #" + prettyMoves(m_nRemainingMovesForPeriod)
386 + " moves = " + belofte::prettyTime(m_nTimeLeftForGame)
387 + " for move est/max "
388 + belofte::prettyTime(m_nTimeForMove)
389 + "/" + belofte::prettyTime(m_nAbsoluteAbortTime);
390 } else if ((m_oType == LevelType::FicherTimeControl) && !m_movesPerPeriod) {
391 s = "FC " + belofte::prettyTime(m_timeForGame)
392 + "+" + belofte::prettyTime(m_incrementPerMove)
393 + " Left for game = " + belofte::prettyTime(m_nTimeLeftForGame)
394 + " for move est/max "
395 + belofte::prettyTime(m_nTimeForMove)
396 + "/" + belofte::prettyTime(m_nAbsoluteAbortTime);
397 } else if (m_oType == LevelType::TimeControl) {
398 s = "TC " + belofte::prettyTime(m_timeForGame)
399 + "/" + prettyMoves(m_movesPerPeriod)
400 + " Left for #" + prettyMoves(m_nRemainingMovesForPeriod)
401 + " moves = " + belofte::prettyTime(m_nTimeLeftForGame)
402 + " for move est/max "
403 + belofte::prettyTime(m_nTimeForMove)
404 + "/" + belofte::prettyTime(m_nAbsoluteAbortTime);
405 } else if (m_oType == LevelType::SecondsPerMove) {
406 s = "ST " + belofte::prettyTime(m_nTimeLeftForGame)
407 + " Time left for move = " + belofte::prettyTime(m_nTimeForMove);
408 } else if (m_oType == LevelType::MateSearch) {
409 s = "Search mate in " + belofte::to_string(static_cast<int>(m_nSearchDepth));
410 } else if (m_oType == LevelType::Infinite) {
411 s = "∞ search";
412 } else if (m_oType == LevelType::Unset) {
413 s = "No level set";
414 } else {
415 s = "To be implemented";
416 }
417
418 if (m_oType != LevelType::MateSearch) {
419 if (m_nMaxDepth != INFINITE_DEPTH) {
420 s += ", depth limited to " + belofte::to_string(static_cast<int>(m_nMaxDepth));
421 }
422 }
423
424 return s;
425}
426
427// eof
This is the main include file, needs to be included before any other include.
int_fast8_t depth_t
Definition belofte.h:112
bool searchDepthReached(depth_t const d) const
Definition level.cpp:306
void clearPondering()
Definition level.cpp:294
void flagLevelChanged()
Definition level.cpp:27
depth_t getSearchDepth() const
Definition level.cpp:279
void setGameTime(int const msPerGame)
new level or new game
Definition level.cpp:43
void setInfinite()
Definition level.cpp:32
void setRemainingTime(int const msPerGame)
xboard issues time command to update available time
Definition level.cpp:157
void setMovePlayed()
Definition level.cpp:167
void setMoveTime(int const msPerMove)
Definition level.cpp:81
void setSearchDepth(depth_t const d)
Definition level.cpp:259
bLevel()
Definition level.cpp:12
void setMateSearch(depth_t const d)
Definition level.cpp:144
void setNodes(int64_t n)
Definition level.cpp:269
bool isPondering() const
Definition level.cpp:299
~bLevel()
Definition level.cpp:21
bool qsDepthReached(depth_t const d) const
Definition level.cpp:311
LevelType getType() const
Definition level.cpp:274
bool stoppingSearch(long const nTimeElapsed) const
Stop search required ?
Definition level.cpp:321
void setPondering()
Definition level.cpp:289
depth_t getMaxDepth() const
Definition level.cpp:284
void setMaxDepth(depth_t const d)
Definition level.cpp:264
bool stillTimeLeft(depth_t const d, long const nTimeElapsed) const
Do we still have time to do another iteration ?
Definition level.cpp:342
void setDepthCommand(depth_t const d)
Definition level.cpp:250
void undoMovePlayed()
used for recalibrating time in case for undo move in xboard moves per period
Definition level.cpp:179
constexpr auto TIME_OVERFLOWMULTIPLYER
Definition level.h:16
constexpr auto TIME_UNDERFLOWDEVIDER
Definition level.h:21
constexpr depth_t QS_DEPTHEXTENSION
Definition level.h:14
constexpr auto TIME_UNDERFLOWMULTIPLYER
Definition level.h:18
constexpr auto MINIMAL_DEPTH_COMPLETED
Definition level.h:15
constexpr auto TIME_LOSTINENGINE
time lost in between UI go command and bestmove return
Definition level.h:25
constexpr depth_t DEFAULT_DEPTH
Definition level.h:12
constexpr depth_t MAXDEPTH_MULTIPLYER
Definition level.h:13
constexpr depth_t INFINITE_DEPTH
Definition level.h:11
LevelType
Implements clock n seconds per game - setGameTime(seconds * 1000) n seconds per game,...
Definition level.h:46
@ FicherTimeControl
constexpr auto TIME_LASTMOVEMARGIN
Definition level.h:26