X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=763a6ac31194a8ae8b8a6e92bbbf0700894373a7;hp=a5203b2e4802c1e77b2aa6ed11c1a528741cc202;hb=aedebe35cfa38b543041bae97e91e8194738b202;hpb=89723339d93c87f52e148080d5b1dc101ee76685 diff --git a/src/search.cpp b/src/search.cpp index a5203b2e..763a6ac3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -62,7 +62,7 @@ using namespace Search; namespace { // Different node types, used as a template parameter - enum NodeType { Root, PV, NonPV }; + enum NodeType { NonPV, PV }; // Razoring and futility margin based on depth const int razor_margin[4] = { 483, 570, 603, 554 }; @@ -188,7 +188,7 @@ void Search::clear() { th->counterMoves.clear(); } - Threads.main()->previousMoveScore = VALUE_INFINITE; + Threads.main()->previousScore = VALUE_INFINITE; } @@ -256,8 +256,9 @@ void MainThread::search() { } else { - if (TB::Cardinality >= rootPos.count(WHITE) - + rootPos.count(BLACK)) + if ( TB::Cardinality >= rootPos.count(WHITE) + + rootPos.count(BLACK) + && !rootPos.can_castle(ANY_CASTLING)) { // If the current root position is in the tablebases, then RootMoves // contains only moves that preserve the draw or the win. @@ -338,7 +339,7 @@ void MainThread::search() { bestThread = th; } - previousMoveScore = bestThread->rootMoves[0].score; + previousScore = bestThread->rootMoves[0].score; // Send new PV when needed if (bestThread != this) @@ -352,6 +353,33 @@ void MainThread::search() { std::cout << sync_endl; } +const int halfDensityMap[][9] = +{ + {2, 0, 1}, + {2, 1, 0}, + + {4, 0, 0, 1, 1}, + {4, 0, 1, 1, 0}, + {4, 1, 1, 0, 0}, + {4, 1, 0, 0, 1}, + + {6, 0, 0, 0, 1, 1, 1}, + {6, 0, 0, 1, 1, 1, 0}, + {6, 0, 1, 1, 1, 0, 0}, + {6, 1, 1, 1, 0, 0, 0}, + {6, 1, 1, 0, 0, 0, 1}, + {6, 1, 0, 0, 0, 1, 1}, + + {8, 0, 0, 0, 0, 1, 1, 1, 1}, + {8, 0, 0, 0, 1, 1, 1, 1, 0}, + {8, 0, 0, 1, 1, 1, 1, 0 ,0}, + {8, 0, 1, 1, 1, 1, 0, 0 ,0}, + {8, 1, 1, 1, 1, 0, 0, 0 ,0}, + {8, 1, 1, 1, 0, 0, 0, 0 ,1}, + {8, 1, 1, 0, 0, 0, 0, 1 ,1}, + {8, 1, 0, 0, 0, 0, 1, 1 ,1}, +}; + // Thread::search() is the main iterative deepening loop. It calls search() // repeatedly with increasing depth until the allocated thinking time has been @@ -393,27 +421,12 @@ void Thread::search() { while (++rootDepth < DEPTH_MAX && !Signals.stop && (!Limits.depth || rootDepth <= Limits.depth)) { // Set up the new depths for the helper threads skipping on average every - // 2nd ply (using a half-density map similar to a Hadamard matrix). + // 2nd ply (using a half-density matrix). if (!mainThread) { - int d = rootDepth + rootPos.game_ply(); - - if (idx <= 6 || idx > 24) - { - if (((d + idx) >> (msb(idx + 1) - 1)) % 2) - continue; - } - else - { - // Table of values of 6 bits with 3 of them set - static const int HalfDensityMap[] = { - 0x07, 0x0b, 0x0d, 0x0e, 0x13, 0x16, 0x19, 0x1a, 0x1c, - 0x23, 0x25, 0x26, 0x29, 0x2c, 0x31, 0x32, 0x34, 0x38 - }; - - if ((HalfDensityMap[idx - 7] >> (d % 6)) & 1) - continue; - } + int row = (idx - 1) % 20; + if (halfDensityMap[row][(rootDepth + rootPos.game_ply()) % halfDensityMap[row][0] + 1]) + continue; } // Age out PV variability metric @@ -441,7 +454,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); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the @@ -531,20 +544,22 @@ void Thread::search() { { if (!Signals.stop && !Signals.stopOnPonderhit) { - // Take some extra time if the best move has changed - if (rootDepth > 4 * ONE_PLY && multiPV == 1) - Time.pv_instability(mainThread->bestMoveChanges); - // Stop the search if only one legal move is available, or if all // of the available time has been used, or if we matched an easyMove // from the previous search and just did a fast verification. + const bool F[] = { !mainThread->failedLow, + bestValue >= mainThread->previousScore }; + + int improvingFactor = 640 - 160*F[0] - 126*F[1] - 124*F[0]*F[1]; + double unstablePvFactor = 1 + mainThread->bestMoveChanges; + + bool doEasyMove = rootMoves[0].pv[0] == easyMove + && mainThread->bestMoveChanges < 0.03 + && Time.elapsed() > Time.optimum() * 25 / 204; + if ( rootMoves.size() == 1 - || Time.elapsed() > Time.available() * ( 640 - 160 * !mainThread->failedLow - - 126 * (bestValue >= mainThread->previousMoveScore) - - 124 * (bestValue >= mainThread->previousMoveScore && !mainThread->failedLow))/640 - || ( mainThread->easyMovePlayed = ( rootMoves[0].pv[0] == easyMove - && mainThread->bestMoveChanges < 0.03 - && Time.elapsed() > Time.available() * 25/206))) + || Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 634 + || (mainThread->easyMovePlayed = doEasyMove)) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". @@ -584,8 +599,8 @@ namespace { template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { - const bool RootNode = NT == Root; - const bool PvNode = NT == PV || NT == Root; + const bool PvNode = NT == PV; + const bool rootNode = PvNode && (ss-1)->ply == 0; assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); @@ -627,7 +642,7 @@ namespace { if (PvNode && thisThread->maxPly < ss->ply) thisThread->maxPly = ss->ply; - if (!RootNode) + if (!rootNode) { // Step 2. Check for aborted search and immediate draw if (Signals.stop.load(std::memory_order_relaxed) || pos.is_draw() || ss->ply >= MAX_PLY) @@ -659,7 +674,7 @@ namespace { posKey = excludedMove ? pos.exclusion_key() : pos.key(); tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; - ttMove = RootNode ? thisThread->rootMoves[thisThread->PVIdx].pv[0] + ttMove = rootNode ? thisThread->rootMoves[thisThread->PVIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; // At non-PV nodes we check for an early TT cutoff @@ -680,13 +695,14 @@ namespace { } // Step 4a. Tablebase probe - if (!RootNode && TB::Cardinality) + if (!rootNode && TB::Cardinality) { int piecesCnt = pos.count(WHITE) + pos.count(BLACK); if ( piecesCnt <= TB::Cardinality && (piecesCnt < TB::Cardinality || depth >= TB::ProbeDepth) - && pos.rule50_count() == 0) + && pos.rule50_count() == 0 + && !pos.can_castle(ANY_CASTLING)) { int found, v = Tablebases::probe_wdl(pos, &found); @@ -757,7 +773,7 @@ namespace { } // Step 7. Futility pruning: child node (skipped when in check) - if ( !RootNode + if ( !rootNode && depth < 7 * ONE_PLY && eval - futility_margin(depth) >= beta && eval < VALUE_KNOWN_WIN // Do not return unproven wins @@ -841,7 +857,7 @@ namespace { { Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4); ss->skipEarlyPruning = true; - search(pos, ss, alpha, beta, d, true); + search(pos, ss, alpha, beta, d, true); ss->skipEarlyPruning = false; tte = TT.probe(posKey, ttHit); @@ -861,7 +877,7 @@ moves_loop: // When in check search starts from here || ss->staticEval == VALUE_NONE ||(ss-2)->staticEval == VALUE_NONE; - singularExtensionNode = !RootNode + singularExtensionNode = !rootNode && depth >= 8 * ONE_PLY && ttMove != MOVE_NONE /* && ttValue != VALUE_NONE Already implicit in the next condition */ @@ -882,13 +898,13 @@ 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. - if (RootNode && !std::count(thisThread->rootMoves.begin() + thisThread->PVIdx, + if (rootNode && !std::count(thisThread->rootMoves.begin() + thisThread->PVIdx, thisThread->rootMoves.end(), move)) continue; ss->moveCount = ++moveCount; - if (RootNode && thisThread == Threads.main() && Time.elapsed() > 3000) + if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000) sync_cout << "info depth " << depth / ONE_PLY << " currmove " << UCI::move(move, pos.is_chess960()) << " currmovenumber " << moveCount + thisThread->PVIdx << sync_endl; @@ -932,7 +948,7 @@ moves_loop: // When in check search starts from here newDepth = depth - ONE_PLY + extension; // Step 13. Pruning at shallow depth - if ( !RootNode + if ( !rootNode && !captureOrPromotion && !inCheck && !givesCheck @@ -974,7 +990,7 @@ moves_loop: // When in check search starts from here prefetch(TT.first_entry(pos.key_after(move))); // Check for legality just before making the move - if (!RootNode && !pos.legal(move, ci.pinned)) + if (!rootNode && !pos.legal(move, ci.pinned)) { ss->moveCount = --moveCount; continue; @@ -999,11 +1015,10 @@ moves_loop: // When in check search starts from here && cmh[pos.piece_on(to_sq(move))][to_sq(move)] <= VALUE_ZERO)) r += ONE_PLY; - // Decrease reduction for moves with a good history and - // increase reduction for moves with a bad history - int rDecrease = ( thisThread->history[pos.piece_on(to_sq(move))][to_sq(move)] - + cmh[pos.piece_on(to_sq(move))][to_sq(move)]) / 14980; - r = std::max(DEPTH_ZERO, r - rDecrease * ONE_PLY); + // Decrease/increase reduction for moves with a good/bad history + int rHist = ( thisThread->history[pos.piece_on(to_sq(move))][to_sq(move)] + + cmh[pos.piece_on(to_sq(move))][to_sq(move)]) / 14980; + r = std::max(DEPTH_ZERO, r - rHist * ONE_PLY); // Decrease reduction for moves that escape a capture. Filter out // castling moves, because they are coded as "king captures rook" and @@ -1034,7 +1049,7 @@ moves_loop: // When in check search starts from here // 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 // parent node fail low with value <= alpha and try another move. - if (PvNode && (moveCount == 1 || (value > alpha && (RootNode || value < beta)))) + if (PvNode && (moveCount == 1 || (value > alpha && (rootNode || value < beta)))) { (ss+1)->pv = pv; (ss+1)->pv[0] = MOVE_NONE; @@ -1057,7 +1072,7 @@ moves_loop: // When in check search starts from here if (Signals.stop.load(std::memory_order_relaxed)) return VALUE_ZERO; - if (RootNode) + if (rootNode) { RootMove& rm = *std::find(thisThread->rootMoves.begin(), thisThread->rootMoves.end(), move); @@ -1101,7 +1116,7 @@ moves_loop: // When in check search starts from here bestMove = move; - if (PvNode && !RootNode) // Update pv even in fail-high case + if (PvNode && !rootNode) // Update pv even in fail-high case update_pv(ss->pv, move, (ss+1)->pv); if (PvNode && value < beta) // Update alpha! Always alpha < beta @@ -1172,7 +1187,6 @@ moves_loop: // When in check search starts from here const bool PvNode = NT == PV; - assert(NT == PV || NT == NonPV); assert(InCheck == !!pos.checkers()); assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); @@ -1472,7 +1486,7 @@ moves_loop: // When in check search starts from here int maxScore = -VALUE_INFINITE; // 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 + // weakness. One is deterministic and bigger for weaker levels, and one is // random. Then we choose the move with the resulting highest score. for (size_t i = 0; i < multiPV; ++i) {