Belofte version 2.1.9
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 return CalcBestMove(b, ml, nDepth, alpha, beta);
25}
26
27//-----------------------------------------------------------------------
28
29/**
30 * Calculate best move from this position at given depth, iterative call
31 * @param b Board
32 * @param ml movelist of this position
33 * @param nDepth depth to search for, increasing until searchDepth
34 * @param alpha maximum for minimizing player
35 * @param beta minimum for maximizing player
36 * @bug alpha/beta values do not bubble up to calling function
37 */
39 bMoveList& ml, depth_t const nDepth,
40 bSearchScore alpha, bSearchScore beta)
41{
42 if (nDepth && getLevel()->searchDepthReached(nDepth)) {
43 return Quiescence(b, nDepth, alpha, beta, (b.isInCheck() ? 1 : 0));
44 }
45
47 if (isNoBench() && gr != GR_UNKNOWN) {
48 ++m_leafnodes; // leaf node because of final position
50 DEBUG_sendInfoSearchingNS(b, nDepth, "(draw)");
51 return 0;
52 }
54 DEBUG_sendInfoSearching(b, nDepth, "(final score)", terminalScore);
55 return terminalScore;
56 }
57
58 DEBUG_sendInfoSearchingNS(b, nDepth, alpha
59 + " " + beta
60 + (b.isInCheck() ? " check" : ""));
61
62 movenum_t n_moves = ml.generateMoves(b);
63 if (!n_moves) {
66 bScore terminalScore = RetrieveBoardEvaluation(b, gr);
67 DEBUG_sendInfoSearching(b, nDepth, "(dead position)", terminalScore);
68 return terminalScore;
69 }
70
71 if (nDepth > 2 && nDepth % 2) CheckIfAbortingSearch();
73
74 depth_t nNewDepth = nDepth + 1;
75 bSearchScore bestscore(-SCORE_INFINITE);
76 ml.sortMoves();
77 ml.clearScores();
78 for (movenum_t moveid = 1; moveid <= n_moves; ++moveid) {
79 bMove m(ml[moveid]);
80 sendInfoCurrMove(b, nDepth, m, moveid);
81 bBoard chldbrd(b, m);
82 chldbrd.applyMove(m);
83 bMoveList chldML;
84 bSearchScore chldscore(-CalcBestMove(chldbrd, chldML, nNewDepth, -beta, -alpha));
85 bScore sc = chldscore.convergeScore();
86 if (ml.setScoreOfMove(moveid, sc)) {
87 DEBUG_sendInfoSearching(b, nDepth, "do update", sc);
88 }
89 if (chldscore.improvesOn(m_nBetaCutOffMargin, beta)) {
91 DEBUG_sendInfoSearchingNS(b, nDepth, "(fail soft) " + chldscore + GTOREQUAL + beta);
92 return sc;
93 }
94 if (chldscore.improvesOn(alpha)) {
95 b.setVariation(chldbrd);
96 bestscore = sc;
97 if (chldscore.isWinning()) {
98 DEBUG_sendInfoSearching(b, nDepth, "(winning)", sc);
99 break;
100 }
101 DEBUG_sendInfoSearchingNS(b, nDepth, "alpha & best update " + chldscore + " > " + alpha);
102 alpha = sc; // alpha does not change type
103 } else {
104 DEBUG_sendInfoSearchingNS(b, nDepth, "no update " + chldscore);
105 }
106 }
107
109 DEBUG_sendInfoSearching(b, nDepth, "(return best)", bestscore.getScore());
110 return bestscore.getScore();
111}
112
113//-----------------------------------------------------------------------
114
115/**
116 * Calculate best move from this position considering only non-silent moves
117 * @param b Board
118 * @param nDepth depth to search for, increasing from searchDepth to qsSearchDepth
119 * @param alpha maximum for minimizing player
120 * @param beta minimum for maximizing player
121 * @param nCheckCount number of checks inside qs search tree
122 * @bug alpha/beta values do not bubble up to calling function
123 */
125 depth_t const nDepth,
126 bSearchScore alpha, bSearchScore beta, uint8_t nCheckCount)
127{
129 if (isNoBench() && gr != GR_UNKNOWN) {
130 ++m_leafnodes; // leaf node because of final position
132 DEBUG_sendInfoSearchingNS(b, nDepth, "(draw)");
133 return 0;
134 }
136 DEBUG_sendInfoSearching(b, nDepth, "(final score)", terminalScore);
137 return terminalScore;
138 }
139
140 DEBUG_sendInfoSearchingNS(b, nDepth, "qs " + alpha
141 + " " + beta
142 + (b.isInCheck() ? " check" : "")
143 + " +: " + belofte::to_string(nCheckCount));
144
145 bMoveList ml;
146 movenum_t n_moves = ml.generateMoves(b);
147 if (!n_moves) {
148 ++m_leafnodes;
149 b.calcMinorPieces();
150 bScore terminalScore = RetrieveBoardEvaluation(b, gr);
151 DEBUG_sendInfoSearching(b, nDepth, "qs (dead position)", terminalScore);
152 return terminalScore;
153 }
154
155 if (getLevel()->qsDepthReached(nDepth)) {
156 ++m_leafnodes;
157 b.calcMinorPieces();
158 bScore terminalScore = RetrieveBoardEvaluation(b, gr);
159 DEBUG_sendInfoSearching(b, nDepth, "qs (qs depth reached)", terminalScore);
160 return terminalScore;
161 }
162
164 if (nDepth > 4 && nDepth % 2) CheckIfAbortingSearch();
165
166 bool escapeFirstCheck = false;
167 if (!ml.getNumberOfQSMoves()) {
168 if ((nCheckCount < 1) && (b.isInCheck())) {
169 // make sure QS does not end up in endless checks
170 escapeFirstCheck = true;
171 } else {
172 ++m_leafnodes;
173 b.calcMinorPieces();
174 bScore terminalScore = RetrieveBoardEvaluation(b, gr);
175 DEBUG_sendInfoSearching(b, nDepth, "qs (qs dead position)", terminalScore);
176 return terminalScore;
177 }
178 }
179 if (b.isInCheck()) ++nCheckCount;
180
181 b.calcMinorPieces();
182 bSearchScore bestscore(RetrieveBoardEvaluation(b, gr));
183
184 if (bestscore.improvesOn(m_nBetaCutOffMargin, beta)) {
185 ++m_leafnodes;
186 DEBUG_sendInfoSearchingNS(b, nDepth, "qs (beta early cut-off) static: " + bestscore + GTOREQUAL + beta);
187 return bestscore.getScore();
188 }
189
190 // apply null-move simulation
191 if (bestscore.improvesOn(alpha)) {
192 DEBUG_sendInfoSearchingNS(b, nDepth, "qs early alpha update " + bestscore + " > " + alpha);
193 alpha = bestscore.getScore();
194 }
195
196 depth_t nNewDepth = nDepth + 1;
197 ml.sortMoves();
198 ml.clearScores();
199 ml.setBestMoveScore(1, bestscore.getScore());
200 for (movenum_t moveid = 1; moveid <= n_moves; ++moveid) {
201 bMove m(ml[moveid]);
202 if (escapeFirstCheck || (m.isNonSilent())) {
203 sendInfoCurrMove(b, nDepth, m, moveid);
204 boardInfo_t bi = b.applyMove(m);
205 bSearchScore chldscore(-Quiescence(b, nNewDepth, -beta, -alpha, nCheckCount));
206 b.unApplyMove(m, bi);
207 ml.setScoreOfMove(moveid, chldscore.convergeScore());
208 if (chldscore.improvesOn(m_nBetaCutOffMargin, beta)) {
210 DEBUG_sendInfoSearchingNS(b, nDepth, "qs (fail soft) " + chldscore + GTOREQUAL + beta);
211 return chldscore.getScore();
212 }
213 if (chldscore.improvesOn(alpha)) {
214 bestscore = chldscore.getScore();
215 if (chldscore.isWinning()) {
216 DEBUG_sendInfoSearching(b, nDepth, "qs (winning)", chldscore.getScore());
217 break;
218 }
219 DEBUG_sendInfoSearchingNS(b, nDepth, "qs alpha & best update: " + chldscore + " > " + alpha);
220 alpha = chldscore.getScore(); // alpha does not change type
221 } else {
222 DEBUG_sendInfoSearchingNS(b, nDepth, "qs no update " + chldscore);
223 }
224 } else {
225 if (moveid == 1) {
226 // no QS moves
227 DEBUG_sendInfoSearchingNS(b, nDepth, "qs no qs-moves " + bestscore);
228 return bestscore.getScore();
229 }
230 DEBUG_sendInfoSearchingNS(b, nDepth, "qs skipped #" + belofte::to_string(moveid) +
231 " till #" + belofte::to_string(n_moves));
232 break; // all QS moves are sorted in front, so break on first non-QS
233 }
234 break; /// @bug quit after first move
235 }
236
238 DEBUG_sendInfoSearching(b, nDepth, "qs (return best)", bestscore.getScore());
239 return bestscore.getScore();
240}
241
242// 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:100
int_fast8_t depth_t
Definition belofte.h:103
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:166
void unApplyMove(bMove const &m, boardInfo_t const oldBoardInfo)
exact restoration of basic board using move details
constexpr bool isInCheck() const
Definition basicboard.h:132
constexpr bool isNonSilent() const
Definition basicmove.h:133
board
Definition board.h:45
void setVariation(bBoard const &chldbrd)
Definition board.cpp:227
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:217
void calcMinorPieces(bool const bForceRecalc=false)
Recalculate minor pieces, used for evaluation and end of game condition in case of less than 5 pieces...
Definition board.cpp:68
Definition move.h:13
void clearScores()
Definition movelist.h:52
movenum_t generateMoves(bBasicBoard const &b)
generate moves if not yet generated
Definition movelist.cpp:340
void setBestMoveScore(movenum_t const moveid, bScore const score)
Definition movelist.h:61
void sortMoves()
sort moves and update bestmove id if less than 5 moves, sort all if more than 5 moves,...
Definition movelist.cpp:293
bool setScoreOfMove(movenum_t const moveid, bScore const score)
Store score of move and update best move.
Definition movelist.cpp:130
constexpr movenum_t getNumberOfQSMoves() const
Definition movelist.h:36
static bScore resultToScoreFlag(gameResult_t const gr)
Class static function convert all draw scores to SCORE_THEORETIC_DRAW.
Definition eval.cpp:77
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:70
void adjustMaxSearchedDepth(depth_t const nDepth)
Definition search.h:132
void sendInfoCurrMove(bBoard const &b, depth_t const nCurDepth, bMove const &m, movenum_t const moveid) const
Definition search.cpp:223
constexpr bool isNoBench() const
Definition search.h:114
bLevel * getLevel()
Definition search.h:141
int64_t m_nonleafnodes
Definition search.h:125
int64_t m_leafnodes
Definition search.h:124
bScore RetrieveBoardEvaluation(bBoard &b, gameResult_t const gr=GR_UNSET) const
Get score of board, eventually from cache.
Definition search.cpp:242
void CheckIfAbortingSearch() const
Definition search.cpp:168
constexpr bScore getScore() const
Definition searchscore.h:64
bool improvesOn(bSearchScore const &sc)
Definition searchscore.h:72
constexpr bool isWinning() const
Definition searchscore.h:70
bScore convergeScore()
Definition searchscore.h:95
@ GR_UNKNOWN
Definition eval.h:36
enum gameResult gameResult_t
Definition eval.h:43
int16_t bScore
Definition eval.h:11
constexpr bScore SCORE_INFINITE
Definition eval.h:19
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
#define DEBUG_sendInfoSearchingNS(b, depth, msg)
Definition search.h:36
#define DEBUG_sendInfoSearching(b, depth, msg, sc)
Definition search.h:35
@ SC_BETA
Definition searchscore.h:15
@ SC_ALPHA
Definition searchscore.h:15