X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=a712ce87bb77feca94399d739367e7049e98f3b1;hp=f511cce656eba0fce19b8eb2494bc8c78388e84d;hb=11c6cf720d4cdd882bc0f2c36e25910cf77fb57b;hpb=8a8640a761f266979c7130a49ebbc9ed4c680102 diff --git a/src/search.cpp b/src/search.cpp index f511cce6..a712ce87 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -112,14 +112,22 @@ namespace { return thisThread->state; } - // Skill structure is used to implement strength limit + // 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) + // results spanning a wide range of k values. struct Skill { - explicit Skill(int l) : level(l) {} - bool enabled() const { return level < 20; } - bool time_to_pick(Depth depth) const { return depth == 1 + level; } + Skill(int skill_level, int uci_elo) { + if (uci_elo) + level = std::clamp(std::pow((uci_elo - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0); + else + level = double(skill_level); + } + bool enabled() const { return level < 20.0; } + bool time_to_pick(Depth depth) const { return depth == 1 + int(level); } Move pick_best(size_t multiPV); - int level; + double level; Move best = MOVE_NONE; }; @@ -243,10 +251,11 @@ void MainThread::search() { Time.availableNodes += Limits.inc[us] - Threads.nodes_searched(); Thread* bestThread = this; + Skill skill = Skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); if ( int(Options["MultiPV"]) == 1 && !Limits.depth - && !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) + && !skill.enabled() && rootMoves[0].pv[0] != MOVE_NONE) bestThread = Threads.get_best_thread(); @@ -311,19 +320,7 @@ void Thread::search() { std::fill(&lowPlyHistory[MAX_LPH - 2][0], &lowPlyHistory.back().back() + 1, 0); size_t multiPV = size_t(Options["MultiPV"]); - - // Pick integer skill levels, but non-deterministically round up or down - // such that the average integer skill corresponds to the input floating point one. - // UCI_Elo is converted 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) results spanning a wide range of k values. - PRNG rng(now()); - double floatLevel = Options["UCI_LimitStrength"] ? - std::clamp(std::pow((Options["UCI_Elo"] - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0) : - double(Options["Skill Level"]); - int intLevel = int(floatLevel) + - ((floatLevel - int(floatLevel)) * 1024 > rng.rand() % 1024 ? 1 : 0); - Skill skill(intLevel); + 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. @@ -380,7 +377,7 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].previousScore; - delta = Value(17); + delta = Value(17) + int(prev) * prev / 16384; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); @@ -673,7 +670,7 @@ namespace { // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit - && tte->depth() > depth + && tte->depth() > depth - (thisThread->id() % 2 == 1) && ttValue != VALUE_NONE // Possible in case of TT access race && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) : (tte->bound() & BOUND_UPPER))) @@ -816,7 +813,7 @@ namespace { if ( !PvNode && depth < 9 && eval - futility_margin(depth, improving) >= beta - && eval < VALUE_KNOWN_WIN) // Do not return unproven wins + && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; // Step 8. Null move search with verification search (~40 Elo) @@ -825,7 +822,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 15 + 177 + && ss->staticEval >= beta - 20 * depth - improvement / 15 + 204 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -1159,10 +1156,9 @@ moves_loop: // When in check, search starts here // cases where we extend a son if it has good chances to be "interesting". if ( depth >= 3 && moveCount > 1 + 2 * rootNode - && ( !captureOrPromotion - || (cutNode && (ss-1)->moveCount > 1) - || !ss->ttPv) - && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) + && ( !ss->ttPv + || !captureOrPromotion + || (cutNode && (ss-1)->moveCount > 1))) { Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); @@ -1210,11 +1206,11 @@ moves_loop: // When in check, search starts here // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). - int deeper = r >= -1 ? 0 - : moveCount <= 3 ? 2 - : moveCount <= 5 ? 1 - : PvNode && depth > 6 ? 1 - : 0; + int deeper = r >= -1 ? 0 + : moveCount <= 5 ? 2 + : PvNode && depth > 6 ? 1 + : cutNode && moveCount <= 7 ? 1 + : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); @@ -1781,8 +1777,8 @@ 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 weakness = 120 - 2 * level; int maxScore = -VALUE_INFINITE; + double weakness = 120 - 2 * level; // Choose best move. For each move score we add two terms, both dependent on // weakness. One is deterministic and bigger for weaker levels, and one is @@ -1790,8 +1786,8 @@ moves_loop: // When in check, search starts here for (size_t i = 0; i < multiPV; ++i) { // This is our magic formula - int push = ( weakness * int(topScore - rootMoves[i].score) - + delta * (rng.rand() % weakness)) / 128; + int push = int(( weakness * int(topScore - rootMoves[i].score) + + delta * (rng.rand() % int(weakness))) / 128); if (rootMoves[i].score + push >= maxScore) {