Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/Stockfish
Path: blob/master/src/movepick.cpp
629 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
#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 SharedHistories* sh,
91
int pl) :
92
pos(p),
93
mainHistory(mh),
94
lowPlyHistory(lph),
95
captureHistory(cph),
96
continuationHistory(ch),
97
sharedHistory(sh),
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
stage = PROBCUT_TT + !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm));
119
}
120
121
// Assigns a numerical value to each move in a list, used for sorting.
122
// Captures are ordered by Most Valuable Victim (MVV), preferring captures
123
// with a good history. Quiets moves are ordered using the history tables.
124
template<GenType Type>
125
ExtMove* MovePicker::score(MoveList<Type>& ml) {
126
127
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
128
129
Color us = pos.side_to_move();
130
131
[[maybe_unused]] Bitboard threatByLesser[KING + 1];
132
if constexpr (Type == QUIETS)
133
{
134
threatByLesser[PAWN] = 0;
135
threatByLesser[KNIGHT] = threatByLesser[BISHOP] = pos.attacks_by<PAWN>(~us);
136
threatByLesser[ROOK] =
137
pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatByLesser[KNIGHT];
138
threatByLesser[QUEEN] = pos.attacks_by<ROOK>(~us) | threatByLesser[ROOK];
139
threatByLesser[KING] = pos.attacks_by<QUEEN>(~us) | threatByLesser[QUEEN];
140
}
141
142
ExtMove* it = cur;
143
for (auto move : ml)
144
{
145
ExtMove& m = *it++;
146
m = move;
147
148
const Square from = m.from_sq();
149
const Square to = m.to_sq();
150
const Piece pc = pos.moved_piece(m);
151
const PieceType pt = type_of(pc);
152
const Piece capturedPiece = pos.piece_on(to);
153
154
if constexpr (Type == CAPTURES)
155
m.value = (*captureHistory)[pc][to][type_of(capturedPiece)]
156
+ 7 * int(PieceValue[capturedPiece]);
157
158
else if constexpr (Type == QUIETS)
159
{
160
// histories
161
m.value = 2 * (*mainHistory)[us][m.raw()];
162
m.value += 2 * sharedHistory->pawn_entry(pos)[pc][to];
163
m.value += (*continuationHistory[0])[pc][to];
164
m.value += (*continuationHistory[1])[pc][to];
165
m.value += (*continuationHistory[2])[pc][to];
166
m.value += (*continuationHistory[3])[pc][to];
167
m.value += (*continuationHistory[5])[pc][to];
168
169
// bonus for checks
170
m.value += (bool(pos.check_squares(pt) & to) && pos.see_ge(m, -75)) * 16384;
171
172
// penalty for moving to a square threatened by a lesser piece
173
// or bonus for escaping an attack by a lesser piece.
174
int v = threatByLesser[pt] & to ? -19 : 20 * bool(threatByLesser[pt] & from);
175
m.value += PieceValue[pt] * v;
176
177
178
if (ply < LOW_PLY_HISTORY_SIZE)
179
m.value += 8 * (*lowPlyHistory)[ply][m.raw()] / (1 + ply);
180
}
181
182
else // Type == EVASIONS
183
{
184
if (pos.capture_stage(m))
185
m.value = PieceValue[capturedPiece] + (1 << 28);
186
else
187
m.value = (*mainHistory)[us][m.raw()] + (*continuationHistory[0])[pc][to];
188
}
189
}
190
return it;
191
}
192
193
// Returns the next move satisfying a predicate function.
194
// This never returns the TT move, as it was emitted before.
195
template<typename Pred>
196
Move MovePicker::select(Pred filter) {
197
198
for (; cur < endCur; ++cur)
199
if (*cur != ttMove && filter())
200
return *cur++;
201
202
return Move::none();
203
}
204
205
// This is the most important method of the MovePicker class. We emit one
206
// new pseudo-legal move on every call until there are no more moves left,
207
// picking the move with the highest score from a list of generated moves.
208
Move MovePicker::next_move() {
209
210
constexpr int goodQuietThreshold = -14000;
211
top:
212
switch (stage)
213
{
214
215
case MAIN_TT :
216
case EVASION_TT :
217
case QSEARCH_TT :
218
case PROBCUT_TT :
219
++stage;
220
return ttMove;
221
222
case CAPTURE_INIT :
223
case PROBCUT_INIT :
224
case QCAPTURE_INIT : {
225
MoveList<CAPTURES> ml(pos);
226
227
cur = endBadCaptures = moves;
228
endCur = endCaptures = score<CAPTURES>(ml);
229
230
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
231
++stage;
232
goto top;
233
}
234
235
case GOOD_CAPTURE :
236
if (select([&]() {
237
if (pos.see_ge(*cur, -cur->value / 18))
238
return true;
239
std::swap(*endBadCaptures++, *cur);
240
return false;
241
}))
242
return *(cur - 1);
243
244
++stage;
245
[[fallthrough]];
246
247
case QUIET_INIT :
248
if (!skipQuiets)
249
{
250
MoveList<QUIETS> ml(pos);
251
252
endCur = endGenerated = score<QUIETS>(ml);
253
254
partial_insertion_sort(cur, endCur, -3560 * depth);
255
}
256
257
++stage;
258
[[fallthrough]];
259
260
case GOOD_QUIET :
261
if (!skipQuiets && select([&]() { return cur->value > goodQuietThreshold; }))
262
return *(cur - 1);
263
264
// Prepare the pointers to loop over the bad captures
265
cur = moves;
266
endCur = endBadCaptures;
267
268
++stage;
269
[[fallthrough]];
270
271
case BAD_CAPTURE :
272
if (select([]() { return true; }))
273
return *(cur - 1);
274
275
// Prepare the pointers to loop over quiets again
276
cur = endCaptures;
277
endCur = endGenerated;
278
279
++stage;
280
[[fallthrough]];
281
282
case BAD_QUIET :
283
if (!skipQuiets)
284
return select([&]() { return cur->value <= goodQuietThreshold; });
285
286
return Move::none();
287
288
case EVASION_INIT : {
289
MoveList<EVASIONS> ml(pos);
290
291
cur = moves;
292
endCur = endGenerated = score<EVASIONS>(ml);
293
294
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
295
++stage;
296
[[fallthrough]];
297
}
298
299
case EVASION :
300
case QCAPTURE :
301
return select([]() { return true; });
302
303
case PROBCUT :
304
return select([&]() { return pos.see_ge(*cur, threshold); });
305
}
306
307
assert(false);
308
return Move::none(); // Silence warning
309
}
310
311
void MovePicker::skip_quiet_moves() { skipQuiets = true; }
312
313
} // namespace Stockfish
314
315