/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Time.init(Limits, us, rootPos.game_ply());
TT.new_search();
+ Eval::verify_NNUE();
+
if (rootMoves.empty())
{
rootMoves.emplace_back(MOVE_NONE);
Thread* bestThread = this;
- if (int(Options["MultiPV"]) == 1 &&
- !Limits.depth &&
- !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) &&
- rootMoves[0].pv[0] != MOVE_NONE)
+ if ( int(Options["MultiPV"]) == 1
+ && !Limits.depth
+ && !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"]))
+ && rootMoves[0].pv[0] != MOVE_NONE)
bestThread = Threads.get_best_thread();
bestPreviousScore = bestThread->rootMoves[0].score;
Key posKey;
Move ttMove, move, excludedMove, bestMove;
Depth extension, newDepth;
- Value bestValue, value, ttValue, eval, maxValue;
+ Value bestValue, value, ttValue, eval, maxValue, probcutBeta;
bool ttHit, ttPv, formerPv, givesCheck, improving, didLMR, priorCapture;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning,
ttCapture, singularQuietLMR;
ttPv = PvNode || (ttHit && tte->is_pv());
formerPv = ttPv && !PvNode;
- if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !priorCapture && is_ok((ss-1)->currentMove))
+ if ( ttPv
+ && depth > 12
+ && ss->ply - 1 < MAX_LPH
+ && !priorCapture
+ && is_ok((ss-1)->currentMove))
thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5);
// thisThread->ttHitAverage can be used to approximate the running average of ttHit
// Step 8. Futility pruning: child node (~50 Elo)
if ( !PvNode
- && depth < 6
+ && depth < 8
&& eval - futility_margin(depth, improving) >= beta
&& eval < VALUE_KNOWN_WIN) // Do not return unproven wins
return eval;
}
}
+ probcutBeta = beta + 176 - 49 * improving;
+
// Step 10. ProbCut (~10 Elo)
// If we have a good enough capture and a reduced search returns a value
// much above beta, we can (almost) safely prune the previous move.
if ( !PvNode
&& depth > 4
- && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY)
+ && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY
+ && !( ttHit
+ && tte->depth() >= depth - 3
+ && ttValue != VALUE_NONE
+ && ttValue < probcutBeta))
{
- Value raisedBeta = beta + 176 - 49 * improving;
- assert(raisedBeta < VALUE_INFINITE);
- MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory);
+ if ( ttHit
+ && tte->depth() >= depth - 3
+ && ttValue != VALUE_NONE
+ && ttValue >= probcutBeta
+ && ttMove
+ && pos.capture_or_promotion(ttMove))
+ return probcutBeta;
+
+ assert(probcutBeta < VALUE_INFINITE);
+ MovePicker mp(pos, ttMove, probcutBeta - ss->staticEval, &captureHistory);
int probCutCount = 0;
while ( (move = mp.next_move()) != MOVE_NONE
- && probCutCount < 2 + 2 * cutNode
- && !( move == ttMove
- && tte->depth() >= depth - 4
- && ttValue < raisedBeta))
+ && probCutCount < 2 + 2 * cutNode)
if (move != excludedMove && pos.legal(move))
{
assert(pos.capture_or_promotion(move));
pos.do_move(move, st);
// Perform a preliminary qsearch to verify that the move holds
- value = -qsearch<NonPV>(pos, ss+1, -raisedBeta, -raisedBeta+1);
+ value = -qsearch<NonPV>(pos, ss+1, -probcutBeta, -probcutBeta+1);
// If the qsearch held, perform the regular search
- if (value >= raisedBeta)
- value = -search<NonPV>(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4, !cutNode);
+ if (value >= probcutBeta)
+ value = -search<NonPV>(pos, ss+1, -probcutBeta, -probcutBeta+1, depth - 4, !cutNode);
pos.undo_move(move);
- if (value >= raisedBeta)
+ if (value >= probcutBeta)
+ {
+ if ( !(ttHit
+ && tte->depth() >= depth - 3
+ && ttValue != VALUE_NONE))
+ tte->save(posKey, value_to_tt(value, ss->ply), ttPv,
+ BOUND_LOWER,
+ depth - 3, move, ss->staticEval);
return value;
+ }
}
}
thisThread->rootMoves.begin() + thisThread->pvLast, move))
continue;
+ // Check for legality
+ if (!rootNode && !pos.legal(move))
+ continue;
+
ss->moveCount = ++moveCount;
if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000)
continue;
// Futility pruning: parent node (~5 Elo)
- if ( lmrDepth < 6
+ if ( lmrDepth < 8
&& !ss->inCheck
&& ss->staticEval + 284 + 188 * lmrDepth <= alpha
&& (*contHist[0])[movedPiece][to_sq(move)]
// search of (alpha-s, beta-s), and just one fails high on (alpha, beta),
// then that move is singular and should be extended. To verify this we do
// a reduced search on all the other moves but the ttMove and if the
- // result is lower than ttValue minus a margin then we will extend the ttMove.
+ // result is lower than ttValue minus a margin, then we will extend the ttMove.
if ( depth >= 6
&& move == ttMove
&& !rootNode
// Speculative prefetch as early as possible
prefetch(TT.first_entry(pos.key_after(move)));
- // Check for legality just before making the move
- if (!rootNode && !pos.legal(move))
- {
- ss->moveCount = --moveCount;
- continue;
- }
-
// Update the current move (this must be done after singular extension search)
ss->currentMove = move;
ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
{
Depth r = reduction(improving, depth, moveCount);
+ // Decrease reduction at non-check cut nodes for second move at low depths
+ if ( cutNode
+ && depth <= 10
+ && moveCount <= 2
+ && !ss->inCheck)
+ r--;
+
// Decrease reduction if the ttHit running average is large
if (thisThread->ttHitAverage > 473 * TtHitAverageResolution * TtHitAverageWindow / 1024)
r--;