X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=72b257cb30b4138a74aba2088e8a310f04747240;hp=a4b2e7e40ac4c0b36a03a433426c27c9af928960;hb=cb1324312d051269700b74cb59759a12530d0b7a;hpb=aa88261a8f509fdabfe235042de1c1ea7a39a399 diff --git a/src/search.cpp b/src/search.cpp index a4b2e7e4..72b257cb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -67,8 +67,7 @@ namespace { const int skipPhase[] = { 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7 }; // Razoring and futility margin based on depth - // razor_margin[0] is unused as long as depth >= ONE_PLY in search - const int razor_margin[] = { 0, 570, 603, 554 }; + const int razor_margin = 600; Value futility_margin(Depth d) { return Value(150 * d / ONE_PLY); } // Futility and reductions lookup tables, initialized at startup @@ -108,7 +107,6 @@ namespace { void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, int bonus); void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCnt, int bonus); - bool pv_is_draw(Position& pos); // 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. @@ -193,11 +191,6 @@ void MainThread::search() { Time.init(Limits, us, rootPos.game_ply()); TT.new_search(); - int contempt = Options["Contempt"] * PawnValueEg / 100; // From centipawns - - Eval::Contempt = (us == WHITE ? make_score(contempt, contempt / 2) - : -make_score(contempt, contempt / 2)); - if (rootMoves.empty()) { rootMoves.emplace_back(MOVE_NONE); @@ -284,6 +277,7 @@ void Thread::search() { Depth lastBestMoveDepth = DEPTH_ZERO; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); double timeReduction = 1.0; + Color us = rootPos.side_to_move(); std::memset(ss-4, 0, 7 * sizeof(Stack)); for (int i = 4; i > 0; i--) @@ -308,6 +302,10 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); + int contempt = Options["Contempt"] * PawnValueEg / 100; // From centipawns + Eval::Contempt = (us == WHITE ? make_score(contempt, contempt / 2) + : -make_score(contempt, contempt / 2)); + // Iterative deepening loop until requested to stop or the target depth is reached while ( (rootDepth += ONE_PLY) < DEPTH_MAX && !Threads.stop @@ -342,6 +340,15 @@ void Thread::search() { delta = Value(18); alpha = std::max(rootMoves[PVIdx].previousScore - delta,-VALUE_INFINITE); beta = std::min(rootMoves[PVIdx].previousScore + delta, VALUE_INFINITE); + + // Adjust contempt based on current situation + contempt = Options["Contempt"] * PawnValueEg / 100; // From centipawns + contempt += bestValue > 500 ? 50: // Dynamic contempt + bestValue < -500 ? -50: + bestValue / 10; + + Eval::Contempt = (us == WHITE ? make_score(contempt, contempt / 2) + : -make_score(contempt, contempt / 2)); } // Start with a small aspiration window and, in the case of a fail @@ -436,24 +443,19 @@ void Thread::search() { bestValue - mainThread->previousScore }; int improvingFactor = std::max(229, std::min(715, 357 + 119 * F[0] - 6 * F[1])); - Color us = rootPos.side_to_move(); - bool thinkHard = bestValue == VALUE_DRAW - && Limits.time[us] - Time.elapsed() > Limits.time[~us] - && ::pv_is_draw(rootPos); - - double unstablePvFactor = 1 + mainThread->bestMoveChanges + thinkHard; + double unstablePvFactor = 1 + mainThread->bestMoveChanges; // if the bestMove is stable over several iterations, reduce time for this move, // the longer the move has been stable, the more. // Use part of the gained time from a previous stable move for the current move. timeReduction = 1; for (int i : {3, 4, 5}) - if (lastBestMoveDepth * i < completedDepth && !thinkHard) + if (lastBestMoveDepth * i < completedDepth ) timeReduction *= 1.3; unstablePvFactor *= std::pow(mainThread->previousTimeReduction, 0.51) / timeReduction; if ( rootMoves.size() == 1 - || Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 628) + || Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 605) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". @@ -500,7 +502,7 @@ namespace { Key posKey; Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; - Value bestValue, value, ttValue, eval; + Value bestValue, value, ttValue, eval, maxValue; bool ttHit, inCheck, givesCheck, singularExtensionNode, improving; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture, pvExact; Piece movedPiece; @@ -512,6 +514,7 @@ namespace { moveCount = captureCount = quietCount = ss->moveCount = 0; ss->statScore = 0; bestValue = -VALUE_INFINITE; + maxValue = VALUE_INFINITE; // Check for the available remaining time if (thisThread == Threads.main()) @@ -599,7 +602,7 @@ namespace { && !pos.can_castle(ANY_CASTLING)) { TB::ProbeState err; - TB::WDLScore v = Tablebases::probe_wdl(pos, &err); + TB::WDLScore wdl = Tablebases::probe_wdl(pos, &err); if (err != TB::ProbeState::FAIL) { @@ -607,15 +610,30 @@ namespace { int drawScore = TB::UseRule50 ? 1 : 0; - value = v < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply + 1 - : v > drawScore ? VALUE_MATE - MAX_PLY - ss->ply - 1 - : VALUE_DRAW + 2 * v * drawScore; + value = wdl < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply + 1 + : wdl > drawScore ? VALUE_MATE - MAX_PLY - ss->ply - 1 + : VALUE_DRAW + 2 * wdl * drawScore; + + Bound b = wdl < -drawScore ? BOUND_UPPER + : wdl > drawScore ? BOUND_LOWER : BOUND_EXACT; - tte->save(posKey, value_to_tt(value, ss->ply), BOUND_EXACT, - std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY), - MOVE_NONE, VALUE_NONE, TT.generation()); + if ( b == BOUND_EXACT + || (b == BOUND_LOWER ? value >= beta : value <= alpha)) + { + tte->save(posKey, value_to_tt(value, ss->ply), b, + std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY), + MOVE_NONE, VALUE_NONE, TT.generation()); - return value; + return value; + } + + if (PvNode) + { + if (b == BOUND_LOWER) + bestValue = value, alpha = std::max(alpha, bestValue); + else + maxValue = value; + } } } } @@ -654,12 +672,12 @@ namespace { // Step 6. Razoring (skipped when in check) if ( !PvNode && depth < 4 * ONE_PLY - && eval + razor_margin[depth / ONE_PLY] <= alpha) + && eval + razor_margin <= alpha) { if (depth <= ONE_PLY) return qsearch(pos, ss, alpha, alpha+1); - Value ralpha = alpha - razor_margin[depth / ONE_PLY]; + Value ralpha = alpha - razor_margin; Value v = qsearch(pos, ss, ralpha, ralpha+1); if (v <= ralpha) return v; @@ -676,7 +694,7 @@ namespace { if ( !PvNode && eval >= beta && ss->staticEval >= beta - 36 * depth / ONE_PLY + 225 - && (ss->ply >= thisThread->nmp_ply || ss->ply % 2 == thisThread->pair)) + && (ss->ply >= thisThread->nmp_ply || ss->ply % 2 != thisThread->nmp_odd)) { assert(eval - beta >= 0); @@ -698,21 +716,18 @@ namespace { if (nullValue >= VALUE_MATE_IN_MAX_PLY) nullValue = beta; - if (depth < 12 * ONE_PLY && abs(beta) < VALUE_KNOWN_WIN) + if (abs(beta) < VALUE_KNOWN_WIN && (depth < 12 * ONE_PLY || thisThread->nmp_ply)) return nullValue; // Do verification search at high depths - R += ONE_PLY; // disable null move pruning for side to move for the first part of the remaining search tree - int nmp_ply = thisThread->nmp_ply; - int pair = thisThread->pair; thisThread->nmp_ply = ss->ply + 3 * (depth-R) / 4; - thisThread->pair = (ss->ply % 2) == 0; + thisThread->nmp_odd = ss->ply % 2; Value v = depth-R < ONE_PLY ? qsearch(pos, ss, beta-1, beta) : search(pos, ss, beta-1, beta, depth-R, false, true); - thisThread->pair = pair; - thisThread->nmp_ply = nmp_ply; + + thisThread->nmp_odd = thisThread->nmp_ply = 0; if (v >= beta) return nullValue; @@ -1095,6 +1110,9 @@ moves_loop: // When in check search starts from here && is_ok((ss-1)->currentMove)) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth)); + if (PvNode) + bestValue = std::min(bestValue, maxValue); + if (!excludedMove) tte->save(posKey, value_to_tt(bestValue, ss->ply), bestValue >= beta ? BOUND_LOWER : @@ -1255,7 +1273,6 @@ moves_loop: // When in check search starts from here // Don't search moves with negative SEE values if ( (!InCheck || evasionPrunable) - && type_of(move) != PROMOTION && !pos.see_ge(move)) continue; @@ -1416,25 +1433,6 @@ moves_loop: // When in check search starts from here } } - - // Is the PV leading to a draw position? Assumes all pv moves are legal - bool pv_is_draw(Position& pos) { - - StateInfo st[MAX_PLY]; - auto& pv = pos.this_thread()->rootMoves[0].pv; - - for (size_t i = 0; i < pv.size(); ++i) - pos.do_move(pv[i], st[i]); - - bool isDraw = pos.is_draw(pv.size()); - - for (size_t i = pv.size(); i > 0; --i) - pos.undo_move(pv[i-1]); - - return isDraw; - } - - // When playing with strength handicap, choose best move among a set of RootMoves // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. @@ -1631,4 +1629,9 @@ void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) 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; }