X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=25a7cf44044780275be61204b56b0035fcf4250d;hp=1742c6766aa3d65adf0616151fc669e1fac9632f;hb=44f79bdf5a092c3acec0a8bf8f2c1440e5a9da90;hpb=0150da5c2bd4661996b05dec4a1eca473515e9d7 diff --git a/src/search.cpp b/src/search.cpp index 1742c676..25a7cf44 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -61,10 +61,13 @@ namespace { // Different node types, used as a template parameter enum NodeType { NonPV, PV }; + constexpr uint64_t ttHitAverageWindow = 4096; + constexpr uint64_t ttHitAverageResolution = 1024; + // Razor and futility margins - constexpr int RazorMargin = 661; + constexpr int RazorMargin = 531; Value futility_margin(Depth d, bool improving) { - return Value(198 * (d - improving)); + return Value(217 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -72,16 +75,16 @@ namespace { Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d] * Reductions[mn]; - return (r + 520) / 1024 + (!i && r > 999); + return (r + 511) / 1024 + (!i && r > 1007); } - constexpr int futility_move_count(bool improving, int depth) { - return (5 + depth * depth) * (1 + improving) / 2; + constexpr int futility_move_count(bool improving, Depth depth) { + return (5 + depth * depth) * (1 + improving) / 2 - 1; } // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 17 ? -8 : 22 * d * d + 151 * d - 140; + return d > 15 ? -8 : 19 * d * d + 155 * d - 132; } // Add a small random component to draw evaluations to avoid 3fold-blindness @@ -150,11 +153,12 @@ namespace { Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0); Value value_to_tt(Value v, int ply); - Value value_from_tt(Value v, int ply); + Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); - void update_quiet_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietCount, int bonus); - void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCount, int bonus); + void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); + void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, + Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth); // perft() is our utility to verify move generation. All the leaf nodes up // to the given depth are generated and counted, and the sum is returned. @@ -190,7 +194,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((23.4 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((24.8 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -331,16 +335,27 @@ void Thread::search() { MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); double timeReduction = 1, totBestMoveChanges = 0; Color us = rootPos.side_to_move(); + int iterIdx = 0; std::memset(ss-7, 0, 10 * sizeof(Stack)); for (int i = 7; i > 0; i--) - (ss-i)->continuationHistory = &this->continuationHistory[0][NO_PIECE][0]; // Use as a sentinel + (ss-i)->continuationHistory = &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel ss->pv = pv; bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; + if (mainThread) + { + if (mainThread->previousScore == VALUE_INFINITE) + for (int i=0; i<4; ++i) + mainThread->iterValue[i] = VALUE_ZERO; + else + for (int i=0; i<4; ++i) + mainThread->iterValue[i] = mainThread->previousScore; + } + size_t multiPV = Options["MultiPV"]; // Pick integer skill levels, but non-deterministically round up or down @@ -362,6 +377,7 @@ void Thread::search() { multiPV = std::max(multiPV, (size_t)4); multiPV = std::min(multiPV, rootMoves.size()); + ttHitAverage = ttHitAverageWindow * ttHitAverageResolution / 2; int ct = int(Options["Contempt"]) * PawnValueEg / 100; // From centipawns @@ -412,12 +428,12 @@ void Thread::search() { if (rootDepth >= 4) { Value previousScore = rootMoves[pvIdx].previousScore; - delta = Value(21 + abs(previousScore) / 128); + delta = Value(21 + abs(previousScore) / 256); alpha = std::max(previousScore - delta,-VALUE_INFINITE); beta = std::min(previousScore + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + 86 * previousScore / (abs(previousScore) + 176); + int dct = ct + (102 - ct / 2) * previousScore / (abs(previousScore) + 157); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); @@ -515,12 +531,13 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (354 + 10 * (mainThread->previousScore - bestValue)) / 692.0; + double fallingEval = (332 + 6 * (mainThread->previousScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 704.0; fallingEval = clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.97 : 0.98; - double reduction = (1.36 + mainThread->previousTimeReduction) / (2.29 * timeReduction); + timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.94 : 0.91; + double reduction = (1.41 + mainThread->previousTimeReduction) / (2.27 * timeReduction); // Use part of the gained time from a previous stable move for the current move for (Thread* th : Threads) @@ -542,6 +559,9 @@ void Thread::search() { Threads.stop = true; } } + + mainThread->iterValue[iterIdx] = bestValue; + iterIdx = (iterIdx + 1) & 3; } if (!mainThread) @@ -594,17 +614,17 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue; - bool ttHit, ttPv, inCheck, givesCheck, improving, doLMR, priorCapture; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; + bool ttHit, ttPv, inCheck, givesCheck, improving, didLMR, priorCapture; + bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularLMR; Piece movedPiece; - int moveCount, captureCount, quietCount, singularLMR; + int moveCount, captureCount, quietCount; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); - moveCount = captureCount = quietCount = singularLMR = ss->moveCount = 0; + moveCount = captureCount = quietCount = ss->moveCount = 0; bestValue = -VALUE_INFINITE; maxValue = VALUE_INFINITE; @@ -660,10 +680,13 @@ namespace { excludedMove = ss->excludedMove; posKey = pos.key() ^ Key(excludedMove << 16); // Isn't a very good hash tte = TT.probe(posKey, ttHit); - ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; + ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; ttPv = PvNode || (ttHit && tte->is_pv()); + // thisThread->ttHitAverage can be used to approximate the running average of ttHit + thisThread->ttHitAverage = (ttHitAverageWindow - 1) * thisThread->ttHitAverage / ttHitAverageWindow + + ttHitAverageResolution * ttHit; // At non-PV nodes we check for an early TT cutoff if ( !PvNode @@ -679,7 +702,7 @@ namespace { if (ttValue >= beta) { if (!pos.capture_or_promotion(ttMove)) - update_quiet_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth)); + update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); // Extra penalty for early quiet moves of the previous ply if ((ss-1)->moveCount <= 2 && !priorCapture) @@ -789,12 +812,12 @@ namespace { && eval <= alpha - RazorMargin) return qsearch(pos, ss, alpha, beta); - improving = ss->staticEval >= (ss-2)->staticEval - || (ss-2)->staticEval == VALUE_NONE; + improving = (ss-2)->staticEval == VALUE_NONE ? (ss->staticEval >= (ss-4)->staticEval + || (ss-4)->staticEval == VALUE_NONE) : ss->staticEval >= (ss-2)->staticEval; // Step 8. Futility pruning: child node (~30 Elo) if ( !PvNode - && depth < 7 + && depth < 6 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; @@ -802,10 +825,10 @@ namespace { // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 22661 + && (ss-1)->statScore < 23397 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 33 * depth + 299 - improving * 30 + && ss->staticEval >= beta - 32 * depth + 292 - improving * 30 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -813,10 +836,10 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (835 + 70 * depth) / 256 + std::min(int(eval - beta) / 185, 3); + Depth R = (854 + 68 * depth) / 258 + std::min(int(eval - beta) / 192, 3); ss->currentMove = MOVE_NULL; - ss->continuationHistory = &thisThread->continuationHistory[0][NO_PIECE][0]; + ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; pos.do_null_move(st); @@ -856,7 +879,7 @@ namespace { && depth >= 5 && abs(beta) < VALUE_MATE_IN_MAX_PLY) { - Value raisedBeta = std::min(beta + 191 - 46 * improving, VALUE_INFINITE); + Value raisedBeta = std::min(beta + 189 - 45 * improving, VALUE_INFINITE); MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory); int probCutCount = 0; @@ -864,12 +887,17 @@ namespace { && probCutCount < 2 + 2 * cutNode) if (move != excludedMove && pos.legal(move)) { + assert(pos.capture_or_promotion(move)); + assert(depth >= 5); + + captureOrPromotion = true; probCutCount++; ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[priorCapture][pos.moved_piece(move)][to_sq(move)]; - - assert(depth >= 5); + ss->continuationHistory = &thisThread->continuationHistory[inCheck] + [captureOrPromotion] + [pos.moved_piece(move)] + [to_sq(move)]; pos.do_move(move, st); @@ -893,15 +921,15 @@ namespace { search(pos, ss, alpha, beta, depth - 7, cutNode); tte = TT.probe(posKey, ttHit); - ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; + ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = ttHit ? tte->move() : MOVE_NONE; } moves_loop: // When in check, search starts from here const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, - nullptr, (ss-4)->continuationHistory, - nullptr, (ss-6)->continuationHistory }; + nullptr , (ss-4)->continuationHistory, + nullptr , (ss-6)->continuationHistory }; Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; @@ -911,8 +939,8 @@ moves_loop: // When in check, search starts from here countermove, ss->killers); - value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc - moveCountPruning = false; + value = bestValue; + singularLMR = moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); // Mark this node as being searched @@ -949,7 +977,48 @@ moves_loop: // When in check, search starts from here movedPiece = pos.moved_piece(move); givesCheck = pos.gives_check(move); - // Step 13. Extensions (~70 Elo) + // Calculate new depth for this move + newDepth = depth - 1; + + // Step 13. Pruning at shallow depth (~170 Elo) + if ( !rootNode + && pos.non_pawn_material(us) + && bestValue > VALUE_MATED_IN_MAX_PLY) + { + // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold + moveCountPruning = moveCount >= futility_move_count(improving, depth); + + if ( !captureOrPromotion + && !givesCheck) + { + // Reduced depth of the next LMR search + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); + + // Countermoves based pruning (~20 Elo) + if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) + && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold + && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) + continue; + + // Futility pruning: parent node (~2 Elo) + if ( lmrDepth < 6 + && !inCheck + && ss->staticEval + 255 + 182 * lmrDepth <= alpha + && thisThread->mainHistory[us][from_to(move)] + + (*contHist[0])[movedPiece][to_sq(move)] + + (*contHist[1])[movedPiece][to_sq(move)] + + (*contHist[3])[movedPiece][to_sq(move)] < 30000) + continue; + + // Prune moves with negative SEE (~10 Elo) + if (!pos.see_ge(move, Value(-(32 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) + continue; + } + else if (!pos.see_ge(move, Value(-194) * depth)) // (~20 Elo) + continue; + } + + // Step 14. Extensions (~70 Elo) // Singular extension search (~60 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), @@ -975,10 +1044,7 @@ moves_loop: // When in check, search starts from here if (value < singularBeta) { extension = 1; - singularLMR++; - - if (value < singularBeta - std::min(4 * depth, 36)) - singularLMR++; + singularLMR = true; } // Multi-cut pruning @@ -986,8 +1052,7 @@ moves_loop: // When in check, search starts from here // search without the ttMove. So we assume this expected Cut-node is not singular, // that multiple moves fail high, and we can prune the whole subtree by returning // a soft bound. - else if ( eval >= beta - && singularBeta >= beta) + else if (singularBeta >= beta) return singularBeta; } @@ -996,65 +1061,23 @@ moves_loop: // When in check, search starts from here && (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))) extension = 1; - // Shuffle extension - else if ( PvNode - && pos.rule50_count() > 18 - && depth < 3 - && ++thisThread->shuffleExts < thisThread->nodes.load(std::memory_order_relaxed) / 4) // To avoid too many extensions - extension = 1; - // Passed pawn extension else if ( move == ss->killers[0] && pos.advanced_pawn_push(move) && pos.pawn_passed(us, to_sq(move))) extension = 1; + // Last captures extension + else if ( PieceValue[EG][pos.captured_piece()] > PawnValueEg + && pos.non_pawn_material() <= 2 * RookValueMg) + extension = 1; + // Castling extension if (type_of(move) == CASTLING) extension = 1; - // Calculate new depth for this move - newDepth = depth - 1 + extension; - - // Step 14. Pruning at shallow depth (~170 Elo) - if ( !rootNode - && pos.non_pawn_material(us) - && bestValue > VALUE_MATED_IN_MAX_PLY) - { - // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold - moveCountPruning = moveCount >= futility_move_count(improving, depth); - - if ( !captureOrPromotion - && !givesCheck - && (!pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg)) - { - // Move count based pruning - if (moveCountPruning) - continue; - - // Reduced depth of the next LMR search - int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); - - // Countermoves based pruning (~20 Elo) - if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) - && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold - && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) - continue; - - // Futility pruning: parent node (~2 Elo) - if ( lmrDepth < 6 - && !inCheck - && ss->staticEval + 250 + 211 * lmrDepth <= alpha) - continue; - - // Prune moves with negative SEE (~10 Elo) - if (!pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) - continue; - } - else if ( !(givesCheck && extension) - && !pos.see_ge(move, Value(-199) * depth)) // (~20 Elo) - continue; - } + // Add extension to new depth + newDepth += extension; // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); @@ -1068,7 +1091,10 @@ moves_loop: // When in check, search starts from here // Update the current move (this must be done after singular extension search) ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[priorCapture][movedPiece][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[inCheck] + [captureOrPromotion] + [movedPiece] + [to_sq(move)]; // Step 15. Make the move pos.do_move(move, st, givesCheck); @@ -1081,10 +1107,15 @@ moves_loop: // When in check, search starts from here && ( !captureOrPromotion || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha - || cutNode)) + || cutNode + || thisThread->ttHitAverage < 375 * ttHitAverageResolution * ttHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); + // Decrease reduction if the ttHit running average is large + if (thisThread->ttHitAverage > 500 * ttHitAverageResolution * ttHitAverageWindow / 1024) + r--; + // Reduction if other threads are searching this position. if (th.marked()) r++; @@ -1094,11 +1125,12 @@ moves_loop: // When in check, search starts from here r -= 2; // Decrease reduction if opponent's move count is high (~10 Elo) - if ((ss-1)->moveCount > 15) + if ((ss-1)->moveCount > 14) r--; // Decrease reduction if ttMove has been singularly extended - r -= singularLMR; + if (singularLMR) + r -= 2; if (!captureOrPromotion) { @@ -1121,7 +1153,7 @@ moves_loop: // When in check, search starts from here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4729; + - 4926; // Reset statScore to zero if negative and most stats shows >= 0 if ( ss->statScore < 0 @@ -1131,10 +1163,10 @@ moves_loop: // When in check, search starts from here ss->statScore = 0; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -99 && (ss-1)->statScore < -116) + if (ss->statScore >= -102 && (ss-1)->statScore < -114) r--; - else if ((ss-1)->statScore >= -117 && ss->statScore < -144) + else if ((ss-1)->statScore >= -116 && ss->statScore < -154) r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) @@ -1145,17 +1177,17 @@ moves_loop: // When in check, search starts from here value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - doFullDepthSearch = (value > alpha && d != newDepth), doLMR = true; + doFullDepthSearch = (value > alpha && d != newDepth), didLMR = true; } else - doFullDepthSearch = !PvNode || moveCount > 1, doLMR = false; + doFullDepthSearch = !PvNode || moveCount > 1, didLMR = false; // Step 17. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); - if (doLMR && !captureOrPromotion) + if (didLMR && !captureOrPromotion) { int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); @@ -1270,21 +1302,11 @@ moves_loop: // When in check, search starts from here if (!moveCount) bestValue = excludedMove ? alpha : inCheck ? mated_in(ss->ply) : VALUE_DRAW; - else if (bestMove) - { - // Quiet best move: update move sorting heuristics - if (!pos.capture_or_promotion(bestMove)) - update_quiet_stats(pos, ss, bestMove, quietsSearched, quietCount, - stat_bonus(depth + (bestValue > beta + PawnValueMg))); - update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth + 1)); - - // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted - if ( ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0])) - && !priorCapture) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); + else if (bestMove) + update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, + quietsSearched, quietCount, capturesSearched, captureCount, depth); - } // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) && !priorCapture) @@ -1323,7 +1345,7 @@ moves_loop: // When in check, search starts from here Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - bool ttHit, pvHit, inCheck, givesCheck, evasionPrunable, priorCapture; + bool ttHit, pvHit, inCheck, givesCheck, captureOrPromotion, evasionPrunable; int moveCount; if (PvNode) @@ -1337,7 +1359,6 @@ moves_loop: // When in check, search starts from here (ss+1)->ply = ss->ply + 1; bestMove = MOVE_NONE; inCheck = pos.checkers(); - priorCapture = pos.captured_piece(); moveCount = 0; // Check for an immediate draw or maximum ply reached @@ -1355,7 +1376,7 @@ moves_loop: // When in check, search starts from here // Transposition table lookup posKey = pos.key(); tte = TT.probe(posKey, ttHit); - ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; + ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = ttHit ? tte->move() : MOVE_NONE; pvHit = ttHit && tte->is_pv(); @@ -1404,12 +1425,12 @@ moves_loop: // When in check, search starts from here if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 153; + futilityBase = bestValue + 154; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, - nullptr, (ss-4)->continuationHistory, - nullptr, (ss-6)->continuationHistory }; + nullptr , (ss-4)->continuationHistory, + nullptr , (ss-6)->continuationHistory }; // Initialize a MovePicker object for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, @@ -1426,6 +1447,7 @@ moves_loop: // When in check, search starts from here assert(is_ok(move)); givesCheck = pos.gives_check(move); + captureOrPromotion = pos.capture_or_promotion(move); moveCount++; @@ -1459,9 +1481,7 @@ moves_loop: // When in check, search starts from here && !pos.capture(move); // Don't search moves with negative SEE values - if ( (!inCheck || evasionPrunable) - && (!givesCheck || !(pos.blockers_for_king(~pos.side_to_move()) & from_sq(move))) - && !pos.see_ge(move)) + if ( (!inCheck || evasionPrunable) && !pos.see_ge(move)) continue; // Speculative prefetch as early as possible @@ -1475,7 +1495,10 @@ moves_loop: // When in check, search starts from here } ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[priorCapture][pos.moved_piece(move)][to_sq(move)]; + ss->continuationHistory = &thisThread->continuationHistory[inCheck] + [captureOrPromotion] + [pos.moved_piece(move)] + [to_sq(move)]; // Make and search the move pos.do_move(move, st, givesCheck); @@ -1537,11 +1560,11 @@ moves_loop: // When in check, search starts from here // from the transposition table (which refers to the plies to mate/be mated // from current position) to "plies to mate/be mated from the root". - Value value_from_tt(Value v, int ply) { + Value value_from_tt(Value v, int ply, int r50c) { return v == VALUE_NONE ? VALUE_NONE - : v >= VALUE_MATE_IN_MAX_PLY ? v - ply - : v <= VALUE_MATED_IN_MAX_PLY ? v + ply : v; + : v >= VALUE_MATE_IN_MAX_PLY ? VALUE_MATE - v > 99 - r50c ? VALUE_MATE_IN_MAX_PLY : v - ply + : v <= VALUE_MATED_IN_MAX_PLY ? VALUE_MATE + v > 99 - r50c ? VALUE_MATED_IN_MAX_PLY : v + ply : v; } @@ -1555,43 +1578,65 @@ moves_loop: // When in check, search starts from here } - // update_continuation_histories() updates histories of the move pairs formed - // by moves at ply -1, -2, and -4 with current move. + // update_all_stats() updates stats at the end of search() when a bestMove is found - void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { + void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, + Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) { - for (int i : {1, 2, 4, 6}) - if (is_ok((ss-i)->currentMove)) - (*(ss-i)->continuationHistory)[pc][to] << bonus; - } + int bonus1, bonus2; + Color us = pos.side_to_move(); + 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))); + bonus1 = stat_bonus(depth + 1); + bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus + : stat_bonus(depth); // smaller bonus - // update_capture_stats() updates move sorting heuristics when a new capture best move is found + if (!pos.capture_or_promotion(bestMove)) + { + update_quiet_stats(pos, ss, bestMove, bonus2); - void update_capture_stats(const Position& pos, Move move, - Move* captures, int captureCount, int bonus) { + // Decrease all the non-best quiet moves + for (int i = 0; i < quietCount; ++i) + { + thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bonus2; + update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), to_sq(quietsSearched[i]), -bonus2); + } + } + else + captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1; - CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory; - Piece moved_piece = pos.moved_piece(move); - PieceType captured = type_of(pos.piece_on(to_sq(move))); + // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted + if ( ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0])) + && !pos.captured_piece()) + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1); - if (pos.capture_or_promotion(move)) - captureHistory[moved_piece][to_sq(move)][captured] << bonus; + // Decrease all the non-best capture moves + for (int i = 0; i < captureCount; ++i) + { + moved_piece = pos.moved_piece(capturesSearched[i]); + captured = type_of(pos.piece_on(to_sq(capturesSearched[i]))); + captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -bonus1; + } + } - // Decrease all the other played capture moves - for (int i = 0; i < captureCount; ++i) - { - moved_piece = pos.moved_piece(captures[i]); - captured = type_of(pos.piece_on(to_sq(captures[i]))); - captureHistory[moved_piece][to_sq(captures[i])][captured] << -bonus; - } + + // update_continuation_histories() updates histories of the move pairs formed + // by moves at ply -1, -2, and -4 with current move. + + void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { + + for (int i : {1, 2, 4, 6}) + if (is_ok((ss-i)->currentMove)) + (*(ss-i)->continuationHistory)[pc][to] << bonus; } - // update_quiet_stats() updates move sorting heuristics when a new quiet best move is found + // update_quiet_stats() updates move sorting heuristics - void update_quiet_stats(const Position& pos, Stack* ss, Move move, - Move* quiets, int quietCount, int bonus) { + void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { if (ss->killers[0] != move) { @@ -1612,13 +1657,6 @@ moves_loop: // When in check, search starts from here Square prevSq = to_sq((ss-1)->currentMove); thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; } - - // Decrease all the other played quiet moves - for (int i = 0; i < quietCount; ++i) - { - thisThread->mainHistory[us][from_to(quiets[i])] << -bonus; - update_continuation_histories(ss, pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); - } } // When playing with strength handicap, choose best move among a set of RootMoves @@ -1704,7 +1742,7 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { for (size_t i = 0; i < multiPV; ++i) { - bool updated = (i <= pvIdx && rootMoves[i].score != -VALUE_INFINITE); + bool updated = rootMoves[i].score != -VALUE_INFINITE; if (depth == 1 && !updated) continue;