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;