Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/Stockfish
Path: blob/master/src/movepick.cpp
376 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
#include "movepick.h"
20
21
#include <cassert>
22
#include <limits>
23
#include <utility>
24
25
#include "bitboard.h"
26
#include "misc.h"
27
#include "position.h"
28
29
namespace Stockfish {
30
31
namespace {
32
33
enum Stages {
34
// generate main search moves
35
MAIN_TT,
36
CAPTURE_INIT,
37
GOOD_CAPTURE,
38
QUIET_INIT,
39
GOOD_QUIET,
40
BAD_CAPTURE,
41
BAD_QUIET,
42
43
// generate evasion moves
44
EVASION_TT,
45
EVASION_INIT,
46
EVASION,
47
48
// generate probcut moves
49
PROBCUT_TT,
50
PROBCUT_INIT,
51
PROBCUT,
52
53
// generate qsearch moves
54
QSEARCH_TT,
55
QCAPTURE_INIT,
56
QCAPTURE
57
};
58
59
60
// Sort moves in descending order up to and including a given limit.
61
// The order of moves smaller than the limit is left unspecified.
62
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
63
64
for (ExtMove *sortedEnd = begin, *p = begin + 1; p < end; ++p)
65
if (p->value >= limit)
66
{
67
ExtMove tmp = *p, *q;
68
*p = *++sortedEnd;
69
for (q = sortedEnd; q != begin && *(q - 1) < tmp; --q)
70
*q = *(q - 1);
71
*q = tmp;
72
}
73
}
74
75
} // namespace
76
77
78
// Constructors of the MovePicker class. As arguments, we pass information
79
// to decide which class of moves to emit, to help sorting the (presumably)
80
// good moves first, and how important move ordering is at the current node.
81
82
// MovePicker constructor for the main search and for the quiescence search
83
MovePicker::MovePicker(const Position& p,
84
Move ttm,
85
Depth d,
86
const ButterflyHistory* mh,
87
const LowPlyHistory* lph,
88
const CapturePieceToHistory* cph,
89
const PieceToHistory** ch,
90
const PawnHistory* ph,
91
int pl) :
92
pos(p),
93
mainHistory(mh),
94
lowPlyHistory(lph),
95
captureHistory(cph),
96
continuationHistory(ch),
97
pawnHistory(ph),
98
ttMove(ttm),
99
depth(d),
100
ply(pl) {
101
102
if (pos.checkers())
103
stage = EVASION_TT + !(ttm && pos.pseudo_legal(ttm));
104
105
else
106
stage = (depth > 0 ? MAIN_TT : QSEARCH_TT) + !(ttm && pos.pseudo_legal(ttm));
107
}
108
109
// MovePicker constructor for ProbCut: we generate captures with Static Exchange
110
// Evaluation (SEE) greater than or equal to the given threshold.
111
MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceToHistory* cph) :
112
pos(p),
113
captureHistory(cph),
114
ttMove(ttm),
115
threshold(th) {
116
assert(!pos.checkers());
117
118
// Removing the SEE check passes as simplification, but hurts mate finding
119
stage = PROBCUT_TT
120
+ !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold));
121
}
122
123
// Assigns a numerical value to each move in a list, used for sorting.
124
// Captures are ordered by Most Valuable Victim (MVV), preferring captures
125
// with a good history. Quiets moves are ordered using the history tables.
126
template<GenType Type>
127
ExtMove* MovePicker::score(MoveList<Type>& ml) {
128
129
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
130
131
Color us = pos.side_to_move();
132
133
[[maybe_unused]] Bitboard threatByLesser[KING + 1];
134
if constexpr (Type == QUIETS)
135
{
136
threatByLesser[PAWN] = 0;
137
threatByLesser[KNIGHT] = threatByLesser[BISHOP] = pos.attacks_by<PAWN>(~us);
138
threatByLesser[ROOK] =
139
pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatByLesser[KNIGHT];
140
threatByLesser[QUEEN] = pos.attacks_by<ROOK>(~us) | threatByLesser[ROOK];
141
threatByLesser[KING] = pos.attacks_by<QUEEN>(~us) | threatByLesser[QUEEN];
142
}
143
144
ExtMove* it = cur;
145
for (auto move : ml)
146
{
147
ExtMove& m = *it++;
148
m = move;
149
150
const Square from = m.from_sq();
151
const Square to = m.to_sq();
152
const Piece pc = pos.moved_piece(m);
153
const PieceType pt = type_of(pc);
154
const Piece capturedPiece = pos.piece_on(to);
155
156
if constexpr (Type == CAPTURES)
157
m.value = (*captureHistory)[pc][to][type_of(capturedPiece)]
158
+ 7 * int(PieceValue[capturedPiece]) + 1024 * bool(pos.check_squares(pt) & to);
159
160
else if constexpr (Type == QUIETS)
161
{
162
// histories
163
m.value = 2 * (*mainHistory)[us][m.from_to()];
164
m.value += 2 * (*pawnHistory)[pawn_history_index(pos)][pc][to];
165
m.value += (*continuationHistory[0])[pc][to];
166
m.value += (*continuationHistory[1])[pc][to];
167
m.value += (*continuationHistory[2])[pc][to];
168
m.value += (*continuationHistory[3])[pc][to];
169
m.value += (*continuationHistory[5])[pc][to];
170
171
// bonus for checks
172
m.value += (bool(pos.check_squares(pt) & to) && pos.see_ge(m, -75)) * 16384;
173
174
// penalty for moving to a square threatened by a lesser piece
175
// or bonus for escaping an attack by a lesser piece.
176
static constexpr int bonus[KING + 1] = {0, 0, 144, 144, 256, 517, 10000};
177
int v = threatByLesser[pt] & to ? -95 : 100 * bool(threatByLesser[pt] & from);
178
m.value += bonus[pt] * v;
179
180
181
if (ply < LOW_PLY_HISTORY_SIZE)
182
m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + ply);
183
}
184
185
else // Type == EVASIONS
186
{
187
if (pos.capture_stage(m))
188
m.value = PieceValue[capturedPiece] + (1 << 28);
189
else
190
{
191
m.value = (*mainHistory)[us][m.from_to()] + (*continuationHistory[0])[pc][to];
192
if (ply < LOW_PLY_HISTORY_SIZE)
193
m.value += 2 * (*lowPlyHistory)[ply][m.from_to()] / (1 + ply);
194
}
195
}
196
}
197
return it;
198
}
199
200
// Returns the next move satisfying a predicate function.
201
// This never returns the TT move, as it was emitted before.
202
template<typename Pred>
203
Move MovePicker::select(Pred filter) {
204
205
for (; cur < endCur; ++cur)
206
if (*cur != ttMove && filter())
207
return *cur++;
208
209
return Move::none();
210
}
211
212
// This is the most important method of the MovePicker class. We emit one
213
// new pseudo-legal move on every call until there are no more moves left,
214
// picking the move with the highest score from a list of generated moves.
215
Move MovePicker::next_move() {
216
217
constexpr int goodQuietThreshold = -14000;
218
top:
219
switch (stage)
220
{
221
222
case MAIN_TT :
223
case EVASION_TT :
224
case QSEARCH_TT :
225
case PROBCUT_TT :
226
++stage;
227
return ttMove;
228
229
case CAPTURE_INIT :
230
case PROBCUT_INIT :
231
case QCAPTURE_INIT : {
232
MoveList<CAPTURES> ml(pos);
233
234
cur = endBadCaptures = moves;
235
endCur = endCaptures = score<CAPTURES>(ml);
236
237
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
238
++stage;
239
goto top;
240
}
241
242
case GOOD_CAPTURE :
243
if (select([&]() {
244
if (pos.see_ge(*cur, -cur->value / 18))
245
return true;
246
std::swap(*endBadCaptures++, *cur);
247
return false;
248
}))
249
return *(cur - 1);
250
251
++stage;
252
[[fallthrough]];
253
254
case QUIET_INIT :
255
if (!skipQuiets)
256
{
257
MoveList<QUIETS> ml(pos);
258
259
endCur = endGenerated = score<QUIETS>(ml);
260
261
partial_insertion_sort(cur, endCur, -3560 * depth);
262
}
263
264
++stage;
265
[[fallthrough]];
266
267
case GOOD_QUIET :
268
if (!skipQuiets && select([&]() { return cur->value > goodQuietThreshold; }))
269
return *(cur - 1);
270
271
// Prepare the pointers to loop over the bad captures
272
cur = moves;
273
endCur = endBadCaptures;
274
275
++stage;
276
[[fallthrough]];
277
278
case BAD_CAPTURE :
279
if (select([]() { return true; }))
280
return *(cur - 1);
281
282
// Prepare the pointers to loop over quiets again
283
cur = endCaptures;
284
endCur = endGenerated;
285
286
++stage;
287
[[fallthrough]];
288
289
case BAD_QUIET :
290
if (!skipQuiets)
291
return select([&]() { return cur->value <= goodQuietThreshold; });
292
293
return Move::none();
294
295
case EVASION_INIT : {
296
MoveList<EVASIONS> ml(pos);
297
298
cur = moves;
299
endCur = endGenerated = score<EVASIONS>(ml);
300
301
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
302
++stage;
303
[[fallthrough]];
304
}
305
306
case EVASION :
307
case QCAPTURE :
308
return select([]() { return true; });
309
310
case PROBCUT :
311
return select([&]() { return pos.see_ge(*cur, threshold); });
312
}
313
314
assert(false);
315
return Move::none(); // Silence warning
316
}
317
318
void MovePicker::skip_quiet_moves() { skipQuiets = true; }
319
320
} // namespace Stockfish
321
322