X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fsearch.cpp;h=a745d3bfdd0ed16933a9d53dcd8f7ab830637940;hb=6d85f43e26cb8632337e67cea5ef88bab78121f3;hp=11a52326dbe5a3694cea01803ad6a2cafd50db0c;hpb=acdbf45171ba8bd0322e3bda0900e9cb2f8fb846;p=stockfish
diff --git a/src/search.cpp b/src/search.cpp
index 11a52326..a745d3bf 100644
--- a/src/search.cpp
+++ b/src/search.cpp
@@ -16,25 +16,34 @@
along with this program. If not, see .
*/
+#include "search.h"
+
#include
+#include
+#include
#include
#include
-#include // For std::memset
+#include
+#include
+#include
#include
#include
+#include
+#include
+#include "bitboard.h"
#include "evaluate.h"
#include "misc.h"
#include "movegen.h"
#include "movepick.h"
+#include "nnue/evaluate_nnue.h"
+#include "nnue/nnue_common.h"
#include "position.h"
-#include "search.h"
+#include "syzygy/tbprobe.h"
#include "thread.h"
#include "timeman.h"
#include "tt.h"
#include "uci.h"
-#include "syzygy/tbprobe.h"
-#include "nnue/evaluate_nnue.h"
namespace Stockfish {
@@ -63,16 +72,17 @@ namespace {
enum NodeType { NonPV, PV, Root };
// Futility margin
- Value futility_margin(Depth d, bool improving) {
- return Value(140 * (d - improving));
+ Value futility_margin(Depth d, bool noTtCutNode, bool improving) {
+ return Value((140 - 40 * noTtCutNode) * (d - improving));
}
// Reductions lookup table initialized at startup
int Reductions[MAX_MOVES]; // [depth or moveNumber]
Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) {
- int r = Reductions[d] * Reductions[mn];
- return (r + 1372 - int(delta) * 1073 / int(rootDelta)) / 1024 + (!i && r > 936);
+ int reductionScale = Reductions[d] * Reductions[mn];
+ return (reductionScale + 1372 - int(delta) * 1073 / int(rootDelta)) / 1024
+ + (!i && reductionScale > 936);
}
constexpr int futility_move_count(bool improving, Depth depth) {
@@ -518,7 +528,6 @@ namespace {
// Check if we have an upcoming move that draws by repetition, or
// if the opponent had an alternative move earlier to this position.
if ( !rootNode
- && pos.rule50_count() >= 3
&& alpha < VALUE_DRAW
&& pos.has_game_cycle(ss->ply))
{
@@ -548,7 +557,7 @@ namespace {
bool givesCheck, improving, priorCapture, singularQuietLMR;
bool capture, moveCountPruning, ttCapture;
Piece movedPiece;
- int moveCount, captureCount, quietCount, improvement;
+ int moveCount, captureCount, quietCount;
// Step 1. Initialize node
Thread* thisThread = pos.this_thread();
@@ -616,7 +625,7 @@ namespace {
// At non-PV nodes we check for an early TT cutoff
if ( !PvNode
&& !excludedMove
- && tte->depth() > depth - (tte->bound() == BOUND_EXACT)
+ && tte->depth() > depth
&& ttValue != VALUE_NONE // Possible in case of TT access race or if !ttHit
&& (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER)))
{
@@ -708,7 +717,6 @@ namespace {
// Skip early pruning when in check
ss->staticEval = eval = VALUE_NONE;
improving = false;
- improvement = 0;
goto moves_loop;
}
else if (excludedMove)
@@ -745,14 +753,14 @@ namespace {
thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus;
}
- // Set up the improvement variable, which is the difference between the current
- // static evaluation and the previous static evaluation at our turn (if we were
- // in check at our previous move we look at the move prior to it). The improvement
- // margin and the improving flag are used in various pruning heuristics.
- improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval
- : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval
- : 173;
- improving = improvement > 0;
+ // Set up the improving flag, which is true if current static evaluation is
+ // bigger than the previous static evaluation at our turn (if we were in
+ // check at our previous move we look at static evaluation at move prior to it
+ // and if we were in check at move prior to it flag is set to true) and is
+ // false otherwise. The improving flag is used in various pruning heuristics.
+ improving = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval > (ss-2)->staticEval
+ : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval > (ss-4)->staticEval
+ : true;
// Step 7. Razoring (~1 Elo).
// If eval is really low check with qsearch if it can exceed alpha, if it can't,
@@ -768,7 +776,7 @@ namespace {
// The depth condition is important for mate finding.
if ( !ss->ttPv
&& depth < 9
- && eval - futility_margin(depth, improving) - (ss-1)->statScore / 306 >= beta
+ && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) - (ss-1)->statScore / 306 >= beta
&& eval >= beta
&& eval < 24923) // larger than VALUE_KNOWN_WIN, but smaller than TB wins
return eval;
@@ -986,32 +994,13 @@ moves_loop: // When in check, search starts here
if ( !givesCheck
&& lmrDepth < 7
&& !ss->inCheck
- && ss->staticEval + 197 + 248 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))]
+ && ss->staticEval + 197 + 248 * lmrDepth + PieceValue[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, occupied, Value(-205) * depth))
- {
- if (depth < 2 - capture)
- continue;
- // Don't prune the move if opponent Queen/Rook is under discovered attack after the exchanges
- // Don't prune the move if opponent King is under discovered attack after or during the exchanges
- Bitboard leftEnemies = (pos.pieces(~us, KING, QUEEN, ROOK)) & 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;
- // Don't consider pieces that were already threatened/hanging before SEE exchanges
- if (attacks && (sq != pos.square(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us))))
- attacks = 0;
- }
- if (!attacks)
- continue;
- }
+ // SEE based pruning for captures and checks (~11 Elo)
+ if (!pos.see_ge(move, Value(-205) * depth))
+ continue;
}
else
{
@@ -1038,7 +1027,7 @@ moves_loop: // When in check, search starts here
lmrDepth = std::max(lmrDepth, 0);
// Prune moves with negative SEE (~4 Elo)
- if (!pos.see_ge(move, Value(-27 * lmrDepth * lmrDepth - 16 * lmrDepth)))
+ if (!pos.see_ge(move, Value(-31 * lmrDepth * lmrDepth)))
continue;
}
}
@@ -1100,15 +1089,11 @@ moves_loop: // When in check, search starts here
// If we are on a cutNode, reduce it based on depth (negative extension) (~1 Elo)
else if (cutNode)
- extension = depth > 8 && depth < 17 ? -3 : -1;
+ extension = depth < 17 ? -3 : -1;
// 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)
@@ -1146,7 +1131,7 @@ moves_loop: // When in check, search starts here
// Decrease further on cutNodes. (~1 Elo)
if ( ss->ttPv
&& !likelyFailLow)
- r -= cutNode && tte->depth() >= depth + 3 ? 3 : 2;
+ r -= cutNode && tte->depth() >= depth ? 3 : 2;
// Decrease reduction if opponent's move count is high (~1 Elo)
if ((ss-1)->moveCount > 8)
@@ -1160,14 +1145,19 @@ moves_loop: // When in check, search starts here
if (ttCapture)
r++;
- // Decrease reduction for PvNodes based on depth (~2 Elo)
+ // Decrease reduction for PvNodes (~2 Elo)
if (PvNode)
- r -= 1 + (depth < 6);
+ r--;
// Decrease reduction if ttMove has been singularly extended (~1 Elo)
if (singularQuietLMR)
r--;
+ // Increase reduction on repetition (~1 Elo)
+ if ( move == (ss-4)->currentMove
+ && pos.has_repeated())
+ r += 2;
+
// Increase reduction if next ply has a lot of fail high (~5 Elo)
if ((ss+1)->cutoffCnt > 3)
r++;
@@ -1235,10 +1225,9 @@ moves_loop: // When in check, search starts here
value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth - (r > 3), !cutNode);
}
- // For PV nodes only, do a full PV search on the first move or after a fail
- // high (in the latter case search only if value < beta), otherwise let the
- // parent node fail low with value <= alpha and try another move.
- if (PvNode && (moveCount == 1 || (value > alpha && (rootNode || value < beta))))
+ // For PV nodes only, do a full PV search on the first move or after a fail high,
+ // otherwise let the parent node fail low with value <= alpha and try another move.
+ if (PvNode && (moveCount == 1 || value > alpha))
{
(ss+1)->pv = pv;
(ss+1)->pv[0] = MOVE_NONE;
@@ -1376,8 +1365,9 @@ moves_loop: // When in check, search starts here
// Bonus for prior countermove that caused the fail low
else if (!priorCapture && prevSq != SQ_NONE)
{
- int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 113 * depth) + ((ss-1)->moveCount > 12);
+ int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 800) + ((ss-1)->moveCount > 12);
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus);
+ thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << stat_bonus(depth) * bonus / 2;
}
if (PvNode)
@@ -1414,6 +1404,17 @@ moves_loop: // When in check, search starts here
assert(PvNode || (alpha == beta - 1));
assert(depth <= 0);
+ // Check if we have an upcoming move that draws by repetition, or
+ // if the opponent had an alternative move earlier to this position.
+ if ( depth < 0
+ && alpha < VALUE_DRAW
+ && pos.has_game_cycle(ss->ply))
+ {
+ alpha = value_draw(pos.this_thread());
+ if (alpha >= beta)
+ return alpha;
+ }
+
Move pv[MAX_PLY+1];
StateInfo st;
ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize);
@@ -1497,10 +1498,10 @@ moves_loop: // When in check, search starts here
return bestValue;
}
- if (PvNode && bestValue > alpha)
+ if (bestValue > alpha)
alpha = bestValue;
- futilityBase = bestValue + 200;
+ futilityBase = std::min(ss->staticEval, bestValue) + 200;
}
const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
@@ -1546,7 +1547,7 @@ moves_loop: // When in check, search starts here
if (moveCount > 2)
continue;
- futilityValue = futilityBase + PieceValue[EG][pos.piece_on(to_sq(move))];
+ futilityValue = futilityBase + PieceValue[pos.piece_on(to_sq(move))];
if (futilityValue <= alpha)
{
@@ -1609,7 +1610,7 @@ moves_loop: // When in check, search starts here
if (PvNode) // Update pv even in fail-high case
update_pv(ss->pv, move, (ss+1)->pv);
- if (PvNode && value < beta) // Update alpha here!
+ if (value < beta) // Update alpha here!
alpha = value;
else
break; // Fail high
@@ -1794,7 +1795,7 @@ moves_loop: // When in check, search starts here
// RootMoves are already sorted by score in descending order
Value topScore = rootMoves[0].score;
- int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValueMg);
+ int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValue);
int maxScore = -VALUE_INFINITE;
double weakness = 120 - 2 * level;
@@ -1829,7 +1830,7 @@ void MainThread::check_time() {
return;
// When using nodes, ensure checking rate is not lower than 0.1% of nodes
- callsCnt = Limits.nodes ? std::min(1024, int(Limits.nodes / 1024)) : 1024;
+ callsCnt = Limits.nodes ? std::min(512, int(Limits.nodes / 1024)) : 512;
static TimePoint lastInfoTime = now();
@@ -1846,7 +1847,7 @@ void MainThread::check_time() {
if (ponder)
return;
- if ( (Limits.use_time_management() && (elapsed > Time.maximum() - 10 || stopOnPonderhit))
+ if ( (Limits.use_time_management() && (elapsed > Time.maximum() || stopOnPonderhit))
|| (Limits.movetime && elapsed >= Limits.movetime)
|| (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes))
Threads.stop = true;