summary |
shortlog |
log |
commit | commitdiff |
tree
raw |
patch |
inline | side by side (from parent 1:
1940485)
Rewrite the MovePicker class using lambda expressions for move filtering.
Includes code style changes by @mcostalba.
Verified for speed, passed STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 43191 W: 9391 L: 9312 D: 24488
http://tests.stockfishchess.org/tests/view/
5a99b9df0ebc590297cc8f04
This rewrite of MovePicker.cpp seems to trigger less random crashes on Ryzen
machines than the version in previous master (reported by Bojun Guo).
Closes https://github.com/official-stockfish/Stockfish/pull/1454
No functional change.
namespace {
enum Stages {
namespace {
enum Stages {
- MAIN_SEARCH, CAPTURES_INIT, GOOD_CAPTURES, KILLER0, KILLER1, COUNTERMOVE, QUIET_INIT, QUIET, BAD_CAPTURES,
- EVASION, EVASIONS_INIT, ALL_EVASIONS,
- PROBCUT, PROBCUT_CAPTURES_INIT, PROBCUT_CAPTURES,
- QSEARCH, QCAPTURES_INIT, QCAPTURES, QCHECKS
+ MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, KILLER0, KILLER1, COUNTERMOVE, QUIET_INIT, QUIET, BAD_CAPTURE,
+ EVASION_TT, EVASION_INIT, EVASION,
+ PROBCUT_TT, PROBCUT_INIT, PROBCUT,
+ QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK
+ // Helper filter used with select_move()
+ const auto Any = [](){ return true; };
+
// partial_insertion_sort() sorts moves in descending order up to and including
// a given limit. The order of moves smaller than the limit is left unspecified.
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
// partial_insertion_sort() sorts moves in descending order up to and including
// a given limit. The order of moves smaller than the limit is left unspecified.
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
- // pick_best() finds the best move in the range (begin, end) and moves it to
- // the front. It's faster than sorting all the moves in advance when there
- // are few moves, e.g., the possible captures.
- Move pick_best(ExtMove* begin, ExtMove* end) {
-
- std::swap(*begin, *std::max_element(begin, end));
- return *begin;
- }
-
- stage = pos.checkers() ? EVASION : MAIN_SEARCH;
+ stage = pos.checkers() ? EVASION_TT : MAIN_TT;
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
stage += (ttMove == MOVE_NONE);
}
/// MovePicker constructor for quiescence search
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
stage += (ttMove == MOVE_NONE);
}
/// MovePicker constructor for quiescence search
-MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const CapturePieceToHistory* cph, Square s)
- : pos(p), mainHistory(mh), captureHistory(cph), recaptureSquare(s), depth(d) {
+MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
+ const CapturePieceToHistory* cph, Square rs)
+ : pos(p), mainHistory(mh), captureHistory(cph), recaptureSquare(rs), depth(d) {
- stage = pos.checkers() ? EVASION : QSEARCH;
+ stage = pos.checkers() ? EVASION_TT : QSEARCH_TT;
ttMove = ttm
&& pos.pseudo_legal(ttm)
&& (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) ? ttm : MOVE_NONE;
ttMove = ttm
&& pos.pseudo_legal(ttm)
&& (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) ? ttm : MOVE_NONE;
ttMove = ttm
&& pos.pseudo_legal(ttm)
&& pos.capture(ttm)
ttMove = ttm
&& pos.pseudo_legal(ttm)
&& pos.capture(ttm)
stage += (ttMove == MOVE_NONE);
}
stage += (ttMove == MOVE_NONE);
}
-/// score() assigns a numerical value to each move in a list, used for sorting.
-/// Captures are ordered by Most Valuable Victim (MVV), preferring captures
-/// with a good history. Quiets are ordered using the histories.
+/// MovePicker::score() assigns a numerical value to each move in a list, used
+/// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring
+/// captures with a good history. Quiets moves are ordered using the histories.
template<GenType Type>
void MovePicker::score() {
template<GenType Type>
void MovePicker::score() {
-/// next_move() is the most important method of the MovePicker class. It returns
-/// a new pseudo legal move every time it is called, until there are no more moves
-/// left. It picks the move with the biggest value from a list of generated moves
-/// taking care not to return the ttMove if it has already been searched.
+/// MovePicker::select_move() returns the next move satisfying a predicate function
+template<PickType T, typename Pred>
+Move MovePicker::select_move(Pred filter) {
-Move MovePicker::next_move(bool skipQuiets) {
+ while (cur < endMoves)
+ {
+ if (T == BEST_SCORE)
+ std::swap(*cur, *std::max_element(cur, endMoves));
+ if (move != ttMove && filter())
+ return move;
+ }
+ return move = MOVE_NONE;
+}
+
+/// MovePicker::next_move() is the most important method of the MovePicker class. It
+/// returns a new pseudo legal move every time it is called, until there are no more
+/// moves left. It picks the move with the highest score from a list of generated moves.
+Move MovePicker::next_move(bool skipQuiets) {
- case MAIN_SEARCH:
- case EVASION:
- case QSEARCH:
- case PROBCUT:
+ case MAIN_TT:
+ case EVASION_TT:
+ case QSEARCH_TT:
+ case PROBCUT_TT:
- case CAPTURES_INIT:
- case PROBCUT_CAPTURES_INIT:
- case QCAPTURES_INIT:
+ case CAPTURE_INIT:
+ case PROBCUT_INIT:
+ case QCAPTURE_INIT:
endBadCaptures = cur = moves;
endMoves = generate<CAPTURES>(pos, cur);
score<CAPTURES>();
++stage;
endBadCaptures = cur = moves;
endMoves = generate<CAPTURES>(pos, cur);
score<CAPTURES>();
++stage;
- // Rebranch at the top of the switch
- goto begin_switch;
-
- case GOOD_CAPTURES:
- while (cur < endMoves)
- {
- move = pick_best(cur++, endMoves);
- if (move != ttMove)
- {
- if (pos.see_ge(move, Value(-55 * (cur-1)->value / 1024)))
- return move;
-
- // Losing capture, move it to the beginning of the array
- *endBadCaptures++ = move;
- }
- }
- ++stage;
+ case GOOD_CAPTURE:
+ if (select_move<BEST_SCORE>([&](){ return pos.see_ge(move, Value(-55 * (cur-1)->value / 1024)) ?
+ // Move losing capture to endBadCaptures to be tried later
+ true : (*endBadCaptures++ = move, false); }))
+ return move;
// If the countermove is the same as a killer, skip it
if ( refutations[0] == refutations[2]
|| refutations[1] == refutations[2])
// If the countermove is the same as a killer, skip it
if ( refutations[0] == refutations[2]
|| refutations[1] == refutations[2])
- refutations[2] = MOVE_NONE;
-
+ refutations[2] = MOVE_NONE;
+ ++stage;
/* fallthrough */
case KILLER0:
/* fallthrough */
case KILLER0:
case COUNTERMOVE:
while (stage <= COUNTERMOVE)
{
case COUNTERMOVE:
while (stage <= COUNTERMOVE)
{
- move = refutations[ stage++ - KILLER0 ];
+ move = refutations[ stage++ - KILLER0];
if ( move != MOVE_NONE
&& move != ttMove
&& pos.pseudo_legal(move)
if ( move != MOVE_NONE
&& move != ttMove
&& pos.pseudo_legal(move)
/* fallthrough */
case QUIET:
/* fallthrough */
case QUIET:
- if (!skipQuiets)
- while (cur < endMoves)
- {
- move = *cur++;
- if ( move != ttMove
- && move != refutations[0]
- && move != refutations[1]
- && move != refutations[2])
- return move;
- }
+ if ( !skipQuiets
+ && select_move<NEXT>([&](){return move != refutations[0]
+ && move != refutations[1]
+ && move != refutations[2];}))
+ return move;
+
+ // Point to beginning and end of bad captures
+ cur = moves, endMoves = endBadCaptures;
- cur = moves; // Point to beginning of bad captures
- case BAD_CAPTURES:
- if (cur < endBadCaptures)
- return *cur++;
- break;
+ case BAD_CAPTURE:
+ return select_move<NEXT>(Any);
cur = moves;
endMoves = generate<EVASIONS>(pos, cur);
score<EVASIONS>();
++stage;
/* fallthrough */
cur = moves;
endMoves = generate<EVASIONS>(pos, cur);
score<EVASIONS>();
++stage;
/* fallthrough */
- case ALL_EVASIONS:
- while (cur < endMoves)
- {
- move = pick_best(cur++, endMoves);
- if (move != ttMove)
- return move;
- }
- break;
+ case EVASION:
+ return select_move<BEST_SCORE>(Any);
- case PROBCUT_CAPTURES:
- while (cur < endMoves)
- {
- move = pick_best(cur++, endMoves);
- if ( move != ttMove
- && pos.see_ge(move, threshold))
- return move;
- }
- break;
+ case PROBCUT:
+ return select_move<BEST_SCORE>([&](){ return pos.see_ge(move, threshold); });
- case QCAPTURES:
- while (cur < endMoves)
- {
- move = pick_best(cur++, endMoves);
- if ( move != ttMove
- && (depth > DEPTH_QS_RECAPTURES || to_sq(move) == recaptureSquare))
- return move;
- }
- if (depth <= DEPTH_QS_NO_CHECKS)
- break;
+ case QCAPTURE:
+ if (select_move<BEST_SCORE>([&](){ return depth > DEPTH_QS_RECAPTURES
+ || to_sq(move) == recaptureSquare; }))
+ return move;
+
+ // If we did not find any move and we do not try checks, we have finished
+ if (depth != DEPTH_QS_CHECKS)
+ return MOVE_NONE;
+
+ ++stage;
+ /* fallthrough */
+
+ case QCHECK_INIT:
cur = moves;
endMoves = generate<QUIET_CHECKS>(pos, cur);
++stage;
/* fallthrough */
cur = moves;
endMoves = generate<QUIET_CHECKS>(pos, cur);
++stage;
/* fallthrough */
- case QCHECKS:
- while (cur < endMoves)
- {
- move = *cur++;
- if (move != ttMove)
- return move;
- }
- break;
-
- default:
- assert(false);
+ case QCHECK:
+ return select_move<NEXT>(Any);
+ assert(false);
+ return MOVE_NONE; // Silence warning
void operator<<(int bonus) {
assert(abs(bonus) <= D); // Ensure range is [-W * D, W * D]
void operator<<(int bonus) {
assert(abs(bonus) <= D); // Ensure range is [-W * D, W * D]
- assert(abs(W * D) < std::numeric_limits<T>::max()); // Ensure we don't overflow
+ assert(W * D < std::numeric_limits<T>::max()); // Ensure we don't overflow
entry += bonus * W - entry * abs(bonus) / D;
entry += bonus * W - entry * abs(bonus) / D;
/// beta algorithm, MovePicker attempts to return the moves which are most likely
/// to get a cut-off first.
/// beta algorithm, MovePicker attempts to return the moves which are most likely
/// to get a cut-off first.
+enum PickType { NEXT, BEST_SCORE };
+
class MovePicker {
public:
MovePicker(const MovePicker&) = delete;
class MovePicker {
public:
MovePicker(const MovePicker&) = delete;
Move next_move(bool skipQuiets = false);
private:
Move next_move(bool skipQuiets = false);
private:
+ template<PickType T, typename Pred> Move select_move(Pred);
template<GenType> void score();
ExtMove* begin() { return cur; }
ExtMove* end() { return endMoves; }
template<GenType> void score();
ExtMove* begin() { return cur; }
ExtMove* end() { return endMoves; }
Move ttMove, refutations[3];
ExtMove *cur, *endMoves, *endBadCaptures;
int stage;
Move ttMove, refutations[3];
ExtMove *cur, *endMoves, *endBadCaptures;
int stage;
Square recaptureSquare;
Value threshold;
Depth depth;
Square recaptureSquare;
Value threshold;
Depth depth;