Belofte version 2.2.0
A promising chess program using the UCI or Winboard interface
search.cpp
Go to the documentation of this file.
1/*---------------------------------------------------------------------+
2 * File: search.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
12/**
13 * Generic search, will call (non-)recursive method per algorithm
14 * only when there are moves to be played
15 * @param b initial board
16 * @param ml movelist of this position
17 */
19{
21
22 /// @todo Add debug manipulator
23 std::stringstream ss;
24 ss << "Searching started: " << this->operator std::string() << " "
25 << getLevel()->operator std::string()
26 << (b.whiteToMove() ? " For white" : " For Black");
27 m_appei->sendInfoSearchStart(ss.str());
28
30 b.setPreviousMoves({});
31 b.calcMinorPieces(true);
32 b.calcGameStage();
33
34 m_leafnodes = 0LL;
35 m_nonleafnodes = 0LL;
37
39 if (gr != GR_UNKNOWN && isNoBench()) {
40 StopSearch();
41 m_appei->sendInfo("engine should not have started searching as position is final");
42 m_appei->sendResult(b, gr);
43 throw gameEndException();
44 }
45
46 if (ml.atLeastOneMovePossible(b)) {
48 && (getLevel()->getType() != LevelType::MateSearch)) {
49 SearchBestMoveIterative(b, ml);
50 } else {
51 try {
52 CalcBestMove(b, ml);
53 } catch (const searchAbortedException&) {
54 sendInfoSearching(b, 0, "abort non iterative search");
55 } catch (...) { throw; // push other exceptions down
56 }
57 }
58 StopSearch();
59 } else {
60 StopSearch();
61 if (isNoBench())
62 m_appei->sendError("no move found", getDuration());
63 if (!m_iterativesearch) return;
64
66 }
67 dumpMoveList(ml, 0);
68}
69
70//-----------------------------------------------------------------------
71
72void bSearchAlgorithm::SearchBestMoveIterative(bBoard& b, bMoveList& ml)
73{
74 depth_t iCurrentDepth = 1;
75 bScore score;
76
77 if (ml.generateMoves(b)) {
78 // in case we break out of search early, take first move
79 // as back-up move
80 ml.setBestMoveId(1);
81 // we sort moves so best move candidate is searched first
82 ml.sortMoves(false);
83 }
84
85 movenum_t n_moves = ml.getNumberOfMoves();
86 movenum_t n_QSmoves = ml.getNumberOfQSMoves();
87
88 std::string sPreviousBestMove;
89 std::string sNewBestMove;
90
91 while (iCurrentDepth <= getLevel()->getMaxDepth()) {
92 getLevel()->setSearchDepth(iCurrentDepth);
93
94 std::string sDepthLBL = " (depth " + belofte::to_string(iCurrentDepth) + ")";
95 m_appei->sendInfo("ItSearch started at" + sDepthLBL);
96
97 // clear search variation of previous searches in current position
99
100 try {
101 score = CalcBestMove(b, ml);
102 {
103 int64_t nNodes = m_leafnodes + m_nonleafnodes;
104 #if !defined(NO_NPS_LOG)
105 int64_t nDuration = getDurationMicroSec();
106 if (nDuration < 1) nDuration = 1;
107 long nNPS = nNodes * 1000000 / nDuration;
108 m_appei->sendInfoDepth(iCurrentDepth, m_maxDepth, nNodes, static_cast<int>(nNPS));
109 #else
110 m_appei->sendInfoDepth(iCurrentDepth, m_maxDepth, nNodes, 0);
111 #endif
112 }
113 // retrieve bestmove, note in case of abort, b has been restored
114 // so take it from ml if possible
115 if (!b.getVariation().empty()) {
116 sNewBestMove = b.getVariation()[0];
117 if (sNewBestMove == sPreviousBestMove) {
118 m_appei->sendInfo("ItSearch Best move unchanged: " + sNewBestMove + sDepthLBL);
119 } else if (sPreviousBestMove.empty()) {
120 m_appei->sendInfo("ItSearch Best move found: " + sNewBestMove + sDepthLBL);
121 } else {
122 m_appei->sendInfo("ItSearch Best move updated: " + sNewBestMove + sDepthLBL);
123 }
124 }
125 bCoordMove cm(ml[ml.getBestMoveId()]);
126 if (!sNewBestMove.empty() && sNewBestMove != static_cast<std::string>(cm)) {
127 m_appei->sendInfo("ItSearch Inconsistency with PV: " + static_cast<std::string>(cm) + " vs " + sNewBestMove + sDepthLBL);
128 }
129 } catch (const searchAbortedException&) {
130 if (iCurrentDepth < 2) {
131 /// @todo this is a special case, abort even without doing a good iteration
132 sendInfoSearching(b, 0, "ItSearch early abort search" + sDepthLBL);
133 }
134 if ((ml.curmoveid > 2) && (n_moves > 1)) {
135 // the movelist has been handling at least one two moves completely at new depth
136 // and there is at least 2 moves
137 bCoordMove cm(ml[ml.getBestMoveId()]);
138 if (sPreviousBestMove != static_cast<std::string>(cm)) {
139 sendInfoSearching(b, 0, "ItSearch partial search best move update: " + static_cast<std::string>(cm) + sDepthLBL);
140 } else {
141 score = ml.getBestMoveScore();
142 sendInfoSearching(b, 0, "ItSearch abort partial search" + sDepthLBL, score);
143 }
144 } else if (ml.curmoveid > 1) {
145 // only one move has been searched, it could be worse than
146 // the result at previous depth
147 /// @todo get score of second best move and compare it
148 bCoordMove cm(ml[ml.getBestMoveId()]);
149 if (sPreviousBestMove != static_cast<std::string>(cm)) {
150 sendInfoSearching(b, 0, "ItSearch initial search best move update: " + static_cast<std::string>(cm) + sDepthLBL);
151 } else {
152 sendInfoSearching(b, 0, "ItSearch abort initial search" + sDepthLBL);
153 }
154 } else {
155 // abort during search of first move
156 sendInfoSearching(b, 0, "ItSearch abort search at" + sDepthLBL);
157 }
158 break;
159 } catch (...) { throw; // push other exceptions down
160 }
161
162 // quit loop in case of absolute score
163 // realscore is converting forced draw to 0
165 sendInfoSearching(b, 0, "ItSearch mating opponent" + sDepthLBL, score);
166 break;
167 }
168 // quit loop in case of getting mated
170 sendInfoSearching(b, 0, "ItSearch getting mated" + sDepthLBL, score);
171 break;
172 }
173 // quit loop in case there is only one move
174 if (n_moves == 1) {
175 sendInfoSearching(b, 0, "ItSearch there is only one move" + sDepthLBL, score);
176 break;
177 }
178
179 if (!sPreviousBestMove.empty()
180 && (sNewBestMove != sPreviousBestMove)) {
181 // there is an update to the bestmove found in this iteration
182 // so increase time for allowing a further iteration
184 m_appei->sendInfo("ItSearch increase time for search");
185 }
186
187 // quit loop if not enough time left for another iteration
188 if (!getLevel()->stillTimeLeft(iCurrentDepth, getDurationMilliSec(), n_QSmoves, n_moves)) {
189 sendInfoSearching(b, 0, "ItSearch not enough time for more depth" + sDepthLBL + " done", score);
190 break;
191 } else if (sPreviousBestMove.empty() && !sNewBestMove.empty()) {
192 sendInfoSearching(b, 0, "ItSearch bestmove set" + sDepthLBL + " done", score);
193 sPreviousBestMove = sNewBestMove;
194 } else if (sNewBestMove != sPreviousBestMove) {
195 sendInfoSearching(b, 0, "ItSearch bestmove updated" + sDepthLBL + " done", score);
196 sPreviousBestMove = sNewBestMove;
197 } else {
198 sendInfoSearching(b, 0, "ItSearch searching ended at" + sDepthLBL + " done", score);
199 }
200
201 if (iCurrentDepth < getLevel()->getMaxDepth()) {
202 dumpMoveList(ml, iCurrentDepth);
203 ml.sortMoves(false);
204 }
205 ++iCurrentDepth;
206 }
207}
208
209//-----------------------------------------------------------------------
210
212{
213 m_aborting = false;
214 Game()->setSearching(true);
215 ClockStart();
216 m_minimizing = sc;
217 m_postlevel = App().sout.getLevel(); // cache value
218 setLevel(&(Game()->getLevel())); // copy level from game-level to search
219 m_appei = AppEI();
220 m_evalptr = Game()->getEval();
221}
222
224{
225 /// @todo: limit calls to getDurationMilliSec
226 if (m_aborting
227 || m_levelptr->stoppingSearch(getDurationMilliSec()))
229}
230
232{
233 m_aborting = false;
234 ClockEnd();
235 Game()->setSearching(false);
236}
237
238//-----------------------------------------------------------------------
239
240void bSearchAlgorithm::dumpMoveList(bMoveList const& ml, depth_t const iDepth) const
241{
242 std::stringstream ss;
243 if (iDepth) ss << "depth " << belofte::to_string(iDepth) << " ";
244 ss << "ml " << ml;
245 m_appei->sendInfo(ss.str());
246}
247
248//-----------------------------------------------------------------------
249
251 depth_t const nDepth, std::string const& comment) const
252{
253 if (nDepth <= m_postlevel) { // speedup
254 if (comment != "") {
255 m_appei->sendInfoSearching(b, nDepth, m_maxDepth, comment, SCORE_UNDEFINED,
257 } else {
258 m_appei->sendInfoSearching(b, nDepth, m_maxDepth, SCORE_UNDEFINED,
260 }
261 }
262}
263
265 depth_t const nDepth, std::string const& comment,
266 bScore const sc) const
267{
268 if (nDepth <= m_postlevel) { // speedup
269 if (comment != "") {
270 m_appei->sendInfoSearching(b, nDepth, m_maxDepth,
271 comment, sc * m_minimizing * b.minimizing(), getDurationMilliSec(),
273 } else {
274 m_appei->sendInfoSearching(b, nDepth, m_maxDepth,
275 sc * m_minimizing * b.minimizing(), getDurationMilliSec(),
277 }
278 }
279 return sc;
280}
281
283 depth_t const nCurDepth,
284 bMove const& m,
285 movenum_t const moveid) const
286{
287 if (nCurDepth <= m_postlevel) {
288 m_appei->sendInfoCurrMove(b, nCurDepth,
289 m, moveid, m_leafnodes + m_nonleafnodes);
290 }
291}
292
293//-----------------------------------------------------------------------
294
295/**
296 * Get score of board, eventually from cache
297 * @param b board
298 * @param gr previous gameresult
299 * @param bRecalcFirst recalculate pieces first
300 * @return score of board, either from cache, from gr or caclulated
301 */
303 gameResult_t gr, bool const bRecalcFirst) const
304{
305 if (gr == GR_UNSET) {
307 }
308 if (gr != GR_UNKNOWN) {
309 // gr is a forced end
311 }
312 if (bRecalcFirst) b.calcMinorPieces(false);
313 return m_evalptr->getEvaluation(b, gr) * b.minimizing();
314}
315
316/**
317 * Get score of board, eventually from cache, from whites view
318 * @param b board
319 * @param gr previous gameresult
320 * @param bRecalcFirst recalculate pieces first
321 * @return score of board, either from cache, from gr or caclulated
322 */
324 gameResult_t gr, bool const bRecalcFirst) const
325{
326 if (gr == GR_UNSET) {
328 }
329 if (gr != GR_UNKNOWN) {
330 // gr is a forced end
332 }
333 if (bRecalcFirst) b.calcMinorPieces(false);
334 return m_evalptr->getEvaluation(b, gr);
335}
336
337//-----------------------------------------------------------------------
338
339// eof
appInstance & App()
Definition belofte.cpp:242
engineInterface * AppEI()
Definition belofte.cpp:248
bGame * Game()
Definition belofte.cpp:253
This is the main include file, needs to be included before any other include.
uint_fast8_t movenum_t
Definition belofte.h:103
int_fast8_t depth_t
Definition belofte.h:106
void ClockEnd()
Definition util.cpp:37
std::string getDuration() const
Definition util.cpp:47
long long getDurationMilliSec() const
Definition util.cpp:83
void ClockStart()
Definition util.cpp:27
long long getDurationMicroSec() const
Definition util.cpp:61
outputWriter sout
normal output
Definition belofte.h:179
constexpr bool whiteToMove() const
Definition basicboard.h:162
constexpr bScore minimizing() const
Definition basicboard.h:172
board
Definition board.h:45
void clearVariation()
Definition board.h:100
void calcMinorPieces(bool const bForceRecalc)
Recalculate minor pieces, used for evaluation and end of game condition in case of less than 5 pieces...
Definition board.cpp:66
movesequence_t const & getVariation() const
Definition board.h:98
void calcGameStage()
calculate stage of game to assist in evaluation
Definition board.cpp:137
void setPreviousMoves(movesequence_t const &v)
Definition board.h:96
bPositionEvaluation * getEval() const
void setSearching(bool const l)
Definition game.h:80
void setMoreTimeRequired()
increase gradually the allowed time for move, first move to max time for move, then move to abort tim...
Definition level.cpp:313
void setSearchDepth(depth_t const d)
Definition level.cpp:232
Definition move.h:13
movenum_t generateMoves(bBasicBoard const &b)
generate moves if not yet generated
Definition movelist.cpp:417
constexpr bScore getBestMoveScore() const
Definition movelist.h:78
constexpr movenum_t getBestMoveId() const
Definition movelist.h:87
constexpr movenum_t getNumberOfMoves() const
Definition movelist.h:49
bool atLeastOneMovePossible(bBasicBoard &b)
see if at least one move can be played e.g.
Definition movelist.cpp:481
movenum_t curmoveid
Definition movelist.h:104
constexpr movenum_t getNumberOfQSMoves() const
Definition movelist.h:51
void setBestMoveId(movenum_t const n)
Definition movelist.h:89
void sortMoves(bool const bFastSort)
sort moves and update bestmove id if less than 5 moves, sort all if more than 5 moves,...
Definition movelist.cpp:369
static bScore resultToScoreFlag(gameResult_t const gr)
Class static function convert all draw scores to SCORE_THEORETIC_DRAW.
Definition eval.cpp:74
static gameResult_t gameEndedResult(bBoard const &b)
Class static function See if board is in finite state, meaning game is ended.
Definition eval.cpp:42
void StartSearch(bScore const sc)
Definition search.cpp:211
void sendInfoCurrMove(bBoard const &b, depth_t const nCurDepth, bMove const &m, movenum_t const moveid) const
Definition search.cpp:282
void StopSearch()
Definition search.cpp:231
bool m_iterativesearch
Definition search.h:146
bScore RetrieveBoardEvaluationForWhite(bBoard &b, gameResult_t const gr, bool const bRecalcFirst) const
Get score of board, eventually from cache, from whites view.
Definition search.cpp:323
void initMaxSearchedDepth()
Definition search.h:156
constexpr bool isNoBench() const
Definition search.h:132
void SearchBestMove(bBoard &b, bMoveList &ml)
Generic search, will call (non-)recursive method per algorithm only when there are moves to be played...
Definition search.cpp:18
bLevel * getLevel()
Definition search.h:161
int64_t m_nonleafnodes
Definition search.h:145
int64_t m_leafnodes
Definition search.h:144
bScore RetrieveBoardEvaluation(bBoard &b, gameResult_t const gr, bool const bRecalcFirst) const
Get score of board, eventually from cache.
Definition search.cpp:302
void setLevel(bLevel *l)
Definition search.h:159
void CheckIfAbortingSearch() const
Definition search.cpp:223
void sendInfoSearching(bBoard const &b, depth_t const nDepth, std::string const &comment) const
Definition search.cpp:250
virtual bScore CalcBestMove(bBoard &b, bMoveList &ml)=0
constexpr bScore realScore() const
Definition searchscore.h:66
virtual void sendInfo(std::string const &info)
int getLevel() const
@ GR_UNSET
Definition eval.h:32
@ GR_UNKNOWN
Definition eval.h:33
enum gameResult gameResult_t
Definition eval.h:40
int16_t bScore
Definition eval.h:11
constexpr bScore SCORE_UNDEFINED
Definition eval.h:21
constexpr bScore SCORE_INFINITE
Definition eval.h:20
@ MateSearch
Definition level.h:46
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:186