X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=0f27564a1053513b392b117a34303abe88db3a12;hp=97ad9912b6b38e21c890e462f30cca55efbacba5;hb=b5581b7779b6e286fa2277625572996477d74b10;hpb=3279655f12ff64603b62a99fe9d7e2c9ea6fb4ef diff --git a/src/search.cpp b/src/search.cpp index 97ad9912..0f27564a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -66,7 +66,7 @@ namespace { constexpr int SkipPhase[] = { 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7 }; // Razor and futility margins - constexpr int RazorMargin[] = {0, 590, 604}; + constexpr int RazorMargin = 600; Value futility_margin(Depth d, bool improving) { return Value((175 - 50 * improving) * d / ONE_PLY); } @@ -82,7 +82,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth depth) { int d = depth / ONE_PLY; - return d > 17 ? 0 : 32 * d * d + 64 * d - 64; + return d > 17 ? 0 : 29 * d * d + 138 * d - 134; } // Skill structure is used to implement strength limit @@ -246,15 +246,30 @@ void MainThread::search() { && !Skill(Options["Skill Level"]).enabled() && rootMoves[0].pv[0] != MOVE_NONE) { - for (Thread* th : Threads) + std::map votes; + Value minScore = this->rootMoves[0].score; + + // Find out minimum score and reset votes for moves which can be voted + for (Thread* th: Threads) { - Depth depthDiff = th->completedDepth - bestThread->completedDepth; - Value scoreDiff = th->rootMoves[0].score - bestThread->rootMoves[0].score; + minScore = std::min(minScore, th->rootMoves[0].score); + votes[th->rootMoves[0].pv[0]] = 0; + } - // Select the thread with the best score, always if it is a mate - if ( scoreDiff > 0 - && (depthDiff >= 0 || th->rootMoves[0].score >= VALUE_MATE_IN_MAX_PLY)) + // Vote according to score and depth + for (Thread* th : Threads) + votes[th->rootMoves[0].pv[0]] += int(th->rootMoves[0].score - minScore) + + int(th->completedDepth); + + // Select best thread + int bestVote = votes[this->rootMoves[0].pv[0]]; + for (Thread* th : Threads) + { + if (votes[th->rootMoves[0].pv[0]] > bestVote) + { + bestVote = votes[th->rootMoves[0].pv[0]]; bestThread = th; + } } } @@ -290,7 +305,7 @@ void Thread::search() { std::memset(ss-4, 0, 7 * sizeof(Stack)); for (int i = 4; i > 0; i--) - (ss-i)->contHistory = this->contHistory[NO_PIECE][0].get(); // Use as sentinel + (ss-i)->continuationHistory = &this->continuationHistory[NO_PIECE][0]; // Use as sentinel bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; @@ -331,7 +346,7 @@ void Thread::search() { if (idx > 0) { int i = (idx - 1) % 20; - if (((rootDepth / ONE_PLY + rootPos.game_ply() + SkipPhase[i]) / SkipSize[i]) % 2) + if (((rootDepth / ONE_PLY + SkipPhase[i]) / SkipSize[i]) % 2) continue; // Retry with an incremented rootDepth } @@ -587,7 +602,7 @@ namespace { (ss+1)->ply = ss->ply + 1; ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; - ss->contHistory = thisThread->contHistory[NO_PIECE][0].get(); + ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0]; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; Square prevSq = to_sq((ss-1)->currentMove); @@ -715,15 +730,9 @@ namespace { } // Step 7. Razoring (~2 Elo) - if ( !PvNode - && depth < 3 * ONE_PLY - && eval <= alpha - RazorMargin[depth / ONE_PLY]) - { - Value ralpha = alpha - (depth >= 2 * ONE_PLY) * RazorMargin[depth / ONE_PLY]; - Value v = qsearch(pos, ss, ralpha, ralpha+1); - if (depth < 2 * ONE_PLY || v <= ralpha) - return v; - } + if ( depth < 2 * ONE_PLY + && eval <= alpha - RazorMargin) + return qsearch(pos, ss, alpha, beta); improving = ss->staticEval >= (ss-2)->staticEval || (ss-2)->staticEval == VALUE_NONE; @@ -738,7 +747,7 @@ namespace { // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 22500 + && (ss-1)->statScore < 23200 && eval >= beta && ss->staticEval >= beta - 36 * depth / ONE_PLY + 225 && !excludedMove @@ -751,7 +760,7 @@ namespace { Depth R = ((823 + 67 * depth / ONE_PLY) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY; ss->currentMove = MOVE_NULL; - ss->contHistory = thisThread->contHistory[NO_PIECE][0].get(); + ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0]; pos.do_null_move(st); @@ -802,7 +811,7 @@ namespace { probCutCount++; ss->currentMove = move; - ss->contHistory = thisThread->contHistory[pos.moved_piece(move)][to_sq(move)].get(); + ss->continuationHistory = &thisThread->continuationHistory[pos.moved_piece(move)][to_sq(move)]; assert(depth >= 5 * ONE_PLY); @@ -835,7 +844,7 @@ namespace { moves_loop: // When in check, search starts from here - const PieceToHistory* contHist[] = { (ss-1)->contHistory, (ss-2)->contHistory, nullptr, (ss-4)->contHistory }; + const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, nullptr, (ss-4)->continuationHistory }; Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, @@ -935,7 +944,7 @@ moves_loop: // When in check, search starts from here int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY; // Countermoves based pruning (~20 Elo) - if ( lmrDepth < 3 + if ( lmrDepth < 3 + ((ss-1)->statScore > 0) && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) continue; @@ -970,7 +979,7 @@ moves_loop: // When in check, search starts from here // Update the current move (this must be done after singular extension search) ss->currentMove = move; - ss->contHistory = thisThread->contHistory[movedPiece][to_sq(move)].get(); + ss->continuationHistory = &thisThread->continuationHistory[movedPiece][to_sq(move)]; // Step 15. Make the move pos.do_move(move, st, givesCheck); @@ -983,20 +992,12 @@ moves_loop: // When in check, search starts from here { Depth r = reduction(improving, depth, moveCount); - if (captureOrPromotion) // (~5 Elo) - { - // Increase reduction by comparing opponent's stat score - if ((ss-1)->statScore >= 0) - r += ONE_PLY; + // Decrease reduction if opponent's move count is high (~10 Elo) + if ((ss-1)->moveCount > 15) + r -= ONE_PLY; - r -= r ? ONE_PLY : DEPTH_ZERO; - } - else + if (!captureOrPromotion) { - // 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 (~0 Elo) if (pvExact) r -= ONE_PLY; @@ -1030,10 +1031,10 @@ moves_loop: // When in check, search starts from here r += ONE_PLY; // 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); + r -= ss->statScore / 20000 * ONE_PLY; } - Depth d = std::max(newDepth - r, ONE_PLY); + Depth d = std::max(newDepth - std::max(r, DEPTH_ZERO), ONE_PLY); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); @@ -1212,8 +1213,10 @@ moves_loop: // When in check, search starts from here ss->pv[0] = MOVE_NONE; } + Thread* thisThread = pos.this_thread(); (ss+1)->ply = ss->ply + 1; ss->currentMove = bestMove = MOVE_NONE; + ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0]; inCheck = pos.checkers(); moveCount = 0; @@ -1239,8 +1242,8 @@ moves_loop: // When in check, search starts from here && ttHit && tte->depth() >= ttDepth && ttValue != VALUE_NONE // Only in case of TT access race - && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) - : (tte->bound() & BOUND_UPPER))) + && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) + : (tte->bound() & BOUND_UPPER))) return ttValue; // Evaluate the position statically @@ -1258,7 +1261,7 @@ moves_loop: // When in check, search starts from here ss->staticEval = bestValue = evaluate(pos); // Can ttValue be used as a better position evaluation? - if ( ttValue != VALUE_NONE + if ( ttValue != VALUE_NONE && (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))) bestValue = ttValue; } @@ -1283,12 +1286,15 @@ moves_loop: // When in check, search starts from here futilityBase = bestValue + 128; } + const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, nullptr, (ss-4)->continuationHistory }; + // Initialize a MovePicker object for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // be generated. - MovePicker mp(pos, ttMove, depth, &pos.this_thread()->mainHistory, - &pos.this_thread()->captureHistory, + MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, + &thisThread->captureHistory, + contHist, to_sq((ss-1)->currentMove)); // Loop through the moves until no moves remain or a beta cutoff occurs @@ -1345,6 +1351,7 @@ moves_loop: // When in check, search starts from here } ss->currentMove = move; + ss->continuationHistory = &thisThread->continuationHistory[pos.moved_piece(move)][to_sq(move)]; // Make and search the move pos.do_move(move, st, givesCheck); @@ -1436,7 +1443,7 @@ moves_loop: // When in check, search starts from here for (int i : {1, 2, 4}) if (is_ok((ss-i)->currentMove)) - (*(ss-i)->contHistory)[pc][to] << bonus; + (*(ss-i)->continuationHistory)[pc][to] << bonus; }