/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
+ Copyright (C) 2004-2022 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
// Reductions lookup table, initialized at startup
int Reductions[MAX_MOVES]; // [depth or moveNumber]
- Depth reduction(bool i, Depth d, int mn, bool rangeReduction, Value delta, Value rootDelta) {
+ Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) {
int r = Reductions[d] * Reductions[mn];
- return (r + 1358 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 904) + rangeReduction;
+ return (r + 1358 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 904);
}
constexpr int futility_move_count(bool improving, Depth depth) {
doubleExtensionAverage[WHITE].set(0, 100); // initialize the running average at 0%
doubleExtensionAverage[BLACK].set(0, 100); // initialize the running average at 0%
+ complexityAverage.set(232, 1);
nodesLastExplosive = nodes;
nodesLastNormal = nodes;
double reduction = (1.47 + mainThread->previousTimeReduction) / (2.32 * timeReduction);
double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth)
* totBestMoveChanges / Threads.size();
- double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability;
+ int complexity = mainThread->complexityAverage.value();
+ double complexPosition = std::clamp(1.0 + (complexity - 232) / 1750.0, 0.5, 1.5);
+
+ double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition;
// Cap used time in case of a single legal move for a better viewer experience in tournaments
// yielding correct scores and sufficiently fast moves.
if ( ss->ply > 10
&& search_explosion(thisThread) == MUST_CALM_DOWN
&& depth > (ss-1)->depth)
- depth = (ss-1)->depth;
+ depth = (ss-1)->depth;
constexpr bool PvNode = nodeType != NonPV;
constexpr bool rootNode = nodeType == Root;
bool givesCheck, improving, didLMR, priorCapture;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture;
Piece movedPiece;
- int moveCount, captureCount, quietCount, bestMoveCount, improvement;
+ int moveCount, captureCount, quietCount, bestMoveCount, improvement, complexity;
// Step 1. Initialize node
ss->inCheck = pos.checkers();
ss->staticEval = eval = VALUE_NONE;
improving = false;
improvement = 0;
+ complexity = 0;
goto moves_loop;
}
else if (ss->ttHit)
: 200;
improving = improvement > 0;
+ complexity = abs(ss->staticEval - (us == WHITE ? eg_value(pos.psq_score()) : -eg_value(pos.psq_score())));
+
+ thisThread->complexityAverage.update(complexity);
// Step 7. Futility pruning: child node (~25 Elo).
// The depth condition is important for mate finding.
&& (ss-1)->statScore < 23767
&& eval >= beta
&& eval >= ss->staticEval
- && ss->staticEval >= beta - 20 * depth - improvement / 15 + 204
+ && ss->staticEval >= beta - 20 * depth - improvement / 15 + 204 + complexity / 25
&& !excludedMove
&& pos.non_pawn_material(us)
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
moves_loop: // When in check, search starts here
- int rangeReduction = 0;
-
// Step 11. A small Probcut idea, when we are in check (~0 Elo)
probCutBeta = beta + 409;
if ( ss->inCheck
moveCountPruning = moveCount >= futility_move_count(improving, depth);
// Reduced depth of the next LMR search
- int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, rangeReduction > 2, delta, thisThread->rootDelta), 0);
+ int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, delta, thisThread->rootDelta), 0);
if ( captureOrPromotion
|| givesCheck)
continue;
// SEE based pruning (~9 Elo)
- if (!pos.see_ge(move, Value(-218) * depth))
+ if (!pos.see_ge(move, Value(-217) * depth))
continue;
}
else
// Continuation history based pruning (~2 Elo)
if ( lmrDepth < 5
- && history < -3000 * depth + 3000)
+ && history < -3875 * (depth - 1))
continue;
history += thisThread->mainHistory[us][from_to(move)];
// Futility pruning: parent node (~9 Elo)
if ( !ss->inCheck
&& lmrDepth < 8
- && ss->staticEval + 142 + 139 * lmrDepth + history / 64 <= alpha)
+ && ss->staticEval + 138 + 137 * lmrDepth + history / 64 <= alpha)
continue;
// Prune moves with negative SEE (~3 Elo)
|| !captureOrPromotion
|| (cutNode && (ss-1)->moveCount > 1)))
{
- Depth r = reduction(improving, depth, moveCount, rangeReduction > 2, delta, thisThread->rootDelta);
+ Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta);
// Decrease reduction at some PvNodes (~2 Elo)
if ( PvNode
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
- // Range reductions (~3 Elo)
- if (ss->staticEval - value < 30 && depth > 7)
- rangeReduction++;
-
// If the son is reduced and fails high it will be re-searched at full depth
doFullDepthSearch = value > alpha && d < newDepth;
- doDeeperSearch = value > alpha + 88;
+ doDeeperSearch = value > (alpha + 62 + 20 * (newDepth - d));
didLMR = true;
}
else
rm.pv.push_back(*m);
// We record how often the best move has been changed in each iteration.
- // This information is used for time management and LMR. In MultiPV mode,
+ // This information is used for time management. In MultiPV mode,
// we must take care to only do this for the first PV line.
if ( moveCount > 1
&& !thisThread->pvIdx)