1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-05-01 01:03:09 +00:00

Join common code in the stages of next_move()

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.
This commit is contained in:
Joost VandeVondele 2018-03-19 00:56:19 +01:00 committed by Stéphane Nicolet
parent 1940485030
commit bd59560480
2 changed files with 89 additions and 112 deletions

View file

@ -25,12 +25,15 @@
namespace { namespace {
enum Stages { enum Stages {
MAIN_SEARCH, CAPTURES_INIT, GOOD_CAPTURES, KILLER0, KILLER1, COUNTERMOVE, QUIET_INIT, QUIET, BAD_CAPTURES, MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, KILLER0, KILLER1, COUNTERMOVE, QUIET_INIT, QUIET, BAD_CAPTURE,
EVASION, EVASIONS_INIT, ALL_EVASIONS, EVASION_TT, EVASION_INIT, EVASION,
PROBCUT, PROBCUT_CAPTURES_INIT, PROBCUT_CAPTURES, PROBCUT_TT, PROBCUT_INIT, PROBCUT,
QSEARCH, QCAPTURES_INIT, QCAPTURES, QCHECKS 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 // 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. // a given limit. The order of moves smaller than the limit is left unspecified.
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
@ -46,15 +49,6 @@ namespace {
} }
} }
// 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;
}
} // namespace } // namespace
@ -72,18 +66,19 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
assert(d > DEPTH_ZERO); assert(d > DEPTH_ZERO);
stage = pos.checkers() ? EVASION : MAIN_SEARCH; stage = pos.checkers() ? EVASION_TT : MAIN_TT;
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
stage += (ttMove == MOVE_NONE); stage += (ttMove == MOVE_NONE);
} }
/// MovePicker constructor for quiescence search /// MovePicker constructor for quiescence search
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const CapturePieceToHistory* cph, Square s) MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
: pos(p), mainHistory(mh), captureHistory(cph), recaptureSquare(s), depth(d) { const CapturePieceToHistory* cph, Square rs)
: pos(p), mainHistory(mh), captureHistory(cph), recaptureSquare(rs), depth(d) {
assert(d <= DEPTH_ZERO); assert(d <= DEPTH_ZERO);
stage = pos.checkers() ? EVASION : QSEARCH; stage = pos.checkers() ? EVASION_TT : QSEARCH_TT;
ttMove = ttm ttMove = ttm
&& pos.pseudo_legal(ttm) && pos.pseudo_legal(ttm)
&& (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) ? ttm : MOVE_NONE; && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) ? ttm : MOVE_NONE;
@ -97,7 +92,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece
assert(!pos.checkers()); assert(!pos.checkers());
stage = PROBCUT; stage = PROBCUT_TT;
ttMove = ttm ttMove = ttm
&& pos.pseudo_legal(ttm) && pos.pseudo_legal(ttm)
&& pos.capture(ttm) && pos.capture(ttm)
@ -105,9 +100,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece
stage += (ttMove == MOVE_NONE); stage += (ttMove == MOVE_NONE);
} }
/// score() assigns a numerical value to each move in a list, used for sorting. /// MovePicker::score() assigns a numerical value to each move in a list, used
/// Captures are ordered by Most Valuable Victim (MVV), preferring captures /// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring
/// with a good history. Quiets are ordered using the histories. /// captures with a good history. Quiets moves are ordered using the histories.
template<GenType Type> template<GenType Type>
void MovePicker::score() { void MovePicker::score() {
@ -134,57 +129,58 @@ void MovePicker::score() {
} }
} }
/// next_move() is the most important method of the MovePicker class. It returns /// MovePicker::select_move() returns the next move satisfying a predicate function
/// a new pseudo legal move every time it is called, until there are no more moves template<PickType T, typename Pred>
/// left. It picks the move with the biggest value from a list of generated moves Move MovePicker::select_move(Pred filter) {
/// taking care not to return the ttMove if it has already been searched.
while (cur < endMoves)
{
if (T == BEST_SCORE)
std::swap(*cur, *std::max_element(cur, endMoves));
move = *cur++;
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) { Move MovePicker::next_move(bool skipQuiets) {
Move move; top:
begin_switch:
switch (stage) { switch (stage) {
case MAIN_SEARCH: case MAIN_TT:
case EVASION: case EVASION_TT:
case QSEARCH: case QSEARCH_TT:
case PROBCUT: case PROBCUT_TT:
++stage; ++stage;
return ttMove; return ttMove;
case CAPTURES_INIT: case CAPTURE_INIT:
case PROBCUT_CAPTURES_INIT: case PROBCUT_INIT:
case QCAPTURES_INIT: case QCAPTURE_INIT:
endBadCaptures = cur = moves; endBadCaptures = cur = moves;
endMoves = generate<CAPTURES>(pos, cur); endMoves = generate<CAPTURES>(pos, cur);
score<CAPTURES>(); score<CAPTURES>();
++stage; ++stage;
goto top;
// Rebranch at the top of the switch case GOOD_CAPTURE:
goto begin_switch; 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
case GOOD_CAPTURES: true : (*endBadCaptures++ = move, false); }))
while (cur < endMoves) return move;
{
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;
// If the countermove is the same as a killer, skip it // If the countermove is the same as a killer, skip it
if ( refutations[0] == refutations[2] if ( refutations[0] == refutations[2]
|| refutations[1] == refutations[2]) || refutations[1] == refutations[2])
refutations[2] = MOVE_NONE; refutations[2] = MOVE_NONE;
++stage;
/* fallthrough */ /* fallthrough */
case KILLER0: case KILLER0:
@ -192,7 +188,7 @@ begin_switch:
case COUNTERMOVE: case COUNTERMOVE:
while (stage <= COUNTERMOVE) while (stage <= COUNTERMOVE)
{ {
move = refutations[ stage++ - KILLER0 ]; move = refutations[ stage++ - KILLER0];
if ( move != MOVE_NONE if ( move != MOVE_NONE
&& move != ttMove && move != ttMove
&& pos.pseudo_legal(move) && pos.pseudo_legal(move)
@ -210,78 +206,55 @@ begin_switch:
/* fallthrough */ /* fallthrough */
case QUIET: case QUIET:
if (!skipQuiets) if ( !skipQuiets
while (cur < endMoves) && select_move<NEXT>([&](){return move != refutations[0]
{ && move != refutations[1]
move = *cur++; && move != refutations[2];}))
if ( move != ttMove return move;
&& move != refutations[0]
&& move != refutations[1] // Point to beginning and end of bad captures
&& move != refutations[2]) cur = moves, endMoves = endBadCaptures;
return move;
}
++stage; ++stage;
cur = moves; // Point to beginning of bad captures
/* fallthrough */ /* fallthrough */
case BAD_CAPTURES: case BAD_CAPTURE:
if (cur < endBadCaptures) return select_move<NEXT>(Any);
return *cur++;
break;
case EVASIONS_INIT: case EVASION_INIT:
cur = moves; cur = moves;
endMoves = generate<EVASIONS>(pos, cur); endMoves = generate<EVASIONS>(pos, cur);
score<EVASIONS>(); score<EVASIONS>();
++stage; ++stage;
/* fallthrough */ /* fallthrough */
case ALL_EVASIONS: case EVASION:
while (cur < endMoves) return select_move<BEST_SCORE>(Any);
{
move = pick_best(cur++, endMoves);
if (move != ttMove)
return move;
}
break;
case PROBCUT_CAPTURES: case PROBCUT:
while (cur < endMoves) return select_move<BEST_SCORE>([&](){ return pos.see_ge(move, threshold); });
{
move = pick_best(cur++, endMoves);
if ( move != ttMove
&& pos.see_ge(move, threshold))
return move;
}
break;
case QCAPTURES: case QCAPTURE:
while (cur < endMoves) if (select_move<BEST_SCORE>([&](){ return depth > DEPTH_QS_RECAPTURES
{ || to_sq(move) == recaptureSquare; }))
move = pick_best(cur++, endMoves); return move;
if ( move != ttMove
&& (depth > DEPTH_QS_RECAPTURES || to_sq(move) == recaptureSquare)) // If we did not find any move and we do not try checks, we have finished
return move; if (depth != DEPTH_QS_CHECKS)
} return MOVE_NONE;
if (depth <= DEPTH_QS_NO_CHECKS)
break; ++stage;
/* fallthrough */
case QCHECK_INIT:
cur = moves; cur = moves;
endMoves = generate<QUIET_CHECKS>(pos, cur); endMoves = generate<QUIET_CHECKS>(pos, cur);
++stage; ++stage;
/* fallthrough */ /* fallthrough */
case QCHECKS: case QCHECK:
while (cur < endMoves) return select_move<NEXT>(Any);
{
move = *cur++;
if (move != ttMove)
return move;
}
break;
default:
assert(false);
} }
return MOVE_NONE; assert(false);
return MOVE_NONE; // Silence warning
} }

View file

@ -49,7 +49,7 @@ public:
void operator<<(int bonus) { void operator<<(int bonus) {
assert(abs(bonus) <= D); // Ensure range is [-W * D, W * D] 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;
@ -110,6 +110,8 @@ typedef Stats<PieceToHistory, W32, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHi
/// beta algorithm, MovePicker attempts to return the moves which are most likely /// beta algorithm, MovePicker attempts to return the moves which are most likely
/// to get a cut-off first. /// to get a cut-off first.
enum PickType { NEXT, BEST_SCORE };
class MovePicker { class MovePicker {
public: public:
MovePicker(const MovePicker&) = delete; MovePicker(const MovePicker&) = delete;
@ -120,6 +122,7 @@ public:
Move next_move(bool skipQuiets = false); Move next_move(bool skipQuiets = false);
private: private:
template<PickType T, typename Pred> Move select_move(Pred);
template<GenType> void score(); template<GenType> void score();
ExtMove* begin() { return cur; } ExtMove* begin() { return cur; }
ExtMove* end() { return endMoves; } ExtMove* end() { return endMoves; }
@ -131,6 +134,7 @@ private:
Move ttMove, refutations[3]; Move ttMove, refutations[3];
ExtMove *cur, *endMoves, *endBadCaptures; ExtMove *cur, *endMoves, *endBadCaptures;
int stage; int stage;
Move move;
Square recaptureSquare; Square recaptureSquare;
Value threshold; Value threshold;
Depth depth; Depth depth;