X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fsearch.cpp;h=76d055e3039e3b3a8245eeee9a8520343d75d392;hb=9cd563cb54b4091c48e16b524b3c9c15b7824c4f;hp=8ace674d11a2f7a8845f68aa075fe01ec28921c3;hpb=0187546275cb015d42a7d99789f2f6a650b03651;p=stockfish diff --git a/src/search.cpp b/src/search.cpp index 8ace674d..76d055e3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -67,7 +67,7 @@ namespace { return Value(140 * (d - improving)); } - // Reductions lookup table, initialized at startup + // 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) { @@ -92,7 +92,7 @@ namespace { // Skill structure is used to implement strength limit. If we have an uci_elo then // we convert it to a suitable fractional skill level using anchoring to CCRL Elo - // (goldfish 1.13 = 2000) and a fit through Ordo derived Elo for match (TC 60+0.6) + // (goldfish 1.13 = 2000) and a fit through Ordo derived Elo for a match (TC 60+0.6) // results spanning a wide range of k values. struct Skill { Skill(int skill_level, int uci_elo) { @@ -304,7 +304,7 @@ void Thread::search() { Skill skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); // When playing with strength handicap enable MultiPV search that we will - // use behind the scenes to retrieve a set of possible moves. + // use behind-the-scenes to retrieve a set of possible moves. if (skill.enabled()) multiPV = std::max(multiPV, (size_t)4); @@ -321,7 +321,7 @@ void Thread::search() { if (mainThread) totBestMoveChanges /= 2; - // Save the last iteration's scores before first PV line is searched and + // Save the last iteration's scores before the first PV line is searched and // all the move scores except the (new) PV are set to -VALUE_INFINITE. for (RootMove& rm : rootMoves) rm.previousScore = rm.score; @@ -363,16 +363,16 @@ void Thread::search() { int failedHighCnt = 0; while (true) { - // Adjust the effective depth searched, but ensuring at least one effective increment for every + // Adjust the effective depth searched, but ensure at least one effective increment for every // four searchAgain steps (see issue #2717). Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4); bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the - // first and eventually the new best one are set to -VALUE_INFINITE + // first and eventually the new best one is set to -VALUE_INFINITE // and we want to keep the same order for all the moves except the - // new PV that goes to the front. Note that in case of MultiPV + // new PV that goes to the front. Note that in the case of MultiPV // search the already searched PV lines are preserved. std::stable_sort(rootMoves.begin() + pvIdx, rootMoves.begin() + pvLast); @@ -440,7 +440,7 @@ void Thread::search() { if (!mainThread) continue; - // If skill level is enabled and time is up, pick a sub-optimal best move + // If the skill level is enabled and time is up, pick a sub-optimal best move if (skill.enabled() && skill.time_to_pick(rootDepth)) skill.pick_best(multiPV); @@ -498,7 +498,7 @@ void Thread::search() { mainThread->previousTimeReduction = timeReduction; - // If skill level is enabled, swap best PV line with the sub-optimal one + // If the skill level is enabled, swap the best PV line with the sub-optimal one if (skill.enabled()) std::swap(rootMoves[0], *std::find(rootMoves.begin(), rootMoves.end(), skill.best ? skill.best : skill.pick_best(multiPV))); @@ -515,7 +515,7 @@ namespace { constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; - // Check if we have an upcoming move which draws by repetition, or + // 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 @@ -580,8 +580,8 @@ namespace { // would be at best mate_in(ss->ply+1), but if alpha is already bigger because // a shorter mate was found upward in the tree then there is no need to search // because we will never beat the current alpha. Same logic but with reversed - // signs applies also in the opposite condition of being mated instead of giving - // mate. In this case return a fail-high score. + // signs apply also in the opposite condition of being mated instead of giving + // mate. In this case, return a fail-high score. alpha = std::max(mated_in(ss->ply), alpha); beta = std::min(mate_in(ss->ply+1), beta); if (alpha >= beta) @@ -734,7 +734,7 @@ namespace { else { ss->staticEval = eval = evaluate(pos); - // Save static evaluation into transposition table + // Save static evaluation into the transposition table tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } @@ -779,10 +779,11 @@ namespace { && (ss-1)->statScore < 17329 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 21 * depth - improvement / 13 + 258 + && ss->staticEval >= beta - 21 * depth + 258 && !excludedMove && pos.non_pawn_material(us) - && (ss->ply >= thisThread->nmpMinPly)) + && ss->ply >= thisThread->nmpMinPly + && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); @@ -801,10 +802,9 @@ namespace { if (nullValue >= beta) { // Do not return unproven mate or TB scores - if (nullValue >= VALUE_TB_WIN_IN_MAX_PLY) - nullValue = beta; + nullValue = std::min(nullValue, VALUE_TB_WIN_IN_MAX_PLY-1); - if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 14)) + if (thisThread->nmpMinPly || depth < 14) return nullValue; assert(!thisThread->nmpMinPly); // Recursive verification is not allowed @@ -845,10 +845,10 @@ namespace { if ( !PvNode && depth > 3 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY - // if value from transposition table is lower than probCutBeta, don't attempt probCut + // If value from transposition table is lower than probCutBeta, don't attempt probCut // there and in further interactions with transposition table cutoff depth is set to depth - 3 // because probCut search has depth set to depth - 4 but we also do a move before it - // so effective depth is equal to depth - 3 + // So effective depth is equal to depth - 3 && !( tte->depth() >= depth - 3 && ttValue != VALUE_NONE && ttValue < probCutBeta)) @@ -920,7 +920,7 @@ moves_loop: // When in check, search starts here moveCountPruning = singularQuietLMR = false; // Indicate PvNodes that will probably fail low if the node was searched - // at a depth equal or greater than the current depth, and the result of this search was a fail low. + // at a depth equal to or greater than the current depth, and the result of this search was a fail low. bool likelyFailLow = PvNode && ttMove && (tte->bound() & BOUND_UPPER) @@ -936,8 +936,8 @@ moves_loop: // When in check, search starts here continue; // At root obey the "searchmoves" option and skip moves not listed in Root - // Move List. As a consequence any illegal move is also skipped. In MultiPV - // mode we also skip PV moves which have been already searched and those + // Move List. As a consequence, any illegal move is also skipped. In MultiPV + // mode we also skip PV moves that have been already searched and those // of lower "TB rank" if we are in a TB root position. if (rootNode && !std::count(thisThread->rootMoves.begin() + thisThread->pvIdx, thisThread->rootMoves.begin() + thisThread->pvLast, move)) @@ -1005,7 +1005,7 @@ moves_loop: // When in check, search starts here { Square sq = pop_lsb(leftEnemies); attacks |= pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied; - // don't consider pieces which were already threatened/hanging before SEE exchanges + // 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; } @@ -1090,7 +1090,7 @@ moves_loop: // When in check, search starts here // Our ttMove is assumed to fail high, and now we failed high also on a reduced // 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. + // a softbound. else if (singularBeta >= beta) return singularBeta; @@ -1098,6 +1098,10 @@ moves_loop: // When in check, search starts here else if (ttValue >= beta) extension = -2 - !PvNode; + // 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; + // If the eval of ttMove is less than value, we reduce it (negative extension) (~1 Elo) else if (ttValue <= value) extension = -1; @@ -1182,7 +1186,7 @@ moves_loop: // When in check, search starts here // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has - // been searched. In general we would like to reduce them, but there are many + // been searched. In general, we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". if ( depth >= 2 && moveCount > 1 + (PvNode && ss->ply <= 1) @@ -1197,10 +1201,10 @@ moves_loop: // When in check, search starts here value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - // Do full depth search when reduced LMR search fails high + // Do a full-depth search when reduced LMR search fails high if (value > alpha && d < newDepth) { - // Adjust full depth search based on LMR results - if result + // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower const bool doDeeperSearch = value > (bestValue + 64 + 11 * (newDepth - d)); const bool doEvenDeeperSearch = value > alpha + 711 && ss->doubleExtensions <= 6; @@ -1221,7 +1225,7 @@ moves_loop: // When in check, search starts here } } - // Step 18. Full depth search when LMR is skipped. If expected reduction is high, reduce its depth by 1. + // Step 18. Full-depth search when LMR is skipped. If expected reduction is high, reduce its depth by 1. else if (!PvNode || moveCount > 1) { // Increase reduction for cut nodes and not ttMove (~1 Elo) @@ -1294,7 +1298,7 @@ moves_loop: // When in check, search starts here ++thisThread->bestMoveChanges; } else - // All other moves but the PV are set to the lowest value: this + // All other moves but the PV, are set to the lowest value: this // is not a problem when sorting because the sort is stable and the // move position in the list is preserved - just the PV is pushed up. rm.score = -VALUE_INFINITE; @@ -1319,12 +1323,12 @@ moves_loop: // When in check, search starts here } else { - // Reduce other moves if we have found at least one score improvement (~1 Elo) - // Reduce more for depth > 3 and depth < 12 (~1 Elo) - if ( depth > 1 + // Reduce other moves if we have found at least one score improvement (~2 Elo) + if ( depth > 2 + && depth < 12 && beta < 14362 && value > -12393) - depth -= depth > 3 && depth < 12 ? 2 : 1; + depth -= 2; assert(depth > 0); alpha = value; // Update alpha! Always alpha < beta @@ -1333,7 +1337,7 @@ moves_loop: // When in check, search starts here } - // If the move is worse than some previously searched move, remember it to update its stats later + // If the move is worse than some previously searched move, remember it, to update its stats later if (move != bestMove) { if (capture && captureCount < 32) @@ -1345,7 +1349,7 @@ moves_loop: // When in check, search starts here } // The following condition would detect a stop only after move loop has been - // completed. But in this case bestValue is valid because we have fully + // completed. But in this case, bestValue is valid because we have fully // searched our subtree, and we can anyhow save the result in TT. /* if (Threads.stop) @@ -1364,7 +1368,7 @@ moves_loop: // When in check, search starts here ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW; - // If there is a move which produces search value greater than alpha we update stats of searched moves + // If there is a move that produces search value greater than alpha we update the stats of searched moves else if (bestMove) update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, quietsSearched, quietCount, capturesSearched, captureCount, depth); @@ -1555,23 +1559,23 @@ moves_loop: // When in check, search starts here bestValue = std::max(bestValue, futilityBase); continue; } - } - - // We prune after the second quiet check evasion move, where being 'in check' is - // implicitly checked through the counter, and being a 'quiet move' apart from - // being a tt move is assumed after an increment because captures are pushed ahead. - if (quietCheckEvasions > 1) - break; - - // Continuation history based pruning (~3 Elo) - if ( !capture - && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 - && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) - continue; + } - // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, Value(-95))) - continue; + // We prune after the second quiet check evasion move, where being 'in check' is + // implicitly checked through the counter, and being a 'quiet move' apart from + // being a tt move is assumed after an increment because captures are pushed ahead. + if (quietCheckEvasions > 1) + break; + + // Continuation history based pruning (~3 Elo) + if ( !capture + && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 + && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) + continue; + + // Do not search moves with bad enough SEE values (~5 Elo) + if (!pos.see_ge(move, Value(-95))) + continue; } // Speculative prefetch as early as possible @@ -1747,7 +1751,7 @@ moves_loop: // When in check, search starts here for (int i : {1, 2, 4, 6}) { - // Only update first 2 continuation histories if we are in check + // Only update the first 2 continuation histories if we are in check if (ss->inCheck && i > 2) break; if (is_ok((ss-i)->currentMove)) @@ -1780,7 +1784,7 @@ moves_loop: // When in check, search starts here } } - // When playing with strength handicap, choose best move among a set of RootMoves + // When playing with strength handicap, choose the best move among a set of RootMoves // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. Move Skill::pick_best(size_t multiPV) { @@ -1911,7 +1915,7 @@ string UCI::pv(const Position& pos, Depth depth) { /// RootMove::extract_ponder_from_tt() is called in case we have no ponder move /// before exiting the search, for instance, in case we stop the search during a /// fail high at root. We try hard to have a ponder move to return to the GUI, -/// otherwise in case of 'ponder on' we have nothing to think on. +/// otherwise in case of 'ponder on' we have nothing to think about. bool RootMove::extract_ponder_from_tt(Position& pos) {