Belofte version 2.1.8
A promising chess program using the UCI or Winboard interface
search_ab.cpp
Go to the documentation of this file.
1/*---------------------------------------------------------------------+
2 * File: search_ab.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#if defined(BELOFTE_NOUNICODE)
13#define ALPHABETA "alpha-beta"
14#else
15#define ALPHABETA "𝛼𝛽"
16#endif
17
18#if defined(_DEBUG)
19#if defined(BELOFTE_NOUNICODE)
20#define GTOREQUAL " >= "
21#else
22#define GTOREQUAL " β‰₯ "
23#endif
24#endif
25
26//-----------------------------------------------------------------------
27// class SearchAlphaBeta
28//-----------------------------------------------------------------------
29
30#if defined(__GNUC__)
31#pragma GCC diagnostic push
32#pragma GCC diagnostic ignored "-Weffc++"
33#endif
34
41
44 , m_nBetaCutOffMargin{SCORE_BETAMARGIN}
45{
46 m_iterativesearch = true;
47}
48
52
60
61//-----------------------------------------------------------------------
62// class SearchAlphaBetaFH
63//-----------------------------------------------------------------------
64
70
74
75#if defined(__GNUC__)
76#pragma GCC diagnostic pop
77#endif
78
79//-----------------------------------------------------------------------
80// class SearchAlphaBeta
81//-----------------------------------------------------------------------
82
83// TOCHECK: why not test for winning before evaluating moves
85 bSearchScore alpha, bSearchScore beta)
86{
87 if (getLevel()->searchDepthReached(nDepth)) {
88 return Quiescence(b, nDepth, alpha, beta, (b.isInCheck() ? 1 : 0));
89 }
90
91 bMoveList& ml = b.getMoveListRef();
92 movenum_t m_nmoves = ml.generateMoves(b);
93
94 bScore m_terminalscore = RetrieveBoardEvaluation(b);
95
96 DEBUG_sendInfoSearching(b, nDepth, alpha
97 + " " + beta
98 + " static: " + belofte::scoreAsStr(m_terminalscore)
99 + (b.isInCheck() ? " check" : ""), m_terminalscore);
100
101 if ((m_terminalscore & SCORE_POSITIVE) == SCORE_THEORETIC_DRAW) {
102 m_nodes++;
103 return DEBUG_returnInfoSearching(b, nDepth, "(th. draw)", SCORE_THEORETIC_DRAW);
104 } else {
105 if (!m_nmoves) {
106 m_nodes++;
107 return DEBUG_returnInfoSearching(b, nDepth, "(dead position)", m_terminalscore);
108 }
109 }
110
111 if (nDepth > m_maxDepth) m_maxDepth = nDepth;
112 if (nDepth > 2 && nDepth % 2) CheckIfAbortingSearch();
113
115 ml.sortMoves();
116 for (movenum_t moveid = 1; moveid <= m_nmoves; moveid++) {
117 sendInfoCurrMove(b, nDepth, moveid); /// @todo optimise
118 bMove m = ml[moveid];
119 bBoard chldbrd(b, m);
120 bSearchScore chldscore(-CalcBestMove(chldbrd, nDepth + 1, -beta, -alpha), tScoreType::SC_CHILD);
121 chldscore = attenuateScore(chldscore.getScore());
122 ml.setScoreOfMove(moveid, chldscore.getScore());
123 if (chldscore > SCORE_WINNING) {
124 b.setVariation(chldbrd);
126 return DEBUG_returnInfoSearching(b, nDepth, "(winning)", chldscore.getScore());
127 }
128 if (chldscore.getRealScore() - m_nBetaCutOffMargin >= beta) {
130 return DEBUG_returnInfoSearching(b, nDepth, "(fail soft) " + chldscore
131 + GTOREQUAL + beta, chldscore.getScore());
132 }
133 if (chldscore > alpha) {
134 b.setVariation(chldbrd);
135 DEBUG_sendInfoSearching(b, nDepth, "alpha & best update " + chldscore
136 + " > " + alpha, chldscore.getScore());
137 bestscore = chldscore.getScore();
138 alpha = chldscore.getScore(); // alpha does not change type
139 } else {
140 DEBUG_sendInfoSearching(b, nDepth, "no update " + chldscore, SCORE_UNDEFINED);
141 }
142 }
143
145 return DEBUG_returnInfoSearching(b, nDepth, "(return best)", bestscore.getScore());
146}
147
148// TOCHECK: why not test for winning before evaluating moves
150 bSearchScore alpha, bSearchScore beta)
151{
152 if (getLevel()->searchDepthReached(nDepth)) {
153 return Quiescence(b, nDepth, alpha, beta, (b.isInCheck() ? 1 : 0));
154 }
155
156 bMoveList& ml = b.getMoveListRef();
157 movenum_t m_nmoves = ml.generateMoves(b);
158
159 bScore m_terminalscore = RetrieveBoardEvaluation(b);
160
161 DEBUG_sendInfoSearching(b, nDepth, alpha
162 + " " + beta
163 + " static: " + belofte::scoreAsStr(m_terminalscore)
164 + (b.isInCheck() ? " check" : ""), m_terminalscore);
165
166 if ((m_terminalscore & SCORE_POSITIVE) == SCORE_THEORETIC_DRAW) {
167 m_nodes++;
168 return DEBUG_returnInfoSearching(b, nDepth, "(th. draw)", SCORE_THEORETIC_DRAW);
169 } else {
170 if (!m_nmoves) {
171 m_nodes++;
172 return DEBUG_returnInfoSearching(b, nDepth, "(dead position)", m_terminalscore);
173 }
174 }
175
176 if (nDepth > m_maxDepth) m_maxDepth = nDepth;
177 if (nDepth > 2 && nDepth % 2) CheckIfAbortingSearch();
178
180 ml.sortMoves();
181 for (movenum_t moveid = 1; moveid <= m_nmoves; moveid++) {
182 sendInfoCurrMove(b, nDepth, moveid); /// @todo optimise
183 bMove m = ml[moveid];
184 bBoard chldbrd(b, m);
185 bSearchScore chldscore(-CalcBestMove(chldbrd, nDepth + 1, -beta, -alpha), tScoreType::SC_CHILD);
186 chldscore = attenuateScore(chldscore.getScore());
187 ml.setScoreOfMove(moveid, chldscore.getScore());
188 if (chldscore > SCORE_WINNING) {
189 b.setVariation(chldbrd);
191 return DEBUG_returnInfoSearching(b, nDepth, "(winning)", chldscore.getScore());
192 }
193 if (chldscore.getRealScore() - m_nBetaCutOffMargin >= beta) {
195 return DEBUG_returnInfoSearching(b, nDepth, "(fail hard) " + chldscore
196 + GTOREQUAL + beta, beta.getScore());
197 }
198 if (chldscore > alpha) {
199 b.setVariation(chldbrd);
200 DEBUG_sendInfoSearching(b, nDepth, "alpha & best update " + chldscore
201 + " > " + alpha, chldscore.getScore());
202 bestscore = chldscore.getScore();
203 alpha = chldscore.getScore(); // alpha does not change type
204 } else {
205 DEBUG_sendInfoSearching(b, nDepth, "no update " + chldscore, SCORE_UNDEFINED);
206 }
207 }
208
210 return DEBUG_returnInfoSearching(b, nDepth, "(return best)", bestscore.getScore());
211}
212
213//-----------------------------------------------------------------------
214
215// TOCHECK: why not test for winning before evaluating moves
217 bSearchScore alpha, bSearchScore beta, uint8_t nCheckCount)
218{
219 bMoveList& ml = b.getMoveListRef();
220 movenum_t m_nmoves = ml.generateMoves(b);
221
222 bScore terminalScore = RetrieveBoardEvaluation(b);
223
224 DEBUG_sendInfoSearching(b, nDepth, "qs " + alpha
225 + " " + beta
226 + " static: " + belofte::scoreAsStr(terminalScore)
227 + (b.isInCheck() ? " check" : "")
228 + " +: " + belofte::to_string(nCheckCount), terminalScore);
229
230 if ((terminalScore & SCORE_POSITIVE) == SCORE_THEORETIC_DRAW) {
231 m_nodes++;
232 return DEBUG_returnInfoSearching(b, nDepth, "qs (th. draw)", SCORE_THEORETIC_DRAW);
233 } else {
234 if (!m_nmoves) {
235 m_nodes++;
236 return DEBUG_returnInfoSearching(b, nDepth, "qs (dead position)", terminalScore);
237 }
238 }
239
240 if (getLevel()->qsDepthReached(nDepth)) {
241 m_nodes++;
242 return DEBUG_returnInfoSearching(b, nDepth, "qs (qs depth reached)", terminalScore);
243 }
244
245 if (nDepth > m_maxDepth) m_maxDepth = nDepth;
246 if (nDepth > 4 && nDepth % 2) CheckIfAbortingSearch();
247
248 bool isAllMoves = false;
249 if (!ml.getNumberOfQSMoves()) {
250 if ((nCheckCount < 1) && (b.isInCheck())) {
251 // make sure QS does not end up in endless checks
252 isAllMoves = true;
253 } else {
254 m_nodes++;
255 return DEBUG_returnInfoSearching(b, nDepth, "qs (qs dead position)", terminalScore);
256 }
257 }
258 if (b.isInCheck()) nCheckCount++;
259
260 if (terminalScore - m_nBetaCutOffMargin >= beta) {
261 m_nodes++;
262 return DEBUG_returnInfoSearching(b, nDepth, "qs (beta early cut-off) static: " + belofte::scoreAsStr(terminalScore)
263 + GTOREQUAL + beta, terminalScore);
264 }
265
266 // apply null-move simulation
267 if (terminalScore > alpha) {
268 DEBUG_sendInfoSearching(b, nDepth, "qs early alpha update " + belofte::scoreAsStr(terminalScore)
269 + " > " + alpha, terminalScore);
270 alpha = terminalScore;
271 }
272
273 bSearchScore bestscore(terminalScore, tScoreType::SC_BEST);
274 ml.sortMoves();
275 for (movenum_t moveid = 1; moveid <= m_nmoves; moveid++) {
276 // TODO: count move 0 as null-move (terminalScore)
277 if (isAllMoves || (ml[moveid].isNonSilent())
278 || ((!nCheckCount) && (b.isInCheck()))) {
279 sendInfoCurrMove(b, nDepth, moveid);
280 bMove m = ml[moveid];
281 bBoard chldbrd(b, m);
282 bSearchScore chldscore(-Quiescence(chldbrd, nDepth + 1, -beta, -alpha, nCheckCount), tScoreType::SC_CHILD);
283 chldscore = attenuateScore(chldscore.getScore());
284 ml.setScoreOfMove(moveid, chldscore.getScore());
285 if (chldscore > SCORE_WINNING) {
286 b.setVariation(chldbrd);
288 return DEBUG_returnInfoSearching(b, nDepth, "qs (winning)", chldscore.getScore());
289 }
290 if (chldscore.getRealScore() - m_nBetaCutOffMargin >= beta) {
292 return DEBUG_returnInfoSearching(b, nDepth, "qs (fail soft) " + chldscore
293 + GTOREQUAL + beta, chldscore.getScore());
294 }
295 if (chldscore > alpha) {
296 b.setVariation(chldbrd);
297 DEBUG_sendInfoSearching(b, nDepth, "qs alpha & best update: " + chldscore
298 + " > " + alpha, chldscore.getScore());
299 bestscore = chldscore.getScore();
300 alpha = chldscore.getScore(); // alpha does not change type
301 } else {
302 DEBUG_sendInfoSearching(b, nDepth, "qs no update " + chldscore, SCORE_UNDEFINED);
303 }
304#if defined(_DEBUG)
305 } else {
306 DEBUG_sendInfoSearching(b, nDepth, "qs skipped #" + belofte::to_string(moveid) +
307 " till #" + belofte::to_string(m_nmoves), SCORE_UNDEFINED);
308#endif
309 /// @todo this is a bug in non-debug mode
310 break; // all QS moves are sorted in front, so break on first non-QS
311 }
312 }
313
315 return DEBUG_returnInfoSearching(b, nDepth, "qs (return best)", bestscore.getScore());
316}
317
318// eof
This is the main include file, needs to be included before any other include.
uint_fast8_t movenum_t
Definition belofte.h:109
int_fast8_t depth_t
Definition belofte.h:112
int16_t bScore
~SearchAlphaBetaFH() final
Definition search_ab.cpp:71
bScore CalcBestMove(bBoard &b, depth_t const nDepth, bSearchScore alpha, bSearchScore beta) override
bScore CalcBestMove(bBoard &b) final
Definition search_ab.cpp:53
~SearchAlphaBeta() override
Definition search_ab.cpp:49
bScore m_nBetaCutOffMargin
Definition search_ab.h:32
bScore Quiescence(bBoard &b, depth_t const nDepth, bSearchScore alpha, bSearchScore beta, uint8_t nCheckCount)
bool isInCheck() const
Definition board.cpp:278
board
Definition board.h:147
void setVariation(bBoard const &chldbrd)
Definition board.cpp:979
bMoveList & getMoveListRef()
return reference to movelist
Definition board.cpp:954
Definition move.h:69
movenum_t setScoreOfMove(movenum_t const moveid, bScore const score)
Store score of move.
Definition movelist.cpp:207
movenum_t sortMoves()
Definition movelist.cpp:225
movenum_t getNumberOfQSMoves() const
return number of non silent moves
Definition movelist.cpp:339
movenum_t generateMoves(bBoard const &b)
generate moves if not yet generated
Definition movelist.cpp:259
void sendInfoCurrMove(bBoard const &b, depth_t const nDepth, movenum_t const moveid) const
Definition search.cpp:327
bScore attenuateScore(bScore const sc) const
converge score towards zero in order to force immediate best move first
Definition search.cpp:356
depth_t m_maxDepth
Definition search.h:106
bScore RetrieveBoardEvaluation(bBoard &b) const
Cache score of board.
Definition search.cpp:343
bool m_iterativesearch
Definition search.h:109
bLevel * getLevel()
Definition search.h:117
int64_t m_nonleafnodes
Definition search.h:108
int64_t m_nodes
Definition search.h:107
void CheckIfAbortingSearch() const
Definition search.cpp:278
bScore getRealScore() const
Definition search.h:63
bScore getScore() const
Definition search.h:62
constexpr bScore SCORE_WINNING
Definition eval.h:24
constexpr bScore SCORE_POSITIVE
Definition eval.h:16
constexpr bScore SCORE_UNDEFINED
Definition eval.h:20
constexpr bScore SCORE_BETAMARGIN
Definition eval.h:30
constexpr bScore SCORE_THEORETIC_DRAW
Definition eval.h:17
constexpr bScore SCORE_INFINITE
Definition eval.h:15
#define DEBUG_returnInfoSearching(b, depth, msg, sc)
Definition search.h:32
@ SC_BETA
Definition search.h:40
@ SC_BEST
Definition search.h:40
@ SC_CHILD
Definition search.h:40
@ SC_ALPHA
Definition search.h:40
#define DEBUG_sendInfoSearching(b, depth, msg, sc)
Definition search.h:31
#define ALPHABETA
Definition search_ab.cpp:13