X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=24c21c4e71057bbd92cb068a3b9b484207af1097;hp=3d559a0d30613cd807e79daae4075b763eabdc84;hb=ee22b61f5e90a180af942828feb83cef90ea5683;hpb=99f3ad6858889bbc40c016c96c9f7e53a773e49e diff --git a/src/search.cpp b/src/search.cpp index 3d559a0d..24c21c4e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -46,7 +46,6 @@ namespace Search { namespace Tablebases { int Cardinality; - uint64_t Hits; bool RootInTB; bool UseRule50; Depth ProbeDepth; @@ -114,8 +113,8 @@ namespace { std::copy(newPv.begin(), newPv.begin() + 3, pv); StateInfo st[2]; - pos.do_move(newPv[0], st[0], pos.gives_check(newPv[0])); - pos.do_move(newPv[1], st[1], pos.gives_check(newPv[1])); + pos.do_move(newPv[0], st[0]); + pos.do_move(newPv[1], st[1]); expectedPosKey = pos.key(); pos.undo_move(newPv[1]); pos.undo_move(newPv[0]); @@ -154,16 +153,19 @@ namespace { {1, 0, 0, 0, 0, 1, 1 ,1}, }; + Value bonus(Depth depth) { int d = depth / ONE_PLY ; return Value(d * d + 2 * d - 2); } + Value penalty(Depth depth) { int d = depth / ONE_PLY ; return -Value(d * d + 4 * d + 1); } + const size_t HalfDensitySize = std::extent::value; EasyMoveManager EasyMove; Value DrawValue[COLOR_NB]; template - Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); + Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode, bool skipEarlyPruning); template - Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); + Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = DEPTH_ZERO); Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); @@ -184,8 +186,6 @@ void Search::init() { for (int mc = 1; mc < 64; ++mc) { double r = log(d) * log(mc) / 2; - if (r < 0.80) - continue; Reductions[NonPV][imp][d][mc] = int(std::round(r)); Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - 1, 0); @@ -215,6 +215,7 @@ void Search::clear() { th->counterMoves.clear(); th->fromTo.clear(); th->counterMoveHistory.clear(); + th->resetCalls = true; } Threads.main()->previousScore = VALUE_INFINITE; @@ -236,7 +237,7 @@ uint64_t Search::perft(Position& pos, Depth depth) { cnt = 1, nodes++; else { - pos.do_move(m, st, pos.gives_check(m)); + pos.do_move(m, st); cnt = leaf ? MoveList(pos).size() : perft(pos, depth - ONE_PLY); nodes += cnt; pos.undo_move(m); @@ -337,12 +338,12 @@ void MainThread::search() { void Thread::search() { - Stack stack[MAX_PLY+7], *ss = stack+5; // To allow referencing (ss-5) and (ss+2) + Stack stack[MAX_PLY+7], *ss = stack+4; // To allow referencing (ss-4) and (ss+2) Value bestValue, alpha, beta, delta; Move easyMove = MOVE_NONE; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); - std::memset(ss-5, 0, 8 * sizeof(Stack)); + std::memset(ss-4, 0, 7 * sizeof(Stack)); bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; @@ -406,7 +407,7 @@ void Thread::search() { // high/low anymore. while (true) { - bestValue = ::search(rootPos, ss, alpha, beta, rootDepth, false); + bestValue = ::search(rootPos, ss, alpha, beta, rootDepth, false, 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 @@ -462,11 +463,7 @@ void Thread::search() { if (!mainThread) continue; - if (Signals.stop) - sync_cout << "info nodes " << Threads.nodes_searched() - << " time " << Time.elapsed() << sync_endl; - - else if (PVIdx + 1 == multiPV || Time.elapsed() > 3000) + if (Signals.stop || PVIdx + 1 == multiPV || Time.elapsed() > 3000) sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl; } @@ -544,7 +541,7 @@ namespace { // search<>() is the main search function for both PV and non-PV nodes template - Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { + Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode, bool skipEarlyPruning) { const bool PvNode = NT == PV; const bool rootNode = PvNode && (ss-1)->ply == 0; @@ -571,6 +568,7 @@ namespace { Thread* thisThread = pos.this_thread(); inCheck = pos.checkers(); moveCount = quietCount = ss->moveCount = 0; + ss->history = VALUE_ZERO; bestValue = -VALUE_INFINITE; ss->ply = (ss-1)->ply + 1; @@ -578,9 +576,13 @@ namespace { if (thisThread->resetCalls.load(std::memory_order_relaxed)) { thisThread->resetCalls = false; - thisThread->callsCnt = 0; + // At low node count increase the checking rate to about 0.1% of nodes + // otherwise use a default value. + thisThread->callsCnt = Limits.nodes ? std::min((int64_t)4096, Limits.nodes / 1024) + : 4096; } - if (++thisThread->callsCnt > 4096) + + if (--thisThread->callsCnt <= 0) { for (Thread* th : Threads) th->resetCalls = true; @@ -615,8 +617,8 @@ namespace { ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; ss->counterMoves = nullptr; - (ss+1)->skipEarlyPruning = false; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; + Square prevSq = to_sq((ss-1)->currentMove); // Step 4. Transposition table lookup. We don't want the score of a partial // search to overwrite a previous full search TT value, so we use a different @@ -639,21 +641,12 @@ namespace { // If ttMove is quiet, update killers, history, counter move on TT hit if (ttValue >= beta && ttMove) { - int d = depth / ONE_PLY; - if (!pos.capture_or_promotion(ttMove)) - { - Value bonus = Value(d * d + 2 * d - 2); - update_stats(pos, ss, ttMove, nullptr, 0, bonus); - } + update_stats(pos, ss, ttMove, nullptr, 0, bonus(depth)); // Extra penalty for a quiet TT move in previous ply when it gets refuted if ((ss-1)->moveCount == 1 && !pos.captured_piece()) - { - Value penalty = Value(d * d + 4 * d + 1); - Square prevSq = to_sq((ss-1)->currentMove); - update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, -penalty); - } + update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, penalty(depth)); } return ttValue; } @@ -668,11 +661,12 @@ namespace { && pos.rule50_count() == 0 && !pos.can_castle(ANY_CASTLING)) { - int found, v = Tablebases::probe_wdl(pos, &found); + TB::ProbeState err; + TB::WDLScore v = Tablebases::probe_wdl(pos, &err); - if (found) + if (err != TB::ProbeState::FAIL) { - TB::Hits++; + thisThread->tbHits++; int drawScore = TB::UseRule50 ? 1 : 0; @@ -717,7 +711,7 @@ namespace { ss->staticEval, TT.generation()); } - if (ss->skipEarlyPruning) + if (skipEarlyPruning) goto moves_loop; // Step 6. Razoring (skipped when in check) @@ -727,10 +721,10 @@ namespace { && eval + razor_margin[depth / ONE_PLY] <= alpha) { if (depth <= ONE_PLY) - return qsearch(pos, ss, alpha, beta, DEPTH_ZERO); + return qsearch(pos, ss, alpha, beta); Value ralpha = alpha - razor_margin[depth / ONE_PLY]; - Value v = qsearch(pos, ss, ralpha, ralpha+1, DEPTH_ZERO); + Value v = qsearch(pos, ss, ralpha, ralpha+1); if (v <= ralpha) return v; } @@ -758,10 +752,8 @@ namespace { Depth R = ((823 + 67 * depth / ONE_PLY) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY; pos.do_null_move(st); - (ss+1)->skipEarlyPruning = true; - nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -beta+1, DEPTH_ZERO) - : - search(pos, ss+1, -beta, -beta+1, depth-R, !cutNode); - (ss+1)->skipEarlyPruning = false; + nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -beta+1) + : - search(pos, ss+1, -beta, -beta+1, depth-R, !cutNode, true); pos.undo_null_move(); if (nullValue >= beta) @@ -774,10 +766,8 @@ namespace { return nullValue; // Do verification search at high depths - ss->skipEarlyPruning = true; - Value v = depth-R < ONE_PLY ? qsearch(pos, ss, beta-1, beta, DEPTH_ZERO) - : search(pos, ss, beta-1, beta, depth-R, false); - ss->skipEarlyPruning = false; + Value v = depth-R < ONE_PLY ? qsearch(pos, ss, beta-1, beta) + : search(pos, ss, beta-1, beta, depth-R, false, true); if (v >= beta) return nullValue; @@ -805,8 +795,8 @@ namespace { { ss->currentMove = move; ss->counterMoves = &thisThread->counterMoveHistory[pos.moved_piece(move)][to_sq(move)]; - pos.do_move(move, st, pos.gives_check(move)); - value = -search(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode); + pos.do_move(move, st); + value = -search(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode, false); pos.undo_move(move); if (value >= rbeta) return value; @@ -819,9 +809,7 @@ namespace { && (PvNode || ss->staticEval + 256 >= beta)) { Depth d = (3 * depth / (4 * ONE_PLY) - 2) * ONE_PLY; - ss->skipEarlyPruning = true; - search(pos, ss, alpha, beta, d, cutNode); - ss->skipEarlyPruning = false; + search(pos, ss, alpha, beta, d, cutNode, true); tte = TT.probe(posKey, ttHit); ttMove = ttHit ? tte->move() : MOVE_NONE; @@ -903,9 +891,7 @@ moves_loop: // When in check search starts from here Value rBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE); Depth d = (depth / (2 * ONE_PLY)) * ONE_PLY; ss->excludedMove = move; - ss->skipEarlyPruning = true; - value = search(pos, ss, rBeta - 1, rBeta, d, cutNode); - ss->skipEarlyPruning = false; + value = search(pos, ss, rBeta - 1, rBeta, d, cutNode, true); ss->excludedMove = MOVE_NONE; if (value < rBeta) @@ -948,9 +934,9 @@ moves_loop: // When in check search starts from here && !pos.see_ge(move, Value(-35 * lmrDepth * lmrDepth))) continue; } - else if ( depth < 7 * ONE_PLY + else if ( depth < 7 * ONE_PLY && !extension - && !pos.see_ge(move, Value(-35 * depth / ONE_PLY * depth / ONE_PLY))) + && !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) continue; } @@ -988,26 +974,32 @@ moves_loop: // When in check search starts from here // 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(). Also use see() instead of see_sign(), - // because the destination square is empty. + // hence break make_move(). else if ( type_of(move) == NORMAL - && type_of(pos.piece_on(to_sq(move))) != PAWN && !pos.see_ge(make_move(to_sq(move), from_sq(move)), VALUE_ZERO)) r -= 2 * ONE_PLY; + ss->history = thisThread->history[moved_piece][to_sq(move)] + + (cmh ? (*cmh )[moved_piece][to_sq(move)] : VALUE_ZERO) + + (fmh ? (*fmh )[moved_piece][to_sq(move)] : VALUE_ZERO) + + (fmh2 ? (*fmh2)[moved_piece][to_sq(move)] : VALUE_ZERO) + + thisThread->fromTo.get(~pos.side_to_move(), move) + - 8000; // Correction factor + + // Decrease/increase reduction by comparing opponent's stat score + if (ss->history > VALUE_ZERO && (ss-1)->history < VALUE_ZERO) + r -= ONE_PLY; + + else if (ss->history < VALUE_ZERO && (ss-1)->history > VALUE_ZERO) + r += ONE_PLY; + // Decrease/increase reduction for moves with a good/bad history - Value val = thisThread->history[moved_piece][to_sq(move)] - + (cmh ? (*cmh )[moved_piece][to_sq(move)] : VALUE_ZERO) - + (fmh ? (*fmh )[moved_piece][to_sq(move)] : VALUE_ZERO) - + (fmh2 ? (*fmh2)[moved_piece][to_sq(move)] : VALUE_ZERO) - + thisThread->fromTo.get(~pos.side_to_move(), move); - int rHist = (val - 8000) / 20000; - r = std::max(DEPTH_ZERO, (r / ONE_PLY - rHist) * ONE_PLY); + r = std::max(DEPTH_ZERO, (r / ONE_PLY - ss->history / 20000) * ONE_PLY); } Depth d = std::max(newDepth - r, ONE_PLY); - value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); + value = -search(pos, ss+1, -(alpha+1), -alpha, d, true, false); doFullDepthSearch = (value > alpha && d != newDepth); } @@ -1017,9 +1009,9 @@ moves_loop: // When in check search starts from here // Step 16. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) value = newDepth < ONE_PLY ? - givesCheck ? -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) - : -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); + givesCheck ? -qsearch(pos, ss+1, -(alpha+1), -alpha) + : -qsearch(pos, ss+1, -(alpha+1), -alpha) + : - search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode, false); // For PV nodes only, do a full PV search on the first move or after a fail // high (in the latter case search only if value < beta), otherwise let the @@ -1030,9 +1022,9 @@ moves_loop: // When in check search starts from here (ss+1)->pv[0] = MOVE_NONE; value = newDepth < ONE_PLY ? - givesCheck ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) - : -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -beta, -alpha, newDepth, false); + givesCheck ? -qsearch(pos, ss+1, -beta, -alpha) + : -qsearch(pos, ss+1, -beta, -alpha) + : - search(pos, ss+1, -beta, -alpha, newDepth, false, false); } // Step 17. Undo move @@ -1128,33 +1120,20 @@ moves_loop: // When in check search starts from here : inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()]; else if (bestMove) { - int d = depth / ONE_PLY; // Quiet best move: update killers, history and countermoves if (!pos.capture_or_promotion(bestMove)) - { - Value bonus = Value(d * d + 2 * d - 2); - update_stats(pos, ss, bestMove, quietsSearched, quietCount, bonus); - } + update_stats(pos, ss, bestMove, quietsSearched, quietCount, bonus(depth)); // Extra penalty for a quiet TT move in previous ply when it gets refuted if ((ss-1)->moveCount == 1 && !pos.captured_piece()) - { - Value penalty = Value(d * d + 4 * d + 1); - Square prevSq = to_sq((ss-1)->currentMove); - update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, -penalty); - } + update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, penalty(depth)); } // Bonus for prior countermove that caused the fail low else if ( depth >= 3 * ONE_PLY && !pos.captured_piece() && is_ok((ss-1)->currentMove)) - { - int d = depth / ONE_PLY; - Value bonus = Value(d * d + 2 * d - 2); - Square prevSq = to_sq((ss-1)->currentMove); - update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, bonus); - } + update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, bonus(depth)); tte->save(posKey, value_to_tt(bestValue, ss->ply), bestValue >= beta ? BOUND_LOWER : @@ -1168,8 +1147,7 @@ moves_loop: // When in check search starts from here // qsearch() is the quiescence search function, which is called by the main - // search function when the remaining depth is zero (or, to be more precise, - // less than ONE_PLY). + // search function with depth zero, or recursively with depth less than ONE_PLY. template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { @@ -1519,7 +1497,7 @@ moves_loop: // When in check search starts from here if ( (Limits.use_time_management() && elapsed > Time.maximum() - 10) || (Limits.movetime && elapsed >= Limits.movetime) - || (Limits.nodes && Threads.nodes_searched() >= Limits.nodes)) + || (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes)) Signals.stop = true; } @@ -1536,7 +1514,8 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { const RootMoves& rootMoves = pos.this_thread()->rootMoves; size_t PVIdx = pos.this_thread()->PVIdx; size_t multiPV = std::min((size_t)Options["MultiPV"], rootMoves.size()); - uint64_t nodes_searched = Threads.nodes_searched(); + uint64_t nodesSearched = Threads.nodes_searched(); + uint64_t tbHits = Threads.tb_hits() + (TB::RootInTB ? rootMoves.size() : 0); for (size_t i = 0; i < multiPV; ++i) { @@ -1563,13 +1542,13 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { if (!tb && i == PVIdx) ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); - ss << " nodes " << nodes_searched - << " nps " << nodes_searched * 1000 / elapsed; + ss << " nodes " << nodesSearched + << " nps " << nodesSearched * 1000 / elapsed; if (elapsed > 1000) // Earlier makes little sense ss << " hashfull " << TT.hashfull(); - ss << " tbhits " << TB::Hits + ss << " tbhits " << tbHits << " time " << elapsed << " pv"; @@ -1596,7 +1575,7 @@ bool RootMove::extract_ponder_from_tt(Position& pos) { if (!pv[0]) return false; - pos.do_move(pv[0], st, pos.gives_check(pv[0])); + pos.do_move(pv[0], st); TTEntry* tte = TT.probe(pos.key(), ttHit); if (ttHit) @@ -1612,7 +1591,6 @@ bool RootMove::extract_ponder_from_tt(Position& pos) { void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) { - Hits = 0; RootInTB = false; UseRule50 = Options["Syzygy50MoveRule"]; ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY; @@ -1645,13 +1623,8 @@ void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) Cardinality = 0; } - if (RootInTB) - { - Hits = rootMoves.size(); - - if (!UseRule50) - TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1 - : TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1 - : VALUE_DRAW; - } + 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; }