Belofte version 2.2.0
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/**
13 * Root search for algorithm
14 * @param b board
15 * @param ml movelist of this position
16 * @todo check why not test for winning before evaluating moves
17 */
19{
22 depth_t const nDepth = 0;
23
24 bScore score = CalcBestMove(b, ml, nDepth, alpha, beta);
25 return score;
26}
27
28//-----------------------------------------------------------------------
29
30/**
31 * Calculate best move from this position at given depth, iterative call
32 * @param b Board
33 * @param ml movelist of this position
34 * @param nDepth depth to search for, increasing until searchDepth
35 * @param alpha maximum for minimizing player
36 * @param beta minimum for maximizing player
37 * @bug alpha/beta values do not bubble up to calling function
38 */
40 bMoveList& ml, depth_t const nDepth,
41 bSearchScore alpha, bSearchScore beta)
42{
43 if (nDepth && getLevel()->searchDepthReached(nDepth)) {
44 bScore terminalScore = Quiescence(b, nDepth, alpha, beta, (b.isInCheck() ? 1 : 0));
45 return terminalScore;
46 }
47
49 if (isNoBench() && gr != GR_UNKNOWN) {
50 ++m_leafnodes; // leaf node because of final position
52 DEBUG_sendInfoSearchingNS(b, nDepth, "(draw)");
54 }
56 DEBUG_sendInfoSearching(b, nDepth, "(final score)", terminalScore);
57 return terminalScore;
58 }
59
60 DEBUG_sendInfoSearchingNS(b, nDepth, alpha
61 + " " + beta
62 + (b.isInCheck() ? " check" : ""));
63
64 movenum_t n_moves = ml.generateMoves(b);
65 if (!n_moves) {
67 bScore terminalScore = RetrieveBoardEvaluation(b, gr, true);
68 DEBUG_sendInfoSearching(b, nDepth, "(dead position)", terminalScore);
69 return terminalScore;
70 }
71
72 if (nDepth > 3 && nDepth % 2) CheckIfAbortingSearch();
74
75 depth_t nNewDepth = nDepth + 1;
76 bSearchScore bestscore(-SCORE_INFINITE);
77 ml.sortMoves(true);
79 for (ml.curmoveid = 1; ml.curmoveid <= n_moves; ++ml.curmoveid) {
80 bMove m(ml[ml.curmoveid]);
81 sendInfoCurrMove(b, nDepth, m, ml.curmoveid);
82 bBoard chldbrd(b, m);
83 chldbrd.applyMove(m);
84 bMoveList chldML;
85 bSearchScore chldscore(-CalcBestMove(chldbrd, chldML, nNewDepth, -beta, -alpha));
86 bScore sc = chldscore.convergeScore();
87 if (ml.setScoreOfMove(ml.curmoveid, sc)) {
88 DEBUG_sendInfoSearching(b, nDepth, "do movescore update", sc);
89 }
90 if (chldscore.improvesOn(m_nBetaCutOffMargin, beta)) {
92 DEBUG_sendInfoSearchingNS(b, nDepth, "(fail soft) " + chldscore + GTOREQUAL + beta);
93 return sc;
94 }
95 if (chldscore.improvesOn(alpha)) {
96 b.setVariation(chldbrd);
97 bestscore = sc;
98 if (chldscore.isWinning()) {
99 DEBUG_sendInfoSearching(b, nDepth, "(winning)", sc);
100 break;
101 }
102 DEBUG_sendInfoSearchingNS(b, nDepth, "alpha & best update to : " + chldscore + " (> " + alpha + ")");
103 alpha = sc; // alpha does not change type
104 } else {
105 DEBUG_sendInfoSearchingNS(b, nDepth, "no update " + chldscore);
106 }
107 }
108
110 bScore score = bestscore.getScore();
111 DEBUG_sendInfoSearching(b, nDepth, "(return best)", score);
112 return score;
113}
114
115//-----------------------------------------------------------------------
116
117/**
118 * Calculate best move from this position considering only non-silent moves
119 * @param b Board
120 * @param nDepth depth to search for, increasing from searchDepth to qsSearchDepth
121 * @param alpha maximum for minimizing player
122 * @param beta minimum for maximizing player
123 * @param nCheckCount number of checks inside qs search tree
124 * @bug alpha/beta values do not bubble up to calling function
125 */
127 depth_t const nDepth,
128 bSearchScore alpha, bSearchScore beta, uint8_t nCheckCount)
129{
131 if (isNoBench() && gr != GR_UNKNOWN) {
132 ++m_leafnodes; // leaf node because of final position
134 DEBUG_sendInfoSearchingNS(b, nDepth, "qs (draw)");
136 }
138 DEBUG_sendInfoSearching(b, nDepth, "qs (final score)", terminalSC);
139 return terminalSC;
140 }
141
142 bMoveList ml;
143 movenum_t n_moves = ml.generateMoves(b);
144 bScore terminalScore = RetrieveBoardEvaluation(b, gr, true);
145
146 DEBUG_sendInfoSearching(b, nDepth, "qs " + alpha
147 + " " + beta
148 + (b.isInCheck() ? " check" : "")
149 + " +: " + belofte::to_string(nCheckCount),
150 terminalScore);
151
152 if (!n_moves) {
153 ++m_leafnodes;
154 DEBUG_sendInfoSearching(b, nDepth, "qs (dead position)", terminalScore);
155 return terminalScore;
156 }
157
158 if (getLevel()->qsDepthReached(nDepth)) {
159 ++m_leafnodes;
160 DEBUG_sendInfoSearching(b, nDepth, "qs (qs depth reached)", terminalScore);
161 return terminalScore;
162 }
163
164 if (nDepth > 5 && nDepth % 2) CheckIfAbortingSearch();
166
167 bool escapeFirstCheck = false;
168 if (!ml.getNumberOfQSMoves()) {
169 if ((nCheckCount < 1) && (b.isInCheck())) {
170 // make sure QS does not end up in endless checks
171 escapeFirstCheck = true;
172 } else {
173 ++m_leafnodes;
174 DEBUG_sendInfoSearching(b, nDepth, "qs (qs dead position)", terminalScore);
175 return terminalScore;
176 }
177 }
178 if (b.isInCheck()) ++nCheckCount;
179
180 bSearchScore bestscore(terminalScore);
181
182 if (bestscore.improvesOn(m_nBetaCutOffMargin, beta)) {
183 ++m_leafnodes;
184 DEBUG_sendInfoSearchingNS(b, nDepth, "qs (beta early cut-off) : " + bestscore + GTOREQUAL + beta);
185 return terminalScore;
186 }
187
188 // apply null-move simulation
189 if (bestscore.improvesOn(alpha)) {
190 DEBUG_sendInfoSearchingNS(b, nDepth, "qs early alpha update : " + bestscore + " > " + alpha);
191 alpha = terminalScore;
192 }
193
194 depth_t nNewDepth = nDepth + 1;
195 ml.sortMoves(true);
197 ml.setBestMoveScore(1, terminalScore);
198 for (ml.curmoveid = 1; ml.curmoveid <= n_moves; ++ml.curmoveid) {
199 bMove m(ml[ml.curmoveid]);
200 if (escapeFirstCheck || m.isNonSilent() || m.isCheck()) {
201 sendInfoCurrMove(b, nDepth, m, ml.curmoveid);
202 boardInfo_t bi = b.applyMove(m);
203 bSearchScore chldscore(-Quiescence(b, nNewDepth, -beta, -alpha, nCheckCount));
204 b.unApplyMove(m, bi);
205 bScore sc = chldscore.convergeScore();
206 if (ml.setScoreOfMove(ml.curmoveid, sc)) {
207 DEBUG_sendInfoSearching(b, nDepth, "qs do bestscore update", sc);
208 if (chldscore.improvesOn(m_nBetaCutOffMargin, beta)) {
209 ++m_leafnodes;
210 DEBUG_sendInfoSearchingNS(b, nDepth, "qs (fail soft) : " + chldscore + GTOREQUAL + beta);
211 return sc;
212 }
213 if (chldscore.improvesOn(alpha)) {
214 bestscore = sc;
215 if (chldscore.isWinning()) {
216 DEBUG_sendInfoSearching(b, nDepth, "qs (winning)", sc);
217 break;
218 }
219 DEBUG_sendInfoSearching(b, nDepth, "qs alpha update (" + alpha + ")", sc);
220 alpha = sc; // alpha does not change type
221 } else {
222 DEBUG_sendInfoSearchingNS(b, nDepth, "qs no update on alpha");
223 }
224 }
225 } else {
226 if (ml.curmoveid == 1) {
227 // no QS moves
228 DEBUG_sendInfoSearching(b, nDepth, "qs (no qs-moves)", terminalScore);
229 return terminalScore;
230 }
231 // all QS moves are sorted in front, so break on first non-QS
232 DEBUG_sendInfoSearchingNS(b, nDepth, "qs skipped #" + belofte::to_string(ml.curmoveid) +
233 " till #" + belofte::to_string(n_moves));
234 break;
235 }
236 }
237
239 bScore score = bestscore.getScore();
240 DEBUG_sendInfoSearching(b, nDepth, "qs (return best)", score);
241 return score;
242}
243
244// eof
union boardInfo boardInfo_t
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
bScore CalcBestMove(bBoard &b, bMoveList &ml) override
Root search for algorithm.
Definition search_ab.cpp:18
bScore m_nBetaCutOffMargin
Definition search_ab.h:43
bScore Quiescence(bBoard &b, depth_t const nDepth, bSearchScore alpha, bSearchScore beta, uint8_t nCheckCount)
Calculate best move from this position considering only non-silent moves.
constexpr bScore minimizing() const
Definition basicboard.h:172
void unApplyMove(bMove const &m, boardInfo_t const oldBoardInfo)
exact restoration of basic board using move details
constexpr bool isInCheck() const
Definition basicboard.h:138
constexpr bool isNonSilent() const
Definition basicmove.h:133
constexpr bool isCheck() const
Definition basicmove.h:109
board
Definition board.h:45
void setVariation(bBoard const &chldbrd)
Definition board.cpp:225
boardInfo_t applyMove(bMove const &m) override
modification of board move is kept on previous board newboard does not have move stored and has flag ...
Definition board.cpp:215
Definition move.h:13
void clearScores()
Definition movelist.h:67
movenum_t generateMoves(bBasicBoard const &b)
generate moves if not yet generated
Definition movelist.cpp:417
bool setScoreOfMove(movenum_t const moveid, bScore const score)
Store score of move and update best move.
Definition movelist.cpp:157
movenum_t curmoveid
Definition movelist.h:104
constexpr movenum_t getNumberOfQSMoves() const
Definition movelist.h:51
void setBestMoveScore(bScore const score)
Definition movelist.h:80
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
static bool isDrawResult(gameResult_t const gr)
Definition eval.h:67
void adjustMaxSearchedDepth(depth_t const nDepth)
Definition search.h:152
void sendInfoCurrMove(bBoard const &b, depth_t const nCurDepth, bMove const &m, movenum_t const moveid) const
Definition search.cpp:282
constexpr bool isNoBench() const
Definition search.h:132
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 CheckIfAbortingSearch() const
Definition search.cpp:223
constexpr bScore getScore() const
Definition searchscore.h:64
bool improvesOn(bSearchScore const &sc)
Definition searchscore.h:80
constexpr bool isWinning() const
Definition searchscore.h:78
bScore convergeScore()
@ 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_THEORETIC_DRAW
Definition eval.h:16
constexpr bScore SCORE_INFINITE
Definition eval.h:20
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
#define DEBUG_sendInfoSearchingNS(b, depth, msg)
Definition search.h:54
#define DEBUG_sendInfoSearching(b, depth, msg, sc)
Definition search.h:53
@ SC_BETA
Definition searchscore.h:15
@ SC_ALPHA
Definition searchscore.h:15