#include "tt.h"
#include "uci.h"
#include "syzygy/tbprobe.h"
+#include "nnue/evaluate_nnue.h"
namespace Stockfish {
assert(0 <= ss->ply && ss->ply < MAX_PLY);
- (ss+1)->ttPv = false;
(ss+1)->excludedMove = bestMove = MOVE_NONE;
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
(ss+2)->cutoffCnt = 0;
ss->doubleExtensions = (ss-1)->doubleExtensions;
- Square prevSq = to_sq((ss-1)->currentMove);
-
- // 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;
+ Square prevSq = is_ok((ss-1)->currentMove) ? to_sq((ss-1)->currentMove) : SQ_NONE;
+ ss->statScore = 0;
// Step 4. Transposition table lookup.
excludedMove = ss->excludedMove;
update_quiet_stats(pos, ss, ttMove, stat_bonus(depth));
// Extra penalty for early quiet moves of the previous ply (~0 Elo on STC, ~2 Elo on LTC)
- if ((ss-1)->moveCount <= 2 && !priorCapture)
+ if (prevSq != SQ_NONE && (ss-1)->moveCount <= 2 && !priorCapture)
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1));
}
// Penalty for a quiet ttMove that fails low (~1 Elo)
}
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());
nullptr , (ss-4)->continuationHistory,
nullptr , (ss-6)->continuationHistory };
- Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];
+ Move countermove = prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : MOVE_NONE;
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
&captureHistory,
{
// 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 move if a heavy enemy piece (KQR) is under attack after the exchanges
+ Bitboard leftEnemies = (pos.pieces(~us, QUEEN, ROOK) | pos.pieces(~us, 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
{
&& (tte->bound() & BOUND_LOWER)
&& tte->depth() >= depth - 3)
{
- Value singularBeta = ttValue - (2 + (ss->ttPv && !PvNode)) * depth;
+ Value singularBeta = ttValue - (3 + 2 * (ss->ttPv && !PvNode)) * depth / 2;
Depth singularDepth = (depth - 1) / 2;
ss->excludedMove = move;
- // the search with excludedMove will update ss->staticEval
value = search<NonPV>(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode);
ss->excludedMove = MOVE_NONE;
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;
+ 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) (~1 Elo)
+ else if (ttValue <= alpha)
+ extension = -1;
}
// Check extensions (~1 Elo)
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
+ // Decrease reduction if move is a killer and we have a good history (~1 Elo)
if (move == ss->killers[0]
&& (*contHist[0])[movedPiece][to_sq(move)] >= 3722)
r--;
+ (*contHist[3])[movedPiece][to_sq(move)]
- 4182;
- // Decrease/increase reduction for moves with a good/bad history (~30 Elo)
+ // Decrease/increase reduction for moves with a good/bad history (~25 Elo)
r -= ss->statScore / (11791 + 3992 * (depth > 6 && depth < 19));
// Step 17. Late moves reduction / extension (LMR, ~117 Elo)
{
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
quietsSearched, quietCount, capturesSearched, captureCount, depth);
// Bonus for prior countermove that caused the fail low
- else if (!priorCapture)
+ else if (!priorCapture && prevSq != SQ_NONE)
{
int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 97 * depth) + ((ss-1)->moveCount > 10);
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus);
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) {
// to search the moves. Because the depth is <= 0 here, only captures,
// queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS)
// will be generated.
- Square prevSq = to_sq((ss-1)->currentMove);
+ Square prevSq = (ss-1)->currentMove != MOVE_NULL ? to_sq((ss-1)->currentMove) : SQ_NONE;
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
&thisThread->captureHistory,
contHist,
Thread* thisThread = pos.this_thread();
CapturePieceToHistory& captureHistory = thisThread->captureHistory;
Piece moved_piece = pos.moved_piece(bestMove);
- PieceType captured = type_of(pos.piece_on(to_sq(bestMove)));
+ PieceType captured;
+
int bonus1 = stat_bonus(depth + 1);
if (!pos.capture_stage(bestMove))
}
}
else
+ {
// Increase stats for the best move in case it was a capture move
+ captured = type_of(pos.piece_on(to_sq(bestMove)));
captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1;
+ }
// Extra penalty for a quiet early move that was not a TT move or
// main killer move in previous ply when it gets refuted.
- if ( ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0]))
+ if ( prevSq != SQ_NONE
+ && ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0]))
&& !pos.captured_piece())
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1);