Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/stockfish
Path: blob/master/src/search.h
513 views
1
/*
2
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
4
5
Stockfish is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
10
Stockfish is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
14
15
You should have received a copy of the GNU General Public License
16
along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#ifndef SEARCH_H_INCLUDED
20
#define SEARCH_H_INCLUDED
21
22
#include <algorithm>
23
#include <array>
24
#include <atomic>
25
#include <cassert>
26
#include <cstddef>
27
#include <cstdint>
28
#include <functional>
29
#include <map>
30
#include <memory>
31
#include <string>
32
#include <string_view>
33
#include <vector>
34
35
#include "history.h"
36
#include "misc.h"
37
#include "nnue/network.h"
38
#include "nnue/nnue_accumulator.h"
39
#include "numa.h"
40
#include "position.h"
41
#include "score.h"
42
#include "syzygy/tbprobe.h"
43
#include "timeman.h"
44
#include "types.h"
45
46
namespace Stockfish {
47
48
// Different node types, used as a template parameter
49
enum NodeType {
50
NonPV,
51
PV,
52
Root
53
};
54
55
class TranspositionTable;
56
class ThreadPool;
57
class OptionsMap;
58
59
namespace Search {
60
61
// Stack struct keeps track of the information we need to remember from nodes
62
// shallower and deeper in the tree during the search. Each search thread has
63
// its own array of Stack objects, indexed by the current ply.
64
struct Stack {
65
Move* pv;
66
PieceToHistory* continuationHistory;
67
CorrectionHistory<PieceTo>* continuationCorrectionHistory;
68
int ply;
69
Move currentMove;
70
Move excludedMove;
71
Value staticEval;
72
int statScore;
73
int moveCount;
74
bool inCheck;
75
bool ttPv;
76
bool ttHit;
77
int cutoffCnt;
78
int reduction;
79
};
80
81
82
// RootMove struct is used for moves at the root of the tree. For each root move
83
// we store a score and a PV (really a refutation in the case of moves which
84
// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves.
85
struct RootMove {
86
87
explicit RootMove(Move m) :
88
pv(1, m) {}
89
bool extract_ponder_from_tt(const TranspositionTable& tt, Position& pos);
90
bool operator==(const Move& m) const { return pv[0] == m; }
91
// Sort in descending order
92
bool operator<(const RootMove& m) const {
93
return m.score != score ? m.score < score : m.previousScore < previousScore;
94
}
95
96
uint64_t effort = 0;
97
Value score = -VALUE_INFINITE;
98
Value previousScore = -VALUE_INFINITE;
99
Value averageScore = -VALUE_INFINITE;
100
Value meanSquaredScore = -VALUE_INFINITE * VALUE_INFINITE;
101
Value uciScore = -VALUE_INFINITE;
102
bool scoreLowerbound = false;
103
bool scoreUpperbound = false;
104
int selDepth = 0;
105
int tbRank = 0;
106
Value tbScore;
107
std::vector<Move> pv;
108
};
109
110
using RootMoves = std::vector<RootMove>;
111
112
113
// LimitsType struct stores information sent by the caller about the analysis required.
114
struct LimitsType {
115
116
// Init explicitly due to broken value-initialization of non POD in MSVC
117
LimitsType() {
118
time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0);
119
movestogo = depth = mate = perft = infinite = 0;
120
nodes = 0;
121
ponderMode = false;
122
}
123
124
bool use_time_management() const { return time[WHITE] || time[BLACK]; }
125
126
std::vector<std::string> searchmoves;
127
TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime;
128
int movestogo, depth, mate, perft, infinite;
129
uint64_t nodes;
130
bool ponderMode;
131
};
132
133
134
// The UCI stores the uci options, thread pool, and transposition table.
135
// This struct is used to easily forward data to the Search::Worker class.
136
struct SharedState {
137
SharedState(const OptionsMap& optionsMap,
138
ThreadPool& threadPool,
139
TranspositionTable& transpositionTable,
140
std::map<NumaIndex, SharedHistories>& sharedHists,
141
const LazyNumaReplicatedSystemWide<Eval::NNUE::Networks>& nets) :
142
options(optionsMap),
143
threads(threadPool),
144
tt(transpositionTable),
145
sharedHistories(sharedHists),
146
networks(nets) {}
147
148
const OptionsMap& options;
149
ThreadPool& threads;
150
TranspositionTable& tt;
151
std::map<NumaIndex, SharedHistories>& sharedHistories;
152
const LazyNumaReplicatedSystemWide<Eval::NNUE::Networks>& networks;
153
};
154
155
class Worker;
156
157
// Null Object Pattern, implement a common interface for the SearchManagers.
158
// A Null Object will be given to non-mainthread workers.
159
class ISearchManager {
160
public:
161
virtual ~ISearchManager() {}
162
virtual void check_time(Search::Worker&) = 0;
163
};
164
165
struct InfoShort {
166
int depth;
167
Score score;
168
};
169
170
struct InfoFull: InfoShort {
171
int selDepth;
172
size_t multiPV;
173
std::string_view wdl;
174
std::string_view bound;
175
size_t timeMs;
176
size_t nodes;
177
size_t nps;
178
size_t tbHits;
179
std::string_view pv;
180
int hashfull;
181
};
182
183
struct InfoIteration {
184
int depth;
185
std::string_view currmove;
186
size_t currmovenumber;
187
};
188
189
// Skill structure is used to implement strength limit. If we have a UCI_Elo,
190
// we convert it to an appropriate skill level, anchored to the Stash engine.
191
// This method is based on a fit of the Elo results for games played between
192
// Stockfish at various skill levels and various versions of the Stash engine.
193
// Skill 0 .. 19 now covers CCRL Blitz Elo from 1320 to 3190, approximately
194
// Reference: https://github.com/vondele/Stockfish/commit/a08b8d4e9711c2
195
struct Skill {
196
// Lowest and highest Elo ratings used in the skill level calculation
197
constexpr static int LowestElo = 1320;
198
constexpr static int HighestElo = 3190;
199
200
Skill(int skill_level, int uci_elo) {
201
if (uci_elo)
202
{
203
double e = double(uci_elo - LowestElo) / (HighestElo - LowestElo);
204
level = std::clamp((((37.2473 * e - 40.8525) * e + 22.2943) * e - 0.311438), 0.0, 19.0);
205
}
206
else
207
level = double(skill_level);
208
}
209
bool enabled() const { return level < 20.0; }
210
bool time_to_pick(Depth depth) const { return depth == 1 + int(level); }
211
Move pick_best(const RootMoves&, size_t multiPV);
212
213
double level;
214
Move best = Move::none();
215
};
216
217
// SearchManager manages the search from the main thread. It is responsible for
218
// keeping track of the time, and storing data strictly related to the main thread.
219
class SearchManager: public ISearchManager {
220
public:
221
using UpdateShort = std::function<void(const InfoShort&)>;
222
using UpdateFull = std::function<void(const InfoFull&)>;
223
using UpdateIter = std::function<void(const InfoIteration&)>;
224
using UpdateBestmove = std::function<void(std::string_view, std::string_view)>;
225
226
struct UpdateContext {
227
UpdateShort onUpdateNoMoves;
228
UpdateFull onUpdateFull;
229
UpdateIter onIter;
230
UpdateBestmove onBestmove;
231
};
232
233
234
SearchManager(const UpdateContext& updateContext) :
235
updates(updateContext) {}
236
237
void check_time(Search::Worker& worker) override;
238
239
void pv(Search::Worker& worker,
240
const ThreadPool& threads,
241
const TranspositionTable& tt,
242
Depth depth);
243
244
Stockfish::TimeManagement tm;
245
double originalTimeAdjust;
246
int callsCnt;
247
std::atomic_bool ponder;
248
249
std::array<Value, 4> iterValue;
250
double previousTimeReduction;
251
Value bestPreviousScore;
252
Value bestPreviousAverageScore;
253
bool stopOnPonderhit;
254
255
size_t id;
256
257
const UpdateContext& updates;
258
};
259
260
class NullSearchManager: public ISearchManager {
261
public:
262
void check_time(Search::Worker&) override {}
263
};
264
265
// Search::Worker is the class that does the actual search.
266
// It is instantiated once per thread, and it is responsible for keeping track
267
// of the search history, and storing data required for the search.
268
class Worker {
269
public:
270
Worker(SharedState&,
271
std::unique_ptr<ISearchManager>,
272
size_t,
273
size_t,
274
size_t,
275
NumaReplicatedAccessToken);
276
277
// Called at instantiation to initialize reductions tables.
278
// Reset histories, usually before a new game.
279
void clear();
280
281
// Called when the program receives the UCI 'go' command.
282
// It searches from the root position and outputs the "bestmove".
283
void start_searching();
284
285
bool is_mainthread() const { return threadIdx == 0; }
286
287
void ensure_network_replicated();
288
289
// Public because they need to be updatable by the stats
290
ButterflyHistory mainHistory;
291
LowPlyHistory lowPlyHistory;
292
293
CapturePieceToHistory captureHistory;
294
ContinuationHistory continuationHistory[2][2];
295
CorrectionHistory<Continuation> continuationCorrectionHistory;
296
297
TTMoveHistory ttMoveHistory;
298
SharedHistories& sharedHistory;
299
300
private:
301
void iterative_deepening();
302
303
void do_move(Position& pos, const Move move, StateInfo& st, Stack* const ss);
304
void
305
do_move(Position& pos, const Move move, StateInfo& st, const bool givesCheck, Stack* const ss);
306
void do_null_move(Position& pos, StateInfo& st, Stack* const ss);
307
void undo_move(Position& pos, const Move move);
308
void undo_null_move(Position& pos);
309
310
// This is the main search function, for both PV and non-PV nodes
311
template<NodeType nodeType>
312
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
313
314
// Quiescence search function, which is called by the main search
315
template<NodeType nodeType>
316
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta);
317
318
Depth reduction(bool i, Depth d, int mn, int delta) const;
319
320
// Pointer to the search manager, only allowed to be called by the main thread
321
SearchManager* main_manager() const {
322
assert(threadIdx == 0);
323
return static_cast<SearchManager*>(manager.get());
324
}
325
326
TimePoint elapsed() const;
327
TimePoint elapsed_time() const;
328
329
Value evaluate(const Position&);
330
331
LimitsType limits;
332
333
size_t pvIdx, pvLast;
334
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
335
int selDepth, nmpMinPly;
336
337
Value optimism[COLOR_NB];
338
339
Position rootPos;
340
StateInfo rootState;
341
RootMoves rootMoves;
342
Depth rootDepth, completedDepth;
343
Value rootDelta;
344
345
size_t threadIdx, numaThreadIdx, numaTotal;
346
NumaReplicatedAccessToken numaAccessToken;
347
348
// Reductions lookup table initialized at startup
349
std::array<int, MAX_MOVES> reductions; // [depth or moveNumber]
350
351
// The main thread has a SearchManager, the others have a NullSearchManager
352
std::unique_ptr<ISearchManager> manager;
353
354
Tablebases::Config tbConfig;
355
356
const OptionsMap& options;
357
ThreadPool& threads;
358
TranspositionTable& tt;
359
const LazyNumaReplicatedSystemWide<Eval::NNUE::Networks>& networks;
360
361
// Used by NNUE
362
Eval::NNUE::AccumulatorStack accumulatorStack;
363
Eval::NNUE::AccumulatorCaches refreshTable;
364
365
friend class Stockfish::ThreadPool;
366
friend class SearchManager;
367
};
368
369
struct ConthistBonus {
370
int index;
371
int weight;
372
};
373
374
375
} // namespace Search
376
377
} // namespace Stockfish
378
379
#endif // #ifndef SEARCH_H_INCLUDED
380
381