mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-22 20:57:10 +00:00
Merge commit '74a8fc060465a822f0c047f908d5fb07ebc6ad96' into cluster
This is the last commit before there are more conflicts.
This commit is contained in:
+68
-32
@@ -63,7 +63,7 @@ static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913,
|
||||
Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) {
|
||||
Value futilityMult = 109 - 40 * noTtCutNode;
|
||||
Value improvingDeduction = 59 * improving * futilityMult / 32;
|
||||
Value worseningDeduction = 328 * oppWorsening * futilityMult / 1024;
|
||||
Value worseningDeduction = oppWorsening * futilityMult / 3;
|
||||
|
||||
return futilityMult * d - improvingDeduction - worseningDeduction;
|
||||
}
|
||||
@@ -550,7 +550,7 @@ void Search::Worker::iterative_deepening() {
|
||||
void Search::Worker::clear() {
|
||||
counterMoves.fill(Move::none());
|
||||
mainHistory.fill(0);
|
||||
captureHistory.fill(0);
|
||||
captureHistory.fill(-700);
|
||||
pawnHistory.fill(-1193);
|
||||
correctionHistory.fill(0);
|
||||
|
||||
@@ -582,9 +582,8 @@ Value Search::Worker::search(
|
||||
// Limit the depth if extensions made it too large
|
||||
depth = std::min(depth, MAX_PLY - 1);
|
||||
|
||||
// Check if we have an upcoming move that draws by repetition, or
|
||||
// if the opponent had an alternative move earlier to this position.
|
||||
if (!rootNode && alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply))
|
||||
// Check if we have an upcoming move that draws by repetition.
|
||||
if (!rootNode && alpha < VALUE_DRAW && pos.upcoming_repetition(ss->ply))
|
||||
{
|
||||
alpha = value_draw(this->nodes);
|
||||
if (alpha >= beta)
|
||||
@@ -800,8 +799,7 @@ 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(-10 * int((ss - 1)->staticEval + ss->staticEval), -1590, 1371);
|
||||
bonus = bonus > 0 ? 2 * bonus : bonus / 2;
|
||||
int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1590, 1371) + 800;
|
||||
thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus;
|
||||
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]
|
||||
@@ -835,8 +833,9 @@ Value Search::Worker::search(
|
||||
&& eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening)
|
||||
- (ss - 1)->statScore / 263
|
||||
>= beta
|
||||
&& eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttData.move || ttCapture))
|
||||
return beta > VALUE_TB_LOSS_IN_MAX_PLY ? beta + (eval - beta) / 3 : eval;
|
||||
&& eval >= beta && (!ttData.move || ttCapture) && beta > VALUE_TB_LOSS_IN_MAX_PLY
|
||||
&& eval < VALUE_TB_WIN_IN_MAX_PLY)
|
||||
return beta + (eval - beta) / 3;
|
||||
|
||||
// Step 9. Null move search with verification search (~35 Elo)
|
||||
if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 14369
|
||||
@@ -880,9 +879,12 @@ Value Search::Worker::search(
|
||||
}
|
||||
|
||||
// Step 10. Internal iterative reductions (~9 Elo)
|
||||
// For PV nodes without a ttMove, we decrease depth by 3.
|
||||
// For PV nodes without a ttMove, we decrease depth.
|
||||
// Additionally, if the current position is found in the TT
|
||||
// and the stored depth in the TT is greater than or equal to
|
||||
// current search depth, we decrease search depth even further.
|
||||
if (PvNode && !ttData.move)
|
||||
depth -= 3;
|
||||
depth -= 3 + (ss->ttHit && ttData.depth >= depth);
|
||||
|
||||
// Use qsearch if depth <= 0.
|
||||
if (depth <= 0)
|
||||
@@ -909,12 +911,19 @@ Value Search::Worker::search(
|
||||
assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta);
|
||||
|
||||
MovePicker mp(pos, ttData.move, probCutBeta - ss->staticEval, &thisThread->captureHistory);
|
||||
Move probcutCapturesSearched[32];
|
||||
int probcutCaptureCount = 0;
|
||||
Piece captured;
|
||||
|
||||
while ((move = mp.next_move()) != Move::none())
|
||||
if (move != excludedMove && pos.legal(move))
|
||||
{
|
||||
assert(pos.capture_stage(move));
|
||||
|
||||
movedPiece = pos.moved_piece(move);
|
||||
captured = pos.piece_on(move.to_sq());
|
||||
|
||||
|
||||
// Prefetch the TT entry for the resulting position
|
||||
prefetch(tt.first_entry(pos.key_after(move)));
|
||||
|
||||
@@ -938,6 +947,19 @@ Value Search::Worker::search(
|
||||
|
||||
if (value >= probCutBeta)
|
||||
{
|
||||
thisThread->captureHistory[movedPiece][move.to_sq()][type_of(captured)]
|
||||
<< stat_bonus(depth - 2);
|
||||
|
||||
for (int i = 0; i < probcutCaptureCount; i++)
|
||||
{
|
||||
movedPiece = pos.moved_piece(probcutCapturesSearched[i]);
|
||||
captured = pos.piece_on(probcutCapturesSearched[i].to_sq());
|
||||
|
||||
thisThread->captureHistory[movedPiece][probcutCapturesSearched[i].to_sq()]
|
||||
[type_of(captured)]
|
||||
<< -stat_malus(depth - 3);
|
||||
}
|
||||
|
||||
// Save ProbCut data into transposition table
|
||||
Distributed::save(tt, threads, thisThread, ttWriter, posKey,
|
||||
value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3,
|
||||
@@ -945,6 +967,9 @@ Value Search::Worker::search(
|
||||
return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta)
|
||||
: value;
|
||||
}
|
||||
|
||||
if (probcutCaptureCount < 32)
|
||||
probcutCapturesSearched[probcutCaptureCount++] = move;
|
||||
}
|
||||
|
||||
Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable);
|
||||
@@ -954,9 +979,8 @@ moves_loop: // When in check, search starts here
|
||||
|
||||
// Step 12. A small Probcut idea, when we are in check (~4 Elo)
|
||||
probCutBeta = beta + 388;
|
||||
if (ss->inCheck && !PvNode && ttCapture && (ttData.bound & BOUND_LOWER)
|
||||
&& ttData.depth >= depth - 4 && ttData.value >= probCutBeta
|
||||
&& std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY
|
||||
if (ss->inCheck && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4
|
||||
&& ttData.value >= probCutBeta && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY
|
||||
&& std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY)
|
||||
return probCutBeta;
|
||||
|
||||
@@ -1042,7 +1066,7 @@ moves_loop: // When in check, search starts here
|
||||
// Futility pruning for captures (~2 Elo)
|
||||
if (!givesCheck && lmrDepth < 7 && !ss->inCheck)
|
||||
{
|
||||
Value futilityValue = ss->staticEval + 287 + 248 * lmrDepth
|
||||
Value futilityValue = ss->staticEval + 294 + 246 * lmrDepth
|
||||
+ PieceValue[capturedPiece] + captHist / 7;
|
||||
if (futilityValue <= alpha)
|
||||
continue;
|
||||
@@ -1050,7 +1074,7 @@ moves_loop: // When in check, search starts here
|
||||
|
||||
// SEE based pruning for captures and checks (~11 Elo)
|
||||
int seeHist = std::clamp(captHist / 32, -180 * depth, 163 * depth);
|
||||
if (!pos.see_ge(move, -160 * depth - seeHist))
|
||||
if (!pos.see_ge(move, -163 * depth - seeHist))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -1061,15 +1085,15 @@ moves_loop: // When in check, search starts here
|
||||
+ thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()];
|
||||
|
||||
// Continuation history based pruning (~2 Elo)
|
||||
if (lmrDepth < 6 && history < -4151 * depth)
|
||||
if (lmrDepth < 6 && history < -3899 * depth)
|
||||
continue;
|
||||
|
||||
history += 2 * thisThread->mainHistory[us][move.from_to()];
|
||||
|
||||
lmrDepth += history / 3678;
|
||||
lmrDepth += history / 4040;
|
||||
|
||||
Value futilityValue =
|
||||
ss->staticEval + (bestValue < ss->staticEval - 51 ? 138 : 54) + 140 * lmrDepth;
|
||||
ss->staticEval + (bestValue < ss->staticEval - 51 ? 135 : 56) + 140 * lmrDepth;
|
||||
|
||||
// Futility pruning: parent node (~13 Elo)
|
||||
if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha)
|
||||
@@ -1135,8 +1159,8 @@ moves_loop: // When in check, search starts here
|
||||
// and if after excluding the ttMove with a reduced search we fail high over the original beta,
|
||||
// we assume this expected cut-node is not singular (multiple moves fail high),
|
||||
// and we can prune the whole subtree by returning a softbound.
|
||||
else if (singularBeta >= beta)
|
||||
return singularBeta;
|
||||
else if (value >= beta && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY)
|
||||
return value;
|
||||
|
||||
// Negative extensions
|
||||
// If other moves failed high over (ttValue - margin) without the ttMove on a reduced search,
|
||||
@@ -1185,7 +1209,8 @@ moves_loop: // When in check, search starts here
|
||||
|
||||
// Decrease reduction if position is or has been on the PV (~7 Elo)
|
||||
if (ss->ttPv)
|
||||
r -= 1 + (ttData.value > alpha) + (ttData.depth >= depth);
|
||||
r -= 1 + (ttData.value > alpha) + (ttData.depth >= depth)
|
||||
- (PvNode && ttData.value < alpha && ttData.depth >= depth);
|
||||
|
||||
// Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC)
|
||||
if (PvNode)
|
||||
@@ -1204,7 +1229,7 @@ moves_loop: // When in check, search starts here
|
||||
|
||||
// Increase reduction if next ply has a lot of fail high (~5 Elo)
|
||||
if ((ss + 1)->cutoffCnt > 3)
|
||||
r++;
|
||||
r += 1 + !(PvNode || cutNode);
|
||||
|
||||
// For first picked move (ttMove) reduce reduction
|
||||
// but never allow it to go below 0 (~3 Elo)
|
||||
@@ -1293,7 +1318,7 @@ moves_loop: // When in check, search starts here
|
||||
rm.effort += nodes - nodeCount;
|
||||
|
||||
rm.averageScore =
|
||||
rm.averageScore != -VALUE_INFINITE ? (2 * value + rm.averageScore) / 3 : value;
|
||||
rm.averageScore != -VALUE_INFINITE ? (value + rm.averageScore) / 2 : value;
|
||||
|
||||
// PV move or new best move?
|
||||
if (moveCount == 1 || value > alpha)
|
||||
@@ -1333,11 +1358,17 @@ moves_loop: // When in check, search starts here
|
||||
rm.score = -VALUE_INFINITE;
|
||||
}
|
||||
|
||||
if (value > bestValue)
|
||||
// In case we have an alternative move equal in eval to the current bestmove,
|
||||
// promote it to bestmove by pretending it just exceeds alpha (but not beta).
|
||||
int inc = (value == bestValue && (int(nodes) & 15) == 0
|
||||
&& ss->ply + 2 + ss->ply / 32 >= thisThread->rootDepth
|
||||
&& std::abs(value) + 1 < VALUE_TB_WIN_IN_MAX_PLY);
|
||||
|
||||
if (value + inc > bestValue)
|
||||
{
|
||||
bestValue = value;
|
||||
|
||||
if (value > alpha)
|
||||
if (value + inc > alpha)
|
||||
{
|
||||
bestMove = move;
|
||||
|
||||
@@ -1396,10 +1427,16 @@ moves_loop: // When in check, search starts here
|
||||
// Bonus for prior countermove that caused the fail low
|
||||
else if (!priorCapture && prevSq != SQ_NONE)
|
||||
{
|
||||
int bonus = (113 * (depth > 5) + 118 * (PvNode || cutNode)
|
||||
+ 191 * ((ss - 1)->statScore < -14396) + 119 * ((ss - 1)->moveCount > 8)
|
||||
int bonus = (113 * (depth > 5) + 118 * (PvNode || cutNode) + 119 * ((ss - 1)->moveCount > 8)
|
||||
+ 64 * (!ss->inCheck && bestValue <= ss->staticEval - 107)
|
||||
+ 147 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 75));
|
||||
|
||||
|
||||
// proportional to "how much damage we have to undo"
|
||||
if ((ss - 1)->statScore < -8000)
|
||||
bonus += std::clamp(-(ss - 1)->statScore / 100, 0, 250);
|
||||
|
||||
|
||||
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq,
|
||||
stat_bonus(depth) * bonus / 100);
|
||||
thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()]
|
||||
@@ -1461,9 +1498,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta,
|
||||
assert(PvNode || (alpha == beta - 1));
|
||||
assert(depth <= 0);
|
||||
|
||||
// Check if we have an upcoming move that draws by repetition, or if
|
||||
// the opponent had an alternative move earlier to this position. (~1 Elo)
|
||||
if (alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply))
|
||||
// Check if we have an upcoming move that draws by repetition. (~1 Elo)
|
||||
if (alpha < VALUE_DRAW && pos.upcoming_repetition(ss->ply))
|
||||
{
|
||||
alpha = value_draw(this->nodes);
|
||||
if (alpha >= beta)
|
||||
@@ -1633,7 +1669,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta,
|
||||
|
||||
// If static exchange evaluation is much worse than what is needed to not
|
||||
// fall below alpha we can prune this move.
|
||||
if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 2 - 30))
|
||||
if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 4))
|
||||
{
|
||||
bestValue = alpha;
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user