diff --git a/src/search.cpp b/src/search.cpp index c22064a26ef..6703b1722be 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -67,7 +67,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = a1 - a2 * noTtCutNode; + Value futilityMult = 111 - 25 * noTtCutNode; Value improvingDeduction = improving * futilityMult * 2; Value worseningDeduction = oppWorsening * futilityMult / 3; @@ -90,7 +90,7 @@ int correction_value(const Worker& w, const Position& pos, const Stack* ss) { m.is_ok() ? (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] : 0; - return (a3 * pcv + a4 * macv + a5 * micv + a6 * (wnpcv + bnpcv) + a7 * cntcv); + return (6913 * pcv + 3263 * macv + 6654 * micv + 8952 * (wnpcv + bnpcv) + 7031 * cntcv); } // Add correctionHistory value to raw staticEval and guarantee evaluation @@ -100,10 +100,10 @@ Value to_corrected_static_eval(Value v, const int cv) { } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(a8 * d - a9, a10); } +int stat_bonus(Depth d) { return std::min(156 * d - 95, 1776); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(a11 * d - a12, a13); } +int stat_malus(Depth d) { return std::min(849 * d - 269, 2704); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -280,7 +280,7 @@ void Search::Worker::iterative_deepening() { int searchAgainCounter = 0; - lowPlyHistory.fill(a14); + lowPlyHistory.fill(88); // Iterative deepening loop until requested to stop or the target depth is reached while (++rootDepth < MAX_PLY && !threads.stop @@ -316,13 +316,13 @@ void Search::Worker::iterative_deepening() { selDepth = 0; // Reset aspiration window starting size - delta = a15 + std::abs(rootMoves[pvIdx].meanSquaredScore) / a16; + delta = 5 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 11276; Value avg = rootMoves[pvIdx].averageScore; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = a17 * avg / (std::abs(avg) + a18); + optimism[us] = 122 * avg / (std::abs(avg) + 77); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -506,10 +506,10 @@ void Search::Worker::iterative_deepening() { // Reset histories, usually before a new game void Search::Worker::clear() { - mainHistory.fill(a19); - lowPlyHistory.fill(a20); - captureHistory.fill(-a21); - pawnHistory.fill(-a22); + mainHistory.fill(56); + lowPlyHistory.fill(118); + captureHistory.fill(-638); + pawnHistory.fill(-1198); pawnCorrectionHistory.fill(0); majorPieceCorrectionHistory.fill(0); minorPieceCorrectionHistory.fill(0); @@ -524,10 +524,10 @@ void Search::Worker::clear() { for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h.fill(-a23); + h.fill(-456); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int(a24 / 100.0 * std::log(i)); + reductions[i] = int(2052 / 100.0 * std::log(i)); refreshTable.clear(networks[numaAccessToken]); } @@ -653,13 +653,13 @@ Value Search::Worker::search( { // Bonus for a quiet ttMove that fails high (~2 Elo) if (!ttCapture) - update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth) * a26 / 1024); + update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth) * 844 / 1024); // Extra penalty for early quiet moves of // the previous ply (~1 Elo on STC, ~2 Elo on LTC) if (prevSq != SQ_NONE && (ss - 1)->moveCount <= 2 && !priorCapture) update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - -stat_malus(depth + 1) * a27 / 1024); + -stat_malus(depth + 1) * 974 / 1024); } // Partial workaround for the graph history interaction problem @@ -767,11 +767,11 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-a28 * int((ss - 1)->staticEval + ss->staticEval), -a29, a30) + a31; - thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * a32 / 1024; + int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1726, 1399) + 642; + thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 1010 / 1024; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] - << bonus * a33 / 1024; + << bonus * 1146 / 1024; } // Set up the improving flag, which is true if current static evaluation is @@ -788,29 +788,29 @@ Value Search::Worker::search( // Step 7. Razoring (~1 Elo) // If eval is really low, skip search entirely and return the qsearch value. // For PvNodes, we must have a guard against mates being returned. - if (!PvNode && eval < alpha - a34 - a35 * depth * depth) + if (!PvNode && eval < alpha - 464 - 286 * depth * depth) return qsearch(pos, ss, alpha, beta); // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. - if (!ss->ttPv && depth < a36 + if (!ss->ttPv && depth < 15 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / a37 + 40 - std::abs(correctionValue) / 131072 + - (ss - 1)->statScore / 289 + 40 - std::abs(correctionValue) / 131072 >= beta && eval >= beta && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval)) return beta + (eval - beta) / 3; - improving |= ss->staticEval >= beta + a38; + improving |= ss->staticEval >= beta + 101; // Step 9. Null move search with verification search (~35 Elo) if (cutNode && (ss - 1)->currentMove != Move::null() && eval >= beta - && ss->staticEval >= beta - a39 * depth + a40 && !excludedMove && pos.non_pawn_material(us) + && ss->staticEval >= beta - 19 * depth + 431 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && !is_loss(beta)) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / a41, a42) + depth / 3 + a43; + Depth R = std::min(int(eval - beta) / 230, 7) + depth / 3 + 5; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -858,7 +858,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search // returns a value much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + a45 - a46 * improving; + probCutBeta = beta + 193 - 61 * improving; if (depth >= 3 && !is_decisive(beta) // If value from transposition table is lower than probCutBeta, don't attempt @@ -920,7 +920,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea (~4 Elo) - probCutBeta = beta + a48; + probCutBeta = beta + 424; if ((ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 && ttData.value >= probCutBeta && !is_decisive(beta) && is_valid(ttData.value) && !is_decisive(ttData.value)) return probCutBeta; @@ -1002,17 +1002,17 @@ Value Search::Worker::search( thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)]; // Futility pruning for captures (~2 Elo) - if (!givesCheck && lmrDepth < a49 && !ss->inCheck) + if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + a50 + a51 * lmrDepth + Value futilityValue = ss->staticEval + 263 + 222 * lmrDepth + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - int seeHist = std::clamp(captHist / a52, -a53 * depth, a54 * depth); - if (!pos.see_ge(move, -a55 * depth - seeHist)) + int seeHist = std::clamp(captHist / 37, -164 * depth, 163 * depth); + if (!pos.see_ge(move, -160 * depth - seeHist)) continue; } else @@ -1023,18 +1023,18 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (history < -a56 * depth) + if (history < -3865 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / a57; + lmrDepth += history / 3399; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - a58 ? a59 : a60) + a61 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 49 ? 138 : 43) + 144 * lmrDepth; // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < a62 && futilityValue <= alpha) + if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha) { if (bestValue <= futilityValue && !is_decisive(bestValue) && !is_win(futilityValue)) @@ -1045,7 +1045,7 @@ Value Search::Worker::search( lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, -a63 * lmrDepth * lmrDepth)) + if (!pos.see_ge(move, -23 * lmrDepth * lmrDepth)) continue; } } @@ -1068,12 +1068,12 @@ Value Search::Worker::search( // and lower extension margins scale well. if (!rootNode && move == ttData.move && !excludedMove - && depth >= a64 - (thisThread->completedDepth > a65) + ss->ttPv + && depth >= 5 - (thisThread->completedDepth > 33) + ss->ttPv && is_valid(ttData.value) && !is_decisive(ttData.value) && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 3) { Value singularBeta = - ttData.value - (a66 + a67 * (ss->ttPv && !PvNode)) * depth / 64; + ttData.value - (53 + 84 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1084,17 +1084,17 @@ Value Search::Worker::search( if (value < singularBeta) { int doubleMargin = - a68 * PvNode - a69 * !ttCapture - std::abs(correctionValue) / a140; - int tripleMargin = a70 + a71 * PvNode - a72 * !ttCapture + a73 * ss->ttPv - - std::abs(correctionValue) / a145; - int quadMargin = a141 + a142 * PvNode - a143 * !ttCapture + a144 * ss->ttPv - - std::abs(correctionValue) / a146; + 250 * PvNode - 176 * !ttCapture - std::abs(correctionValue) / 239082; + int tripleMargin = 100 + 285 * PvNode - 253 * !ttCapture + 97 * ss->ttPv + - std::abs(correctionValue) / 274334; + int quadMargin = 387 + 269 * PvNode - 236 * !ttCapture + 102 * ss->ttPv + - std::abs(correctionValue) / 270916; extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin) + (value < singularBeta - quadMargin); - depth += ((!PvNode) && (depth < a74)); + depth += ((!PvNode) && (depth < 15)); } // Multi-cut pruning @@ -1127,7 +1127,7 @@ Value Search::Worker::search( else if (PvNode && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > a75) + > 4263) extension = 1; } @@ -1153,42 +1153,42 @@ Value Search::Worker::search( // Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC) if (PvNode) - r -= a79; + r -= 1061; // These reduction adjustments have no proven non-linear scaling - r += a80; + r += 292; - r -= std::abs(correctionValue) / a81; + r -= std::abs(correctionValue) / 33838; // Increase reduction for cut nodes (~4 Elo) if (cutNode) - r += a83 - (ttData.depth >= depth && ss->ttPv) * a84; + r += 2825 - (ttData.depth >= depth && ss->ttPv) * 1101; // Increase reduction if ttMove is a capture but the current move is not a capture (~3 Elo) if (ttCapture && !capture) - r += a85 + (depth < a86) * a87; + r += 1230 + (depth < 7) * 1194; // Increase reduction if next ply has a lot of fail high (~5 Elo) - if ((ss + 1)->cutoffCnt > a88) - r += a89 + allNode * a90; + if ((ss + 1)->cutoffCnt > 3) + r += 993 + allNode * 945; // For first picked move (ttMove) reduce reduction (~3 Elo) else if (move == ttData.move) - r -= a91; + r -= 2106; if (capture) ss->statScore = - a92 * int(PieceValue[pos.captured_piece()]) + 7 * int(PieceValue[pos.captured_piece()]) + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(pos.captured_piece())] - - a93; + - 4790; else ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - a94; + + (*contHist[1])[movedPiece][move.to_sq()] - 3752; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore * a95 / 16384; + r -= ss->statScore * 1610 / 16384; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1) @@ -1214,8 +1214,8 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result was // good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + a96 + 2 * newDepth); // (~1 Elo) - const bool doShallowerSearch = value < bestValue + a97; // (~2 Elo) + const bool doDeeperSearch = value > (bestValue + 43 + 2 * newDepth); // (~1 Elo) + const bool doShallowerSearch = value < bestValue + 9; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1233,11 +1233,11 @@ Value Search::Worker::search( { // Increase reduction if ttMove is not present (~6 Elo) if (!ttData.move) - r += a99; + r += 2178; // Note that if expected reduction is high, we reduce search depth by 1 here (~9 Elo) value = - -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > a100), !cutNode); + -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 3385), !cutNode); } // For PV nodes only, do a full PV search on the first move or after a fail high, @@ -1343,7 +1343,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < a101 && !is_decisive(value)) + if (depth > 2 && depth < 16 && !is_decisive(value)) depth -= 2; assert(depth > 0); @@ -1388,26 +1388,26 @@ Value Search::Worker::search( else if (!priorCapture && prevSq != SQ_NONE) { int bonusScale = - (a102 * (depth > a103) + a104 * !allNode + a105 * ((ss - 1)->moveCount > a106) - + a107 * (!ss->inCheck && bestValue <= ss->staticEval - a108) - + a109 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - a110)); + (119 * (depth > 5) + 39 * !allNode + 193 * ((ss - 1)->moveCount > 8) + + 143 * (!ss->inCheck && bestValue <= ss->staticEval - 107) + + 110 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 81)); // Proportional to "how much damage we have to undo" - bonusScale += std::min(-(ss - 1)->statScore / a111, a112); + bonusScale += std::min(-(ss - 1)->statScore / 100, 316); bonusScale = std::max(bonusScale, 0); const int scaledBonus = stat_bonus(depth) * bonusScale / 32; update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - scaledBonus * a113 / 1024); + scaledBonus * 512 / 1024); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] - << scaledBonus * a114 / 1024; + << scaledBonus * 219 / 1024; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] - << scaledBonus * a115 / 1024; + << scaledBonus * 1257 / 1024; } else if (priorCapture && prevSq != SQ_NONE) @@ -1442,14 +1442,14 @@ Value Search::Worker::search( || (bestValue > ss->staticEval && bestMove))) // positive correction & no fail low { const auto m = (ss - 1)->currentMove; - const int nonPawnWeight = a117; + const int nonPawnWeight = 185; auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); thisThread->pawnCorrectionHistory[us][pawn_structure_index(pos)] - << bonus * a118 / 128; - thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus * a119 / 128; - thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus * a120 / 128; + << bonus * 115 / 128; + thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus * 164 / 128; + thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus * 162 / 128; thisThread->nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)] << bonus * nonPawnWeight / 128; thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)] @@ -1581,7 +1581,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + a121; + futilityBase = ss->staticEval + 322; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1644,11 +1644,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)] [move.to_sq()] - <= a122) + <= 4679) continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -a123)) + if (!pos.see_ge(move, -83)) continue; } @@ -1715,7 +1715,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const { int reductionScale = reductions[d] * reductions[mn]; - return reductionScale - delta * a124 / rootDelta + !i * reductionScale * a125 / 300 + a126; + return reductionScale - delta * 806 / rootDelta + !i * reductionScale * 101 / 300 + 1132; } // elapsed() returns the time elapsed since the search started. If the @@ -1815,30 +1815,30 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - update_quiet_histories(pos, ss, workerThread, bestMove, bonus * a127 / 1024); + update_quiet_histories(pos, ss, workerThread, bestMove, bonus * 1214 / 1024); // Decrease stats for all non-best quiet moves for (Move move : quietsSearched) - update_quiet_histories(pos, ss, workerThread, move, -malus * a128 / 1024); + update_quiet_histories(pos, ss, workerThread, move, -malus * 1117 / 1024); } else { // Increase stats for the best move in case it was a capture move captured = type_of(pos.piece_on(bestMove.to_sq())); - captureHistory[moved_piece][bestMove.to_sq()][captured] << bonus * a129 / 1024; + captureHistory[moved_piece][bestMove.to_sq()][captured] << bonus * 1267 / 1024; } // Extra penalty for a quiet early move that was not a TT move in // previous ply when it gets refuted. if (prevSq != SQ_NONE && ((ss - 1)->moveCount == 1 + (ss - 1)->ttHit) && !pos.captured_piece()) - update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus * a130 / 1024); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus * 942 / 1024); // Decrease stats for all non-best capture moves for (Move move : capturesSearched) { moved_piece = pos.moved_piece(move); captured = type_of(pos.piece_on(move.to_sq())); - captureHistory[moved_piece][move.to_sq()][captured] << -malus * a131 / 1024; + captureHistory[moved_piece][move.to_sq()][captured] << -malus * 1210 / 1024; } } @@ -1847,7 +1847,7 @@ void update_all_stats(const Position& pos, // at ply -1, -2, -3, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { static const std::array conthist_bonuses = { - {{1, a132}, {2, a133}, {3, a134}, {4, a135}, {5, 122}, {6, a136}}}; + {{1, 1136}, {2, 697}, {3, 326}, {4, 448}, {5, 122}, {6, 491}}}; for (const auto [i, weight] : conthist_bonuses) { @@ -1868,12 +1868,12 @@ void update_quiet_histories( workerThread.mainHistory[us][move.from_to()] << bonus; // Untuned to prevent duplicate effort if (ss->ply < LOW_PLY_HISTORY_SIZE) - workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus * a137 / 1024; + workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus * 850 / 1024; - update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * a138 / 1024); + update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * 898 / 1024); int pIndex = pawn_structure_index(pos); - workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus * a139 / 1024; + workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus * 778 / 1024; } }