#include "tt.h"
#include "uci.h"
#include "syzygy/tbprobe.h"
+#include "nnue/evaluate_nnue.h"
namespace Stockfish {
Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) {
int r = Reductions[d] * Reductions[mn];
- return (r + 1449 - int(delta) * 1032 / int(rootDelta)) / 1024 + (!i && r > 941);
+ return (r + 1449 - int(delta) * 937 / int(rootDelta)) / 1024 + (!i && r > 941);
}
constexpr int futility_move_count(bool improving, Depth depth) {
// History and stats update bonus, based on depth
int stat_bonus(Depth d) {
- return std::min(340 * d - 470, 1855);
+ return std::min(341 * d - 470, 1710);
}
// Add a small random component to draw evaluations to avoid 3-fold blindness
bestValue = delta = alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE;
+ optimism[WHITE] = optimism[BLACK] = VALUE_ZERO;
if (mainThread)
{
multiPV = std::min(multiPV, rootMoves.size());
- complexityAverage.set(153, 1);
-
- optimism[us] = optimism[~us] = VALUE_ZERO;
-
int searchAgainCounter = 0;
// Iterative deepening loop until requested to stop or the target depth is reached
timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.57 : 0.65;
double reduction = (1.4 + mainThread->previousTimeReduction) / (2.08 * timeReduction);
double bestMoveInstability = 1 + 1.8 * totBestMoveChanges / Threads.size();
- int complexity = mainThread->complexityAverage.value();
- double complexPosition = std::min(1.03 + (complexity - 241) / 1552.0, 1.45);
- double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition;
+ double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability;
// Cap used time in case of a single legal move for a better viewer experience in tournaments
// yielding correct scores and sufficiently fast moves.
bool givesCheck, improving, priorCapture, singularQuietLMR;
bool capture, moveCountPruning, ttCapture;
Piece movedPiece;
- int moveCount, captureCount, quietCount, improvement, complexity;
+ int moveCount, captureCount, quietCount, improvement;
// Step 1. Initialize node
Thread* thisThread = pos.this_thread();
(ss+2)->cutoffCnt = 0;
ss->doubleExtensions = (ss-1)->doubleExtensions;
Square prevSq = is_ok((ss-1)->currentMove) ? to_sq((ss-1)->currentMove) : SQ_NONE;
-
- // Initialize statScore to zero for the grandchildren of the current position.
- // So statScore is shared between all grandchildren and only the first grandchild
- // starts with statScore = 0. Later grandchildren start with the last calculated
- // statScore of the previous grandchild. This influences the reduction rules in
- // LMR which are based on the statScore of parent position.
- if (!rootNode)
- (ss+2)->statScore = 0;
+ ss->statScore = 0;
// Step 4. Transposition table lookup.
excludedMove = ss->excludedMove;
ss->staticEval = eval = VALUE_NONE;
improving = false;
improvement = 0;
- complexity = 0;
goto moves_loop;
}
else if (excludedMove)
{
- // Providing the hint that this node's accumulator will be used often brings significant Elo gain (13 elo)
+ // Providing the hint that this node's accumulator will be used often brings significant Elo gain (13 Elo)
Eval::NNUE::hint_common_parent_position(pos);
eval = ss->staticEval;
- complexity = abs(ss->staticEval - pos.psq_eg_stm());
}
else if (ss->ttHit)
{
// Never assume anything about values stored in TT
ss->staticEval = eval = tte->eval();
if (eval == VALUE_NONE)
- ss->staticEval = eval = evaluate(pos, &complexity);
- else // Fall back to (semi)classical complexity for TT hits, the NNUE complexity is lost
+ ss->staticEval = eval = evaluate(pos);
+ else
{
- complexity = abs(ss->staticEval - pos.psq_eg_stm());
if (PvNode)
Eval::NNUE::hint_common_parent_position(pos);
}
}
else
{
- ss->staticEval = eval = evaluate(pos, &complexity);
+ ss->staticEval = eval = evaluate(pos);
// Save static evaluation into transposition table
tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval);
}
- thisThread->complexityAverage.update(complexity);
-
// Use static evaluation difference to improve quiet move ordering (~4 Elo)
if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture)
{
// Step 7. Razoring (~1 Elo).
// If eval is really low check with qsearch if it can exceed alpha, if it can't,
// return a fail low.
- if (eval < alpha - 426 - 252 * depth * depth)
+ if (eval < alpha - 426 - 256 * depth * depth)
{
value = qsearch<NonPV>(pos, ss, alpha - 1, alpha);
if (value < alpha)
&& (ss-1)->statScore < 18755
&& eval >= beta
&& eval >= ss->staticEval
- && ss->staticEval >= beta - 19 * depth - improvement / 13 + 253 + complexity / 25
+ && ss->staticEval >= beta - 20 * depth - improvement / 13 + 253
&& !excludedMove
&& pos.non_pawn_material(us)
- && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
+ && (ss->ply >= thisThread->nmpMinPly))
{
assert(eval - beta >= 0);
- // Null move dynamic reduction based on depth, eval and complexity of position
- Depth R = std::min(int(eval - beta) / 168, 6) + depth / 3 + 4 - (complexity > 825);
+ // Null move dynamic reduction based on depth and eval
+ Depth R = std::min(int(eval - beta) / 172, 6) + depth / 3 + 4;
ss->currentMove = MOVE_NULL;
ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0];
assert(!thisThread->nmpMinPly); // Recursive verification is not allowed
// Do verification search at high depths, with null move pruning disabled
- // for us, until ply exceeds nmpMinPly.
+ // until ply exceeds nmpMinPly.
thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / 4;
- thisThread->nmpColor = us;
Value v = search<NonPV>(pos, ss, beta-1, beta, depth-R, false);
Eval::NNUE::hint_common_parent_position(pos);
}
- // Step 11. If the position is not in TT, decrease depth by 3.
+ // Step 11. If the position is not in TT, decrease depth by 2 (or by 4 if the TT entry for the current position was hit and the stored depth is greater than or equal to the current depth).
// Use qsearch if depth is equal or below zero (~9 Elo)
if ( PvNode
&& !ttMove)
- depth -= 3;
+ depth -= 2 + 2 * (ss->ttHit && tte->depth() >= depth);
if (depth <= 0)
return qsearch<PV>(pos, ss, alpha, beta);
{
// Futility pruning for captures (~2 Elo)
if ( !givesCheck
- && !PvNode
&& lmrDepth < 6
&& !ss->inCheck
&& ss->staticEval + 182 + 230 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))]
+ captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha)
continue;
+ Bitboard occupied;
// SEE based pruning (~11 Elo)
- if (!pos.see_ge(move, Value(-206) * depth))
- continue;
+ if (!pos.see_ge(move, occupied, Value(-206) * depth))
+ {
+ if (depth < 2 - capture)
+ continue;
+ // Don't prune the move if opp. King/Queen/Rook is attacked by a slider after the exchanges.
+ // Since in see_ge we don't update occupied when the king recaptures, we also don't prune the
+ // move when the opp. King gets a discovered slider attack DURING the exchanges.
+ Bitboard leftEnemies = pos.pieces(~us, ROOK, QUEEN, KING) & occupied;
+ Bitboard attacks = 0;
+ occupied |= to_sq(move);
+ while (leftEnemies && !attacks)
+ {
+ Square sq = pop_lsb(leftEnemies);
+ attacks = pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied;
+ // Exclude Queen/Rook(s) which were already threatened before SEE
+ if (attacks && sq != pos.square<KING>(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us)))
+ attacks = 0;
+ }
+ if (!attacks)
+ continue;
+ }
}
else
{
lmrDepth = std::max(lmrDepth, 0);
// Prune moves with negative SEE (~4 Elo)
- if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth)))
+ if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 16 * lmrDepth)))
continue;
}
}
else if (singularBeta >= beta)
return singularBeta;
- // If the eval of ttMove is greater than beta, we reduce it (negative extension)
+ // If the eval of ttMove is greater than beta, we reduce it (negative extension) (~7 Elo)
else if (ttValue >= beta)
extension = -2 - !PvNode;
- // If the eval of ttMove is less than value, we reduce it (negative extension)
+ // If the eval of ttMove is less than value, we reduce it (negative extension) (~1 Elo)
else if (ttValue <= value)
extension = -1;
- // If the eval of ttMove is less than alpha, we reduce it (negative extension)
+ // If the eval of ttMove is less than alpha, we reduce it (negative extension) (~1 Elo)
else if (ttValue <= alpha)
extension = -1;
}
if (ttCapture)
r++;
- // Decrease reduction for PvNodes based on depth
+ // Decrease reduction for PvNodes based on depth (~2 Elo)
if (PvNode)
r -= 1 + 12 / (3 + depth);
if (singularQuietLMR)
r--;
- // Decrease reduction if we move a threatened piece (~1 Elo)
- if ( depth > 9
- && (mp.threatenedPieces & from_sq(move)))
- r--;
-
- // Increase reduction if next ply has a lot of fail high
+ // Increase reduction if next ply has a lot of fail high (~5 Elo)
if ((ss+1)->cutoffCnt > 3)
r++;
- // Decrease reduction if move is a killer and we have a good history
- if (move == ss->killers[0]
- && (*contHist[0])[movedPiece][to_sq(move)] >= 3722)
- r--;
-
ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)]
+ (*contHist[0])[movedPiece][to_sq(move)]
+ (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)]
- - 4182;
+ - 4082;
- // Decrease/increase reduction for moves with a good/bad history (~30 Elo)
- r -= ss->statScore / (11791 + 3992 * (depth > 6 && depth < 19));
+ // Decrease/increase reduction for moves with a good/bad history (~25 Elo)
+ r -= ss->statScore / (11079 + 4626 * (depth > 6 && depth < 19));
// Step 17. Late moves reduction / extension (LMR, ~117 Elo)
// We use various heuristics for the sons of a node after the first son has
if (newDepth > d)
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
- int bonus = value > alpha ? stat_bonus(newDepth)
- : -stat_bonus(newDepth);
+ int bonus = value <= alpha ? -stat_bonus(newDepth)
+ : value >= beta ? stat_bonus(newDepth)
+ : 0;
update_continuation_histories(ss, movedPiece, to_sq(move), bonus);
}
if (PvNode && value < beta) // Update alpha! Always alpha < beta
{
- alpha = value;
-
- // Reduce other moves if we have found at least one score improvement
+ // Reduce other moves if we have found at least one score improvement (~1 Elo)
if ( depth > 1
- && depth < 6
- && beta < 10534
- && alpha > -10534)
+ && beta < 12535
+ && value > -12535)
depth -= 1;
assert(depth > 0);
+ alpha = value;
}
else
{
bestValue = std::min(bestValue, maxValue);
// If no good move is found and the previous position was ttPv, then the previous
- // opponent move is probably good and the new position is added to the search tree.
+ // opponent move is probably good and the new position is added to the search tree. (~7 Elo)
if (bestValue <= alpha)
ss->ttPv = ss->ttPv || ((ss-1)->ttPv && depth > 3);
// qsearch() is the quiescence search function, which is called by the main search
// function with zero depth, or recursively with further decreasing depth per call.
- // (~155 elo)
+ // (~155 Elo)
template <NodeType nodeType>
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {