// Threads.stop. However, if we are pondering or in an infinite search,
// the UCI protocol states that we shouldn't print the best move before the
// GUI sends a "stop" or "ponderhit" command. We therefore simply wait here
- // until the GUI sends one of those commands (which also raises Threads.stop).
- Threads.stopOnPonderhit = true;
+ // until the GUI sends one of those commands.
- while (!Threads.stop && (Threads.ponder || Limits.infinite))
+ while (!Threads.stop && (ponder || Limits.infinite))
{} // Busy wait for a stop or a ponder reset
// Stop the threads if not already stopped (also raise the stop if
// Find out minimum score and reset votes for moves which can be voted
for (Thread* th: Threads)
- {
minScore = std::min(minScore, th->rootMoves[0].score);
- votes[th->rootMoves[0].pv[0]] = 0;
- }
// Vote according to score and depth
- auto square = [](int64_t x) { return x * x; };
for (Thread* th : Threads)
- votes[th->rootMoves[0].pv[0]] += 200 + (square(th->rootMoves[0].score - minScore + 1)
- * int64_t(th->completedDepth));
+ {
+ int64_t s = th->rootMoves[0].score - minScore + 1;
+ votes[th->rootMoves[0].pv[0]] += 200 + s * s * int(th->completedDepth);
+ }
// Select best thread
- int64_t bestVote = votes[this->rootMoves[0].pv[0]];
+ auto 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;
}
- }
}
previousScore = bestThread->rootMoves[0].score;
{
failedHighCnt = 0;
failedLow = true;
- Threads.stopOnPonderhit = false;
+ mainThread->stopOnPonderhit = false;
}
}
else if (bestValue >= beta)
// Do we have time for the next iteration? Can we stop searching now?
if ( Limits.use_time_management()
&& !Threads.stop
- && !Threads.stopOnPonderhit)
+ && !mainThread->stopOnPonderhit)
{
double fallingEval = (306 + 119 * failedLow + 6 * (mainThread->previousScore - bestValue)) / 581.0;
fallingEval = std::max(0.5, std::min(1.5, fallingEval));
// If the bestMove is stable over several iterations, reduce time accordingly
- timeReduction = 1.0;
- for (int i : {3, 4, 5})
- if (lastBestMoveDepth * i < completedDepth)
- timeReduction *= 1.25;
+ timeReduction = lastBestMoveDepth + 10 * ONE_PLY < completedDepth ? 1.95 : 1.0;
// Use part of the gained time from a previous stable move for the current move
double bestMoveInstability = 1.0 + mainThread->bestMoveChanges;
{
// If we are allowed to ponder do not stop the search now but
// keep pondering until the GUI sends "ponderhit" or "stop".
- if (Threads.ponder)
- Threads.stopOnPonderhit = true;
+ if (mainThread->ponder)
+ mainThread->stopOnPonderhit = true;
else
Threads.stop = true;
}
Move ttMove, move, excludedMove, bestMove;
Depth extension, newDepth;
Value bestValue, value, ttValue, eval, maxValue, pureStaticEval;
- bool ttHit, inCheck, givesCheck, improving;
- bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture, pvExact;
+ bool ttHit, ttPv, inCheck, givesCheck, improving;
+ bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture;
Piece movedPiece;
int moveCount, captureCount, quietCount;
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
: ttHit ? tte->move() : MOVE_NONE;
+ ttPv = (ttHit && tte->is_pv()) || (PvNode && depth > 4 * ONE_PLY);
// At non-PV nodes we check for an early TT cutoff
if ( !PvNode
if ( b == BOUND_EXACT
|| (b == BOUND_LOWER ? value >= beta : value <= alpha))
{
- tte->save(posKey, value_to_tt(value, ss->ply), b,
+ tte->save(posKey, value_to_tt(value, ss->ply), ttPv, b,
std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY),
MOVE_NONE, VALUE_NONE);
{
if ((ss-1)->currentMove != MOVE_NULL)
{
- int p = (ss-1)->statScore;
- int bonus = p > 0 ? (-p - 2500) / 512 :
- p < 0 ? (-p + 2500) / 512 : 0;
+ int bonus = -(ss-1)->statScore / 512;
pureStaticEval = evaluate(pos);
ss->staticEval = eval = pureStaticEval + bonus;
else
ss->staticEval = eval = pureStaticEval = -(ss-1)->staticEval + 2 * Eval::Tempo;
- tte->save(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, pureStaticEval);
+ tte->save(posKey, VALUE_NONE, ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, pureStaticEval);
}
// Step 7. Razoring (~2 Elo)
skipQuiets = 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
// or a beta cutoff occurs.
&& tte->depth() >= depth - 3 * ONE_PLY
&& pos.legal(move))
{
- Value reducedBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE);
+ Value singularBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE);
ss->excludedMove = move;
- value = search<NonPV>(pos, ss, reducedBeta - 1, reducedBeta, depth / 2, cutNode);
+ value = search<NonPV>(pos, ss, singularBeta - 1, singularBeta, depth / 2, cutNode);
ss->excludedMove = MOVE_NONE;
- if (value < reducedBeta)
+ if (value < singularBeta)
extension = ONE_PLY;
+
+ // Multi-cut pruning
+ // Our ttMove is assumed to fail high, and now we failed high also on a reduced
+ // search without the ttMove. So we assume this expected Cut-node is not singular,
+ // that is multiple moves fail high, and we can prune the whole subtree by returning
+ // the hard beta bound.
+ else if (cutNode && singularBeta > beta)
+ return beta;
}
- else if ( givesCheck // Check extension (~2 Elo)
- && pos.see_ge(move))
+
+ // Check extension (~2 Elo)
+ else if ( givesCheck
+ && (pos.blockers_for_king(~us) & from_sq(move) || pos.see_ge(move)))
extension = ONE_PLY;
- // Extension if castling
+ // Castling extension
else if (type_of(move) == CASTLING)
extension = ONE_PLY;
{
Depth r = reduction<PvNode>(improving, depth, moveCount);
+ // Decrease reduction if position is or has been on the PV
+ if (ttPv)
+ r -= ONE_PLY;
+
// Decrease reduction if opponent's move count is high (~10 Elo)
if ((ss-1)->moveCount > 15)
r -= ONE_PLY;
if (!captureOrPromotion)
{
- // Decrease reduction for exact PV nodes (~0 Elo)
- if (pvExact)
- r -= ONE_PLY;
-
// Increase reduction if ttMove is a capture (~0 Elo)
if (ttCapture)
r += ONE_PLY;
bestValue = std::min(bestValue, maxValue);
if (!excludedMove)
- tte->save(posKey, value_to_tt(bestValue, ss->ply),
+ tte->save(posKey, value_to_tt(bestValue, ss->ply), ttPv,
bestValue >= beta ? BOUND_LOWER :
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, pureStaticEval);
Move ttMove, move, bestMove;
Depth ttDepth;
Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha;
- bool ttHit, inCheck, givesCheck, evasionPrunable;
+ bool ttHit, pvHit, inCheck, givesCheck, evasionPrunable;
int moveCount;
if (PvNode)
tte = TT.probe(posKey, ttHit);
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
ttMove = ttHit ? tte->move() : MOVE_NONE;
+ pvHit = ttHit && tte->is_pv();
if ( !PvNode
&& ttHit
if (bestValue >= beta)
{
if (!ttHit)
- tte->save(posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER,
+ tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, BOUND_LOWER,
DEPTH_NONE, MOVE_NONE, ss->staticEval);
return bestValue;
if (inCheck && bestValue == -VALUE_INFINITE)
return mated_in(ss->ply); // Plies to mate from the root
- tte->save(posKey, value_to_tt(bestValue, ss->ply),
+ tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit,
bestValue >= beta ? BOUND_LOWER :
PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
ttDepth, bestMove, ss->staticEval);
}
// We should not stop pondering until told so by the GUI
- if (Threads.ponder)
+ if (ponder)
return;
- if ( (Limits.use_time_management() && elapsed > Time.maximum() - 10)
+ if ( (Limits.use_time_management() && (elapsed > Time.maximum() - 10 || stopOnPonderhit))
|| (Limits.movetime && elapsed >= Limits.movetime)
|| (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes))
Threads.stop = true;