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:
parent
1940485030
commit
bd59560480
2 changed files with 89 additions and 112 deletions
195
src/movepick.cpp
195
src/movepick.cpp
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue