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