mirror of
https://github.com/sockspls/badfish
synced 2025-05-01 09:13:08 +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
181
src/movepick.cpp
181
src/movepick.cpp
|
@ -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)
|
|
||||||
{
|
|
||||||
move = pick_best(cur++, endMoves);
|
|
||||||
if (move != ttMove)
|
|
||||||
{
|
|
||||||
if (pos.see_ge(move, Value(-55 * (cur-1)->value / 1024)))
|
|
||||||
return move;
|
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:
|
||||||
|
@ -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 = *cur++;
|
|
||||||
if ( move != ttMove
|
|
||||||
&& move != refutations[0]
|
|
||||||
&& move != refutations[1]
|
&& move != refutations[1]
|
||||||
&& move != refutations[2])
|
&& move != refutations[2];}))
|
||||||
return move;
|
return move;
|
||||||
}
|
|
||||||
|
// Point to beginning and end of bad captures
|
||||||
|
cur = moves, endMoves = endBadCaptures;
|
||||||
++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);
|
|
||||||
if ( move != ttMove
|
|
||||||
&& (depth > DEPTH_QS_RECAPTURES || to_sq(move) == recaptureSquare))
|
|
||||||
return move;
|
return move;
|
||||||
}
|
|
||||||
if (depth <= DEPTH_QS_NO_CHECKS)
|
// If we did not find any move and we do not try checks, we have finished
|
||||||
break;
|
if (depth != DEPTH_QS_CHECKS)
|
||||||
|
return MOVE_NONE;
|
||||||
|
|
||||||
|
++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);
|
assert(false);
|
||||||
}
|
return MOVE_NONE; // Silence warning
|
||||||
|
|
||||||
return MOVE_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue