X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=39a731936f4b6e2b59bea6c203a31c643a0f9c62;hp=78e4748f5ce64a51efbd2b33821f3579b8d87097;hb=27c5cb59127101d834636d4faa0062d4e7bd05ce;hpb=307a5a4f63169fd215860dc478dcf2a9db2c46e8 diff --git a/src/search.cpp b/src/search.cpp index 78e4748f..39a73193 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -37,7 +37,7 @@ namespace Search { - volatile SignalsType Signals; + SignalsType Signals; LimitsType Limits; StateStackPtr SetupStates; } @@ -174,9 +174,9 @@ void Search::init() { } -/// Search::reset() clears all search memory, to obtain reproducible search results +/// Search::clear() resets to zero search state, to obtain reproducible results -void Search::reset () { +void Search::clear() { TT.clear(); CounterMovesHistory.clear(); @@ -216,7 +216,7 @@ uint64_t Search::perft(Position& pos, Depth depth) { return nodes; } -template uint64_t Search::perft(Position& pos, Depth depth); +template uint64_t Search::perft(Position&, Depth); /// MainThread::think() is called by the main thread when the program receives @@ -329,10 +329,22 @@ void MainThread::think() { wait(Signals.stop); } - sync_cout << "bestmove " << UCI::move(rootMoves[0].pv[0], rootPos.is_chess960()); + // Check if there are threads with a better score than main thread. + Thread* bestThread = this; + for (Thread* th : Threads) + if ( th->completedDepth > bestThread->completedDepth + && th->rootMoves[0].score > bestThread->rootMoves[0].score) + bestThread = th; + + // Send new PV when needed. + // FIXME: Breaks multiPV, and skill levels + if (bestThread != this) + sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth, -VALUE_INFINITE, VALUE_INFINITE) << sync_endl; + + sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); - if (rootMoves[0].pv.size() > 1 || rootMoves[0].extract_ponder_from_tt(rootPos)) - std::cout << " ponder " << UCI::move(rootMoves[0].pv[1], rootPos.is_chess960()); + if (bestThread->rootMoves[0].pv.size() > 1 || bestThread->rootMoves[0].extract_ponder_from_tt(rootPos)) + std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960()); std::cout << sync_endl; } @@ -344,7 +356,7 @@ void MainThread::think() { void Thread::search(bool isMainThread) { - Stack* ss = stack + 2; // To allow referencing (ss-2) and (ss+2) + Stack stack[MAX_PLY+4], *ss = stack+2; // To allow referencing (ss-2) and (ss+2) Value bestValue, alpha, beta, delta; Move easyMove = MOVE_NONE; @@ -352,6 +364,7 @@ void Thread::search(bool isMainThread) { bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; + completedDepth = DEPTH_ZERO; if (isMainThread) { @@ -376,7 +389,7 @@ void Thread::search(bool isMainThread) { { // Set up the new depth for the helper threads if (!isMainThread) - rootDepth = Threads.main()->rootDepth + Depth(int(3 * log(1 + this->idx))); + rootDepth = Threads.main()->rootDepth + Depth(int(2.2 * log(1 + this->idx))); // Age out PV variability metric if (isMainThread) @@ -472,6 +485,9 @@ void Thread::search(bool isMainThread) { sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl; } + if (!Signals.stop) + completedDepth = rootDepth; + if (!isMainThread) continue; @@ -539,12 +555,7 @@ void Thread::search(bool isMainThread) { namespace { - // search<>() is the main search function for both PV and non-PV nodes and for - // normal and SplitPoint nodes. When called just after a split point the search - // is simpler because we have already probed the hash table, done a null move - // search, and searched the first move before splitting, so we don't have to - // repeat all this work again. We also don't need to store anything to the hash - // table here: This is taken care of after we return from the split point. + // 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) { @@ -581,7 +592,7 @@ namespace { if (!RootNode) { // Step 2. Check for aborted search and immediate draw - if (Signals.stop || pos.is_draw() || ss->ply >= MAX_PLY) + if (Signals.stop.load(std::memory_order_relaxed) || pos.is_draw() || ss->ply >= MAX_PLY) return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; @@ -1008,7 +1019,7 @@ moves_loop: // When in check search starts from here // Finished searching the move. If a stop occurred, the return value of // the search cannot be trusted, and we return immediately without // updating best move, PV and TT. - if (Signals.stop) + if (Signals.stop.load(std::memory_order_relaxed)) return VALUE_ZERO; if (RootNode) @@ -1100,7 +1111,7 @@ moves_loop: // When in check search starts from here && is_ok((ss - 1)->currentMove) && is_ok((ss - 2)->currentMove)) { - Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY)); + Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + depth / ONE_PLY - 1); Square prevPrevSq = to_sq((ss - 2)->currentMove); CounterMovesStats& prevCmh = CounterMovesHistory[pos.piece_on(prevPrevSq)][prevPrevSq]; prevCmh.update(pos.piece_on(prevSq), prevSq, bonus); @@ -1364,8 +1375,8 @@ moves_loop: // When in check search starts from here } - // update_stats() updates killers, history, countermove history and - // countermoves stats for a quiet best move. + // update_stats() updates killers, history, countermove and countermove + // history when a new quiet best move is found. void update_stats(const Position& pos, Stack* ss, Move move, Depth depth, Move* quiets, int quietsCnt) { @@ -1376,7 +1387,7 @@ moves_loop: // When in check search starts from here ss->killers[0] = move; } - Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY)); + Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + depth / ONE_PLY - 1); Square prevSq = to_sq((ss-1)->currentMove); CounterMovesStats& cmh = CounterMovesHistory[pos.piece_on(prevSq)][prevSq]; @@ -1399,14 +1410,14 @@ moves_loop: // When in check search starts from here cmh.update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); } - // Extra penalty for TT move in previous ply when it gets refuted + // Extra penalty for a quiet TT move in previous ply when it gets refuted if ( (ss-1)->moveCount == 1 && !pos.captured_piece_type() && is_ok((ss-2)->currentMove)) { Square prevPrevSq = to_sq((ss-2)->currentMove); CounterMovesStats& prevCmh = CounterMovesHistory[pos.piece_on(prevPrevSq)][prevPrevSq]; - prevCmh.update(pos.piece_on(prevSq), prevSq, -bonus - 2 * depth / ONE_PLY - 1); + prevCmh.update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY); } } @@ -1416,23 +1427,23 @@ moves_loop: // When in check search starts from here Move Skill::pick_best(size_t multiPV) { - // PRNG sequence should be non-deterministic, so we seed it with the time at init const Search::RootMoveVector& rootMoves = Threads.main()->rootMoves; - static PRNG rng(now()); + static PRNG rng(now()); // PRNG sequence should be non-deterministic // RootMoves are already sorted by score in descending order - int variance = std::min(rootMoves[0].score - rootMoves[multiPV - 1].score, PawnValueMg); + Value topScore = rootMoves[0].score; + int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValueMg); int weakness = 120 - 2 * level; int maxScore = -VALUE_INFINITE; - // Choose best move. For each move score we add two terms both dependent on + // Choose best move. For each move score we add two terms, both dependent on // weakness. One deterministic and bigger for weaker levels, and one random, // then we choose the move with the resulting highest score. for (size_t i = 0; i < multiPV; ++i) { // This is our magic formula - int push = ( weakness * int(rootMoves[0].score - rootMoves[i].score) - + variance * (rng.rand() % weakness)) / 128; + int push = ( weakness * int(topScore - rootMoves[i].score) + + delta * (rng.rand() % weakness)) / 128; if (rootMoves[i].score + push > maxScore) { @@ -1440,6 +1451,7 @@ moves_loop: // When in check search starts from here best = rootMoves[i].pv[0]; } } + return best; } @@ -1517,7 +1529,8 @@ void RootMove::insert_pv_in_tt(Position& pos) { TTEntry* tte = TT.probe(pos.key(), ttHit); if (!ttHit || tte->move() != m) // Don't overwrite correct entries - tte->save(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, m, VALUE_NONE, TT.generation()); + tte->save(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, + m, VALUE_NONE, TT.generation()); pos.do_move(m, *st++, pos.gives_check(m, CheckInfo(pos))); } @@ -1527,10 +1540,10 @@ void RootMove::insert_pv_in_tt(Position& pos) { } -/// 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. +/// 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. bool RootMove::extract_ponder_from_tt(Position& pos) { @@ -1554,11 +1567,11 @@ bool RootMove::extract_ponder_from_tt(Position& pos) } -/// check_time() is called by the timer thread when the timer triggers. It is -/// used to print debug info and, more importantly, to detect when we are out of +/// TimerThread::check_time() is called by when the timer triggers. It is used +/// to print debug info and, more importantly, to detect when we are out of /// available time and thus stop the search. -void check_time() { +void TimerThread::check_time() { static TimePoint lastInfoTime = now(); int elapsed = Time.elapsed(); @@ -1577,7 +1590,7 @@ void check_time() { { bool stillAtFirstMove = Signals.firstRootMove && !Signals.failedLowAtRoot - && elapsed > Time.available() * 75 / 100; + && elapsed > Time.available() * 3 / 4; if ( stillAtFirstMove || elapsed > Time.maximum() - 2 * TimerThread::Resolution)