X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=d7980bd7ab1d2ef23b7770e1344a7398a46d4c00;hp=435b720695f52caf5bd8fd3154a9f8a266726d62;hb=aef7076c344881954b4f586bd4779594d0b29037;hpb=0cfb653eeca1bc44b9d331498a9ccb3e9b97a9c1 diff --git a/src/search.cpp b/src/search.cpp index 435b7206..d7980bd7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -48,7 +48,6 @@ namespace Tablebases { bool RootInTB; bool UseRule50; Depth ProbeDepth; - Value Score; } namespace TB = Tablebases; @@ -93,7 +92,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth depth) { int d = depth / ONE_PLY; - return d > 17 ? 0 : d * d + 2 * d - 2; + return d > 17 ? 0 : 32 * d * d + 64 * d - 64; } // Skill structure is used to implement strength limit @@ -318,7 +317,17 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - int ct = Options["Contempt"] * PawnValueEg / 100; // From centipawns + int ct = int(Options["Contempt"]) * PawnValueEg / 100; // From centipawns + + // In analysis mode, adjust contempt in accordance with user preference + if (Limits.infinite || Options["UCI_AnalyseMode"]) + ct = Options["Analysis Contempt"] == "Off" ? 0 + : Options["Analysis Contempt"] == "Both" ? ct + : Options["Analysis Contempt"] == "White" && us == BLACK ? -ct + : Options["Analysis Contempt"] == "Black" && us == WHITE ? -ct + : ct; + + // In evaluate.cpp the evaluation is from the white point of view contempt = (us == WHITE ? make_score(ct, ct / 2) : -make_score(ct, ct / 2)); @@ -344,9 +353,20 @@ void Thread::search() { for (RootMove& rm : rootMoves) rm.previousScore = rm.score; + size_t PVFirst = 0; + PVLast = 0; + // MultiPV loop. We perform a full root search for each PV line for (PVIdx = 0; PVIdx < multiPV && !Threads.stop; ++PVIdx) { + if (PVIdx == PVLast) + { + PVFirst = PVLast; + for (PVLast++; PVLast < rootMoves.size(); PVLast++) + if (rootMoves[PVLast].TBRank != rootMoves[PVFirst].TBRank) + break; + } + // Reset UCI info selDepth for each depth and each PV line selDepth = 0; @@ -358,13 +378,11 @@ void Thread::search() { alpha = std::max(previousScore - delta,-VALUE_INFINITE); beta = std::min(previousScore + delta, VALUE_INFINITE); - ct = Options["Contempt"] * PawnValueEg / 100; // From centipawns - // Adjust contempt based on root move's previousScore (dynamic contempt) - ct += int(std::round(48 * atan(float(previousScore) / 128))); + int dct = ct + 88 * previousScore / (abs(previousScore) + 200); - contempt = (us == WHITE ? make_score(ct, ct / 2) - : -make_score(ct, ct / 2)); + contempt = (us == WHITE ? make_score(dct, dct / 2) + : -make_score(dct, dct / 2)); } // Start with a small aspiration window and, in the case of a fail @@ -380,7 +398,7 @@ void Thread::search() { // 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 // search the already searched PV lines are preserved. - std::stable_sort(rootMoves.begin() + PVIdx, rootMoves.end()); + std::stable_sort(rootMoves.begin() + PVIdx, rootMoves.begin() + PVLast); // If search has been stopped, we break immediately. Sorting is // safe because RootMoves is still valid, although it refers to @@ -420,7 +438,7 @@ void Thread::search() { } // Sort the PV lines searched so far and update the GUI - std::stable_sort(rootMoves.begin(), rootMoves.begin() + PVIdx + 1); + std::stable_sort(rootMoves.begin() + PVFirst, rootMoves.begin() + PVIdx + 1); if ( mainThread && (Threads.stop || PVIdx + 1 == multiPV || Time.elapsed() > 3000)) @@ -835,9 +853,10 @@ moves_loop: // When in check, search starts from here // 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. + // mode we also skip PV moves which 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.end(), move)) + thisThread->rootMoves.begin() + thisThread->PVLast, move)) continue; ss->moveCount = ++moveCount; @@ -959,29 +978,29 @@ moves_loop: // When in check, search starts from here { Depth r = reduction(improving, depth, moveCount); - if (captureOrPromotion) + if (captureOrPromotion) // (~5 Elo) r -= r ? ONE_PLY : DEPTH_ZERO; else { - // Decrease reduction if opponent's move count is high + // Decrease reduction if opponent's move count is high (~5 Elo) if ((ss-1)->moveCount > 15) r -= ONE_PLY; - // Decrease reduction for exact PV nodes + // Decrease reduction for exact PV nodes (~0 Elo) if (pvExact) r -= ONE_PLY; - // Increase reduction if ttMove is a capture + // Increase reduction if ttMove is a capture (~0 Elo) if (ttCapture) r += ONE_PLY; - // Increase reduction for cut nodes + // Increase reduction for cut nodes (~5 Elo) if (cutNode) r += 2 * ONE_PLY; // Decrease reduction for moves that escape a capture. Filter out // castling moves, because they are coded as "king captures rook" and - // hence break make_move(). + // hence break make_move(). (~5 Elo) else if ( type_of(move) == NORMAL && !pos.see_ge(make_move(to_sq(move), from_sq(move)))) r -= 2 * ONE_PLY; @@ -992,14 +1011,14 @@ moves_loop: // When in check, search starts from here + (*contHist[3])[movedPiece][to_sq(move)] - 4000; - // Decrease/increase reduction by comparing opponent's stat score + // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) if (ss->statScore >= 0 && (ss-1)->statScore < 0) r -= ONE_PLY; else if ((ss-1)->statScore >= 0 && ss->statScore < 0) r += ONE_PLY; - // Decrease/increase reduction for moves with a good/bad history + // Decrease/increase reduction for moves with a good/bad history (~30 Elo) r = std::max(DEPTH_ZERO, (r / ONE_PLY - ss->statScore / 20000) * ONE_PLY); } @@ -1085,6 +1104,7 @@ moves_loop: // When in check, search starts from here else { assert(value >= beta); // Fail high + ss->statScore = std::max(ss->statScore, 0); break; } } @@ -1548,7 +1568,7 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { Value v = updated ? rootMoves[i].score : rootMoves[i].previousScore; bool tb = TB::RootInTB && abs(v) < VALUE_MATE - MAX_PLY; - v = tb ? TB::Score : v; + v = tb ? rootMoves[i].TBScore : v; if (ss.rdbuf()->in_avail()) // Not at first line ss << "\n"; @@ -1609,52 +1629,49 @@ bool RootMove::extract_ponder_from_tt(Position& pos) { return pv.size() > 1; } - -void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) { +void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { RootInTB = false; - UseRule50 = Options["Syzygy50MoveRule"]; - ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY; - Cardinality = Options["SyzygyProbeLimit"]; + UseRule50 = bool(Options["Syzygy50MoveRule"]); + ProbeDepth = int(Options["SyzygyProbeDepth"]) * ONE_PLY; + Cardinality = int(Options["SyzygyProbeLimit"]); + bool dtz_available = true; - // Skip TB probing when no TB found: !TBLargest -> !TB::Cardinality + // Tables with fewer pieces than SyzygyProbeLimit are searched with + // ProbeDepth == DEPTH_ZERO if (Cardinality > MaxCardinality) { Cardinality = MaxCardinality; ProbeDepth = DEPTH_ZERO; } - if (Cardinality < popcount(pos.pieces()) || pos.can_castle(ANY_CASTLING)) - return; - - // Don't filter any moves if the user requested analysis on multiple - if (Options["MultiPV"] != 1) - return; + if (Cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) + { + // Rank moves using DTZ tables + RootInTB = root_probe(pos, rootMoves); - // If the current root position is in the tablebases, then RootMoves - // contains only moves that preserve the draw or the win. - RootInTB = root_probe(pos, rootMoves, TB::Score); + if (!RootInTB) + { + // DTZ tables are missing; try to rank moves using WDL tables + dtz_available = false; + RootInTB = root_probe_wdl(pos, rootMoves); + } + } if (RootInTB) - Cardinality = 0; // Do not probe tablebases during the search - - else // If DTZ tables are missing, use WDL tables as a fallback { - // Filter out moves that do not preserve the draw or the win. - RootInTB = root_probe_wdl(pos, rootMoves, TB::Score); + // Sort moves according to TB rank + std::sort(rootMoves.begin(), rootMoves.end(), + [](const RootMove &a, const RootMove &b) { return a.TBRank > b.TBRank; } ); - // Only probe during search if winning - if (RootInTB && TB::Score <= VALUE_DRAW) + // Probe during search only if DTZ is not available and we are winning + if (dtz_available || rootMoves[0].TBScore <= VALUE_DRAW) Cardinality = 0; } - - if (RootInTB && !UseRule50) - TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1 - : TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1 - : VALUE_DRAW; - - // Since root_probe() and root_probe_wdl() dirty the root move scores, - // we reset them to -VALUE_INFINITE - for (RootMove& rm : rootMoves) - rm.score = -VALUE_INFINITE; + else + { + // Assign the same rank to all moves + for (auto& m : rootMoves) + m.TBRank = 0; + } }