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

Cleanup and reorder in qsearch

This patch is a simplification / code normalisation in qsearch.

Adds steps in comments the same way we have in search;

Makes a separate "pruning" stage instead of heuristics randomly being spread over qsearch code;
Reorders pruning heuristics from least taxing ones to more taxing ones;
Removes repeated check for best value not being mated, instead uses 1 check - thus removes some lines of code.
Moves prefetch and move setup after pruning - makes no sense to do them if move will actually get pruned.

Passed non-regression test:
https://tests.stockfishchess.org/tests/view/63dd2c5ff9a50a69252c1413
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 113504 W: 29898 L: 29770 D: 53836
Ptnml(0-2): 287, 11861, 32327, 11991, 286

https://github.com/official-stockfish/Stockfish/pull/4382

Non-functional change.
This commit is contained in:
Michael Chaly 2023-02-04 23:46:44 +03:00 committed by Joost VandeVondele
parent d5817a5896
commit 8f843633db

View file

@ -1427,6 +1427,7 @@ moves_loop: // When in check, search starts here
bool pvHit, givesCheck, capture; bool pvHit, givesCheck, capture;
int moveCount; int moveCount;
// Step 1. Initialize node
if (PvNode) if (PvNode)
{ {
(ss+1)->pv = pv; (ss+1)->pv = pv;
@ -1438,7 +1439,7 @@ moves_loop: // When in check, search starts here
ss->inCheck = pos.checkers(); ss->inCheck = pos.checkers();
moveCount = 0; moveCount = 0;
// Check for an immediate draw or maximum ply reached // Step 2. Check for an immediate draw or maximum ply reached
if ( pos.is_draw(ss->ply) if ( pos.is_draw(ss->ply)
|| ss->ply >= MAX_PLY) || ss->ply >= MAX_PLY)
return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : VALUE_DRAW; return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : VALUE_DRAW;
@ -1450,13 +1451,14 @@ moves_loop: // When in check, search starts here
// only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS.
ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS
: DEPTH_QS_NO_CHECKS; : DEPTH_QS_NO_CHECKS;
// Transposition table lookup // Step 3. Transposition table lookup
posKey = pos.key(); posKey = pos.key();
tte = TT.probe(posKey, ss->ttHit); tte = TT.probe(posKey, ss->ttHit);
ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
ttMove = ss->ttHit ? tte->move() : MOVE_NONE; ttMove = ss->ttHit ? tte->move() : MOVE_NONE;
pvHit = ss->ttHit && tte->is_pv(); pvHit = ss->ttHit && tte->is_pv();
// At non-PV nodes we check for an early TT cutoff
if ( !PvNode if ( !PvNode
&& ss->ttHit && ss->ttHit
&& tte->depth() >= ttDepth && tte->depth() >= ttDepth
@ -1464,7 +1466,7 @@ moves_loop: // When in check, search starts here
&& (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER)))
return ttValue; return ttValue;
// Evaluate the position statically // Step 4. Static evaluation of the position
if (ss->inCheck) if (ss->inCheck)
{ {
ss->staticEval = VALUE_NONE; ss->staticEval = VALUE_NONE;
@ -1522,7 +1524,8 @@ moves_loop: // When in check, search starts here
int quietCheckEvasions = 0; int quietCheckEvasions = 0;
// Loop through the moves until no moves remain or a beta cutoff occurs // Step 5. Loop through all pseudo-legal moves until no moves remain
// or a beta cutoff occurs.
while ((move = mp.next_move()) != MOVE_NONE) while ((move = mp.next_move()) != MOVE_NONE)
{ {
assert(is_ok(move)); assert(is_ok(move));
@ -1536,9 +1539,11 @@ moves_loop: // When in check, search starts here
moveCount++; moveCount++;
// Step 6. Pruning.
if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY)
{
// Futility pruning and moveCount pruning (~10 Elo) // Futility pruning and moveCount pruning (~10 Elo)
if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY if ( !givesCheck
&& !givesCheck
&& to_sq(move) != prevSq && to_sq(move) != prevSq
&& futilityBase > -VALUE_KNOWN_WIN && futilityBase > -VALUE_KNOWN_WIN
&& type_of(move) != PROMOTION) && type_of(move) != PROMOTION)
@ -1561,43 +1566,43 @@ moves_loop: // When in check, search starts here
} }
} }
// Do not search moves with bad enough SEE values (~5 Elo) // We prune after 2nd quiet check evasion where being 'in check' is implicitly checked through the counter
if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY // and being a 'quiet' apart from being a tt move is assumed after an increment because captures are pushed ahead.
&& !pos.see_ge(move, Value(-108))) if (quietCheckEvasions > 1)
break;
// Continuation history based pruning (~3 Elo)
if ( !capture
&& (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0
&& (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0)
continue; continue;
// Do not search moves with bad enough SEE values (~5 Elo)
if (!pos.see_ge(move, Value(-108)))
continue;
}
// Speculative prefetch as early as possible // Speculative prefetch as early as possible
prefetch(TT.first_entry(pos.key_after(move))); prefetch(TT.first_entry(pos.key_after(move)));
// Update the current move
ss->currentMove = move; ss->currentMove = move;
ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
[capture] [capture]
[pos.moved_piece(move)] [pos.moved_piece(move)]
[to_sq(move)]; [to_sq(move)];
// Continuation history based pruning (~3 Elo)
if ( !capture
&& bestValue > VALUE_TB_LOSS_IN_MAX_PLY
&& (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0
&& (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0)
continue;
// We prune after 2nd quiet check evasion where being 'in check' is implicitly checked through the counter
// and being a 'quiet' apart from being a tt move is assumed after an increment because captures are pushed ahead.
if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY
&& quietCheckEvasions > 1)
break;
quietCheckEvasions += !capture && ss->inCheck; quietCheckEvasions += !capture && ss->inCheck;
// Make and search the move // Step 7. Make and search the move
pos.do_move(move, st, givesCheck); pos.do_move(move, st, givesCheck);
value = -qsearch<nodeType>(pos, ss+1, -beta, -alpha, depth - 1); value = -qsearch<nodeType>(pos, ss+1, -beta, -alpha, depth - 1);
pos.undo_move(move); pos.undo_move(move);
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
// Check for a new best move // Step 8. Check for a new best move
if (value > bestValue) if (value > bestValue)
{ {
bestValue = value; bestValue = value;
@ -1617,6 +1622,7 @@ moves_loop: // When in check, search starts here
} }
} }
// Step 9. Check for mate
// All legal moves have been searched. A special case: if we're in check // All legal moves have been searched. A special case: if we're in check
// and no legal moves were found, it is checkmate. // and no legal moves were found, it is checkmate.
if (ss->inCheck && bestValue == -VALUE_INFINITE) if (ss->inCheck && bestValue == -VALUE_INFINITE)