Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/stockfish
Path: blob/master/src/movepick.cpp
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
#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
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]) + 1024 * bool(pos.check_squares(pt) & to);
157
158
else if constexpr (Type == QUIETS)
159
{
160
// histories
161
m.value = 2 * (*mainHistory)[us][m.from_to()];
162
m.value += 2 * (*pawnHistory)[pawn_history_index(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
static constexpr int bonus[KING + 1] = {0, 0, 144, 144, 256, 517, 10000};
175
int v = threatByLesser[pt] & to ? -95 : 100 * bool(threatByLesser[pt] & from);
176
m.value += bonus[pt] * v;
177
178
179
if (ply < LOW_PLY_HISTORY_SIZE)
180
m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + ply);
181
}
182
183
else // Type == EVASIONS
184
{
185
if (pos.capture_stage(m))
186
m.value = PieceValue[capturedPiece] + (1 << 28);
187
else
188
{
189
m.value = (*mainHistory)[us][m.from_to()] + (*continuationHistory[0])[pc][to];
190
if (ply < LOW_PLY_HISTORY_SIZE)
191
m.value += (*lowPlyHistory)[ply][m.from_to()];
192
}
193
}
194
}
195
return it;
196
}
197
198
// Returns the next move satisfying a predicate function.
199
// This never returns the TT move, as it was emitted before.
200
template<typename Pred>
201
Move MovePicker::select(Pred filter) {
202
203
for (; cur < endCur; ++cur)
204
if (*cur != ttMove && filter())
205
return *cur++;
206
207
return Move::none();
208
}
209
210
// This is the most important method of the MovePicker class. We emit one
211
// new pseudo-legal move on every call until there are no more moves left,
212
// picking the move with the highest score from a list of generated moves.
213
Move MovePicker::next_move() {
214
215
constexpr int goodQuietThreshold = -14000;
216
top:
217
switch (stage)
218
{
219
220
case MAIN_TT :
221
case EVASION_TT :
222
case QSEARCH_TT :
223
case PROBCUT_TT :
224
++stage;
225
return ttMove;
226
227
case CAPTURE_INIT :
228
case PROBCUT_INIT :
229
case QCAPTURE_INIT : {
230
MoveList<CAPTURES> ml(pos);
231
232
cur = endBadCaptures = moves;
233
endCur = endCaptures = score<CAPTURES>(ml);
234
235
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
236
++stage;
237
goto top;
238
}
239
240
case GOOD_CAPTURE :
241
if (select([&]() {
242
if (pos.see_ge(*cur, -cur->value / 18))
243
return true;
244
std::swap(*endBadCaptures++, *cur);
245
return false;
246
}))
247
return *(cur - 1);
248
249
++stage;
250
[[fallthrough]];
251
252
case QUIET_INIT :
253
if (!skipQuiets)
254
{
255
MoveList<QUIETS> ml(pos);
256
257
endCur = endGenerated = score<QUIETS>(ml);
258
259
partial_insertion_sort(cur, endCur, -3560 * depth);
260
}
261
262
++stage;
263
[[fallthrough]];
264
265
case GOOD_QUIET :
266
if (!skipQuiets && select([&]() { return cur->value > goodQuietThreshold; }))
267
return *(cur - 1);
268
269
// Prepare the pointers to loop over the bad captures
270
cur = moves;
271
endCur = endBadCaptures;
272
273
++stage;
274
[[fallthrough]];
275
276
case BAD_CAPTURE :
277
if (select([]() { return true; }))
278
return *(cur - 1);
279
280
// Prepare the pointers to loop over quiets again
281
cur = endCaptures;
282
endCur = endGenerated;
283
284
++stage;
285
[[fallthrough]];
286
287
case BAD_QUIET :
288
if (!skipQuiets)
289
return select([&]() { return cur->value <= goodQuietThreshold; });
290
291
return Move::none();
292
293
case EVASION_INIT : {
294
MoveList<EVASIONS> ml(pos);
295
296
cur = moves;
297
endCur = endGenerated = score<EVASIONS>(ml);
298
299
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
300
++stage;
301
[[fallthrough]];
302
}
303
304
case EVASION :
305
case QCAPTURE :
306
return select([]() { return true; });
307
308
case PROBCUT :
309
return select([&]() { return pos.see_ge(*cur, threshold); });
310
}
311
312
assert(false);
313
return Move::none(); // Silence warning
314
}
315
316
void MovePicker::skip_quiet_moves() { skipQuiets = true; }
317
318
} // namespace Stockfish
319
320