1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-30 16:53: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 {
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) {
@ -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
@ -72,18 +66,19 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
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;
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) {
assert(d <= DEPTH_ZERO);
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;
@ -97,7 +92,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece
assert(!pos.checkers());
stage = PROBCUT;
stage = PROBCUT_TT;
ttMove = ttm
&& pos.pseudo_legal(ttm)
&& pos.capture(ttm)
@ -105,9 +100,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece
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() {
@ -134,57 +129,58 @@ 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) {
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 move;
begin_switch:
top:
switch (stage) {
case MAIN_SEARCH:
case EVASION:
case QSEARCH:
case PROBCUT:
case MAIN_TT:
case EVASION_TT:
case QSEARCH_TT:
case PROBCUT_TT:
++stage;
return ttMove;
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;
goto top;
// 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])
refutations[2] = MOVE_NONE;
refutations[2] = MOVE_NONE;
++stage;
/* fallthrough */
case KILLER0:
@ -192,7 +188,7 @@ begin_switch:
case COUNTERMOVE:
while (stage <= COUNTERMOVE)
{
move = refutations[ stage++ - KILLER0 ];
move = refutations[ stage++ - KILLER0];
if ( move != MOVE_NONE
&& move != ttMove
&& pos.pseudo_legal(move)
@ -210,78 +206,55 @@ begin_switch:
/* 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;
++stage;
cur = moves; // Point to beginning of bad captures
/* fallthrough */
case BAD_CAPTURES:
if (cur < endBadCaptures)
return *cur++;
break;
case BAD_CAPTURE:
return select_move<NEXT>(Any);
case EVASIONS_INIT:
case EVASION_INIT:
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 */
case QCHECKS:
while (cur < endMoves)
{
move = *cur++;
if (move != ttMove)
return move;
}
break;
default:
assert(false);
case QCHECK:
return select_move<NEXT>(Any);
}
return MOVE_NONE;
assert(false);
return MOVE_NONE; // Silence warning
}

View file

@ -49,7 +49,7 @@ public:
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;
@ -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
/// to get a cut-off first.
enum PickType { NEXT, BEST_SCORE };
class MovePicker {
public:
MovePicker(const MovePicker&) = delete;
@ -120,6 +122,7 @@ public:
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; }
@ -131,6 +134,7 @@ private:
Move ttMove, refutations[3];
ExtMove *cur, *endMoves, *endBadCaptures;
int stage;
Move move;
Square recaptureSquare;
Value threshold;
Depth depth;