Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
return d > 17 ? 0 : 29 * d * d + 138 * d - 134;
}
+ // Add a small random component to draw evaluations to keep search dynamic
+ // and to avoid 3fold-blindness.
+ Value value_draw(Depth depth, Thread* thisThread) {
+ return depth < 4 ? VALUE_DRAW
+ : VALUE_DRAW + Value(2 * (thisThread->nodes.load(std::memory_order_relaxed) % 2) - 1);
+ }
+
// Skill structure is used to implement strength limit
struct Skill {
explicit Skill(int l) : level(l) {}
Time.availableNodes = 0;
TT.clear();
Threads.clear();
+ Tablebases::init(Options["SyzygyPath"]); // Free up mapped files
}
if (rootDepth >= 5 * ONE_PLY)
{
Value previousScore = rootMoves[pvIdx].previousScore;
- delta = Value(18);
+ delta = Value(20);
alpha = std::max(previousScore - delta,-VALUE_INFINITE);
beta = std::min(previousScore + delta, VALUE_INFINITE);
// Start with a small aspiration window and, in the case of a fail
// high/low, re-search with a bigger window until we don't fail
// high/low anymore.
+ int failedHighCnt = 0;
while (true)
{
- bestValue = ::search<PV>(rootPos, ss, alpha, beta, rootDepth, false);
+ Depth adjustedDepth = std::max(ONE_PLY, rootDepth - failedHighCnt * ONE_PLY);
+ bestValue = ::search<PV>(rootPos, ss, alpha, beta, adjustedDepth, 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
if (mainThread)
{
+ failedHighCnt = 0;
failedLow = true;
Threads.stopOnPonderhit = false;
}
}
else if (bestValue >= beta)
+ {
beta = std::min(bestValue + delta, VALUE_INFINITE);
+ if (mainThread)
+ ++failedHighCnt;
+ }
else
break;
&& !rootNode
&& pos.has_game_cycle(ss->ply))
{
- alpha = VALUE_DRAW;
+ alpha = value_draw(depth, pos.this_thread());
if (alpha >= beta)
return alpha;
}
if ( Threads.stop.load(std::memory_order_relaxed)
|| pos.is_draw(ss->ply)
|| ss->ply >= MAX_PLY)
- return (ss->ply >= MAX_PLY && !inCheck) ? evaluate(pos) : VALUE_DRAW;
+ return (ss->ply >= MAX_PLY && !inCheck) ? evaluate(pos)
+ : value_draw(depth, pos.this_thread());
// Step 3. Mate distance pruning. Even if we mate at the next move our score
// would be at best mate_in(ss->ply+1), but if alpha is already bigger because
TB::ProbeState err;
TB::WDLScore wdl = Tablebases::probe_wdl(pos, &err);
+ // Force check of time on the next occasion
+ if (thisThread == Threads.main())
+ static_cast<MainThread*>(thisThread)->callsCnt = 0;
+
if (err != TB::ProbeState::FAIL)
{
thisThread->tbHits.fetch_add(1, std::memory_order_relaxed);
&& (ss-1)->currentMove != MOVE_NULL
&& (ss-1)->statScore < 23200
&& eval >= beta
- && ss->staticEval >= beta - 36 * depth / ONE_PLY + 225
+ && pureStaticEval >= beta - 36 * depth / ONE_PLY + 225
&& !excludedMove
&& pos.non_pawn_material(us)
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
skipQuiets = false;
- ttCapture = false;
+ ttCapture = ttMove && pos.capture_or_promotion(ttMove);
pvExact = PvNode && ttHit && tte->bound() == BOUND_EXACT;
// Step 12. Loop through all pseudo-legal moves until no moves remain
extension = ONE_PLY;
}
else if ( givesCheck // Check extension (~2 Elo)
- && !moveCountPruning
&& pos.see_ge(move))
extension = ONE_PLY;
+ // Extension if castling
+ else if (type_of(move) == CASTLING)
+ extension = ONE_PLY;
+
// Calculate new depth for this move
newDepth = depth - ONE_PLY + extension;
continue;
}
- if (move == ttMove && captureOrPromotion)
- ttCapture = true;
-
// Update the current move (this must be done after singular extension search)
ss->currentMove = move;
ss->continuationHistory = &thisThread->continuationHistory[movedPiece][to_sq(move)];
// Extra penalty for a quiet TT move in previous ply when it gets refuted
if ((ss-1)->moveCount == 1 && !pos.captured_piece())
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
+
+ // Extra penalty for killer move in previous ply when it gets refuted
+ else if ( (ss-1)->killers[0]
+ && (ss-1)->currentMove == (ss-1)->killers[0]
+ && !pos.captured_piece())
+ update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth));
}
// Bonus for prior countermove that caused the fail low
else if ( (depth >= 3 * ONE_PLY || PvNode)
if (value > alpha)
{
+ bestMove = move;
+
if (PvNode) // Update pv even in fail-high case
update_pv(ss->pv, move, (ss+1)->pv);
if (PvNode && value < beta) // Update alpha here!
- {
alpha = value;
- bestMove = move;
- }
- else // Fail high
- {
- tte->save(posKey, value_to_tt(value, ss->ply), BOUND_LOWER,
- ttDepth, move, ss->staticEval);
-
- return value;
- }
+ else
+ break; // Fail high
}
}
}
return mated_in(ss->ply); // Plies to mate from the root
tte->save(posKey, value_to_tt(bestValue, ss->ply),
- PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
+ bestValue >= beta ? BOUND_LOWER :
+ PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
ttDepth, bestMove, ss->staticEval);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);