}
double bestMoveInstability = 1 + 2 * totBestMoveChanges / Threads.size();
- double totalTime = rootMoves.size() == 1 ? 0 :
- Time.optimum() * fallingEval * reduction * bestMoveInstability;
+ double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability;
- // Stop the search if we have exceeded the totalTime, at least 1ms search
+ // Cap used time in case of a single legal move for a better viewer experience in tournaments
+ // yielding correct scores and sufficiently fast moves.
+ if (rootMoves.size() == 1)
+ totalTime = std::min(500.0, totalTime);
+
+ // Stop the search if we have exceeded the totalTime
if (Time.elapsed() > totalTime)
{
// If we are allowed to ponder do not stop the search now but
&& captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0)
continue;
- // See based pruning
+ // SEE based pruning
if (!pos.see_ge(move, Value(-221) * depth)) // (~25 Elo)
continue;
}
if (thisThread->ttHitAverage > 509 * TtHitAverageResolution * TtHitAverageWindow / 1024)
r--;
- // Reduction if other threads are searching this position
+ // Increase reduction if other threads are searching this position
if (th.marked())
r++;
if (ss->ttPv)
r -= 2;
+ // Increase reduction at root and non-PV nodes when the best move does not change frequently
+ if ((rootNode || !PvNode) && depth > 10 && thisThread->bestMoveChanges <= 2)
+ r++;
+
if (moveCountPruning && !formerPv)
r++;
moveCount++;
// Futility pruning
- if ( !ss->inCheck
+ if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY
&& !givesCheck
&& futilityBase > -VALUE_KNOWN_WIN
&& !pos.advanced_pawn_push(move))
}
// Do not search moves with negative SEE values
- if ( !ss->inCheck
+ if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY
&& !(givesCheck && pos.is_discovery_check_on_king(~pos.side_to_move(), move))
&& !pos.see_ge(move))
continue;