Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rebased add functions to check for decisive scores #5696

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/nnue/nnue_misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat
board[y][x] = board[y][x + 8] = board[y + 3][x + 8] = board[y + 3][x] = '+';
if (pc != NO_PIECE)
board[y + 1][x + 4] = PieceToChar[pc];
if (value != VALUE_NONE)
if (is_valid(value))
format_cp_compact(value, &board[y + 2][x + 2], pos);
};

Expand Down
2 changes: 1 addition & 1 deletion src/score.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace Stockfish {
Score::Score(Value v, const Position& pos) {
assert(-VALUE_INFINITE < v && v < VALUE_INFINITE);

if (std::abs(v) < VALUE_TB_WIN_IN_MAX_PLY)
if (!is_decisive(v))
{
score = InternalUnits{UCIEngine::to_cp(v, pos)};
}
Expand Down
80 changes: 36 additions & 44 deletions src/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ void Search::Worker::iterative_deepening() {
// if we would have had time to fully search other root-moves. Thus
// we suppress this output and below pick a proven score/PV for this
// thread (from the previous iteration).
&& !(threads.abortedSearch && rootMoves[0].uciScore <= VALUE_TB_LOSS_IN_MAX_PLY))
&& !(threads.abortedSearch && is_loss(rootMoves[0].uciScore)))
main_manager()->pv(*this, threads, tt, rootDepth);

if (threads.stop)
Expand All @@ -401,7 +401,7 @@ void Search::Worker::iterative_deepening() {
// We make sure not to pick an unproven mated-in score,
// in case this thread prematurely stopped search (aborted-search).
if (threads.abortedSearch && rootMoves[0].score != -VALUE_INFINITE
&& rootMoves[0].score <= VALUE_TB_LOSS_IN_MAX_PLY)
&& is_loss(rootMoves[0].score))
{
// Bring the last best move to the front for best thread selection.
Utility::move_to_front(rootMoves, [&lastBestPV = std::as_const(lastBestPV)](
Expand Down Expand Up @@ -635,7 +635,7 @@ Value Search::Worker::search(

// At non-PV nodes we check for an early TT cutoff
if (!PvNode && !excludedMove && ttData.depth > depth - (ttData.value <= beta)
&& ttData.value != VALUE_NONE // Can happen when !ttHit or when access race in probe()
&& is_valid(ttData.value) // Can happen when !ttHit or when access race in probe()
&& (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER))
&& (cutNode == (ttData.value >= beta) || depth > 8))
{
Expand Down Expand Up @@ -732,7 +732,7 @@ Value Search::Worker::search(
{
// Never assume anything about values stored in TT
unadjustedStaticEval = ttData.eval;
if (unadjustedStaticEval == VALUE_NONE)
if (!is_valid(unadjustedStaticEval))
unadjustedStaticEval =
evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]);
else if (PvNode)
Expand All @@ -742,7 +742,7 @@ Value Search::Worker::search(
to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss);

// ttValue can be used as a better position evaluation (~7 Elo)
if (ttData.value != VALUE_NONE
if (is_valid(ttData.value)
&& (ttData.bound & (ttData.value > eval ? BOUND_LOWER : BOUND_UPPER)))
eval = ttData.value;
}
Expand Down Expand Up @@ -782,7 +782,7 @@ Value Search::Worker::search(
if (eval < alpha - 469 - 307 * depth * depth)
{
value = qsearch<NonPV>(pos, ss, alpha - 1, alpha);
if (value < alpha && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY)
if (value < alpha && !is_decisive(value))
return value;
}

Expand All @@ -792,16 +792,15 @@ Value Search::Worker::search(
&& eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening)
- (ss - 1)->statScore / 290
>= beta
&& eval >= beta && (!ttData.move || ttCapture) && beta > VALUE_TB_LOSS_IN_MAX_PLY
&& eval < VALUE_TB_WIN_IN_MAX_PLY)
&& eval >= beta && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval))
return beta + (eval - beta) / 3;

improving |= ss->staticEval >= beta + 100;

// Step 9. Null move search with verification search (~35 Elo)
if (cutNode && (ss - 1)->currentMove != Move::null() && eval >= beta
&& ss->staticEval >= beta - 21 * depth + 421 && !excludedMove && pos.non_pawn_material(us)
&& ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY)
&& ss->ply >= thisThread->nmpMinPly && !is_loss(beta))
{
assert(eval - beta >= 0);

Expand All @@ -819,7 +818,7 @@ Value Search::Worker::search(
pos.undo_null_move();

// Do not return unproven mate or TB scores
if (nullValue >= beta && nullValue < VALUE_TB_WIN_IN_MAX_PLY)
if (nullValue >= beta && !is_win(nullValue))
{
if (thisThread->nmpMinPly || depth < 16)
return nullValue;
Expand Down Expand Up @@ -858,12 +857,12 @@ Value Search::Worker::search(
// returns a value much above beta, we can (almost) safely prune the previous move.
probCutBeta = beta + 187 - 53 * improving - 27 * opponentWorsening;
if (!PvNode && depth > 3
&& std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY
&& !is_decisive(beta)
// If value from transposition table is lower than probCutBeta, don't attempt
// probCut there and in further interactions with transposition table cutoff
// depth is set to depth - 3 because probCut search has depth set to depth - 4
// but we also do a move before it. So effective depth is equal to depth - 3.
&& !(ttData.depth >= depth - 3 && ttData.value != VALUE_NONE && ttData.value < probCutBeta))
&& !(ttData.depth >= depth - 3 && is_valid(ttData.value) && ttData.value < probCutBeta))
{
assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta);

Expand Down Expand Up @@ -915,8 +914,7 @@ Value Search::Worker::search(
// Save ProbCut data into transposition table
ttWriter.write(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER,
depth - 3, move, unadjustedStaticEval, tt.generation());
return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta)
: value;
return is_decisive(value) ? value : value - (probCutBeta - beta);
}
}

Expand All @@ -928,8 +926,7 @@ Value Search::Worker::search(
// Step 12. A small Probcut idea (~4 Elo)
probCutBeta = beta + 417;
if ((ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 && ttData.value >= probCutBeta
&& std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY
&& std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY)
&& !is_decisive(beta) && is_valid(ttData.value) && !is_decisive(ttData.value))
return probCutBeta;

const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory,
Expand Down Expand Up @@ -992,7 +989,7 @@ Value Search::Worker::search(

// Step 14. Pruning at shallow depth (~120 Elo).
// Depth conditions are important for mate finding.
if (!rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY)
if (!rootNode && pos.non_pawn_material(us) && !is_loss(bestValue))
{
// Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~8 Elo)
if (moveCount >= futility_move_count(improving, depth))
Expand Down Expand Up @@ -1042,8 +1039,8 @@ Value Search::Worker::search(
// Futility pruning: parent node (~13 Elo)
if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha)
{
if (bestValue <= futilityValue && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY
&& futilityValue < VALUE_TB_WIN_IN_MAX_PLY)
if (bestValue <= futilityValue
&& !is_decisive(bestValue) && !is_win(futilityValue))
bestValue = futilityValue;
continue;
}
Expand Down Expand Up @@ -1075,8 +1072,8 @@ Value Search::Worker::search(

if (!rootNode && move == ttData.move && !excludedMove
&& depth >= 4 - (thisThread->completedDepth > 33) + ss->ttPv
&& std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY && (ttData.bound & BOUND_LOWER)
&& ttData.depth >= depth - 3)
&& is_valid(ttData.value) && !is_decisive(ttData.value)
&& (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 3)
{
Value singularBeta = ttData.value - (56 + 79 * (ss->ttPv && !PvNode)) * depth / 64;
Depth singularDepth = newDepth / 2;
Expand All @@ -1103,7 +1100,7 @@ Value Search::Worker::search(
// 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 (value >= beta && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY)
else if (value >= beta && !is_decisive(value))
return value;

// Negative extensions
Expand Down Expand Up @@ -1313,9 +1310,8 @@ Value Search::Worker::search(

// 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 >= thisThread->rootDepth
&& std::abs(value) + 1 < VALUE_TB_WIN_IN_MAX_PLY);
int inc = (value == bestValue && ss->ply + 2 >= thisThread->rootDepth
&& (int(nodes) & 15) == 0 && !is_win(std::abs(value) + 1));

if (value + inc > bestValue)
{
Expand All @@ -1337,7 +1333,7 @@ Value Search::Worker::search(
else
{
// Reduce other moves if we have found at least one score improvement (~2 Elo)
if (depth > 2 && depth < 14 && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY)
if (depth > 2 && depth < 14 && !is_decisive(value))
depth -= 2;

assert(depth > 0);
Expand Down Expand Up @@ -1365,8 +1361,8 @@ Value Search::Worker::search(
assert(moveCount || !ss->inCheck || excludedMove || !MoveList<LEGAL>(pos).size());

// Adjust best value for fail high cases at non-pv nodes
if (!PvNode && bestValue >= beta && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY
&& std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(alpha) < VALUE_TB_WIN_IN_MAX_PLY)
if (!PvNode && bestValue >= beta && !is_decisive(bestValue)
&& !is_decisive(beta) && !is_decisive(alpha))
bestValue = (bestValue * depth + beta) / (depth + 1);

if (!moveCount)
Expand Down Expand Up @@ -1518,7 +1514,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)

// At non-PV nodes we check for an early TT cutoff
if (!PvNode && ttData.depth >= DEPTH_QS
&& ttData.value != VALUE_NONE // Can happen when !ttHit or when access race in probe()
&& is_valid(ttData.value) // Can happen when !ttHit or when access race in probe()
&& (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER)))
return ttData.value;

Expand All @@ -1532,14 +1528,14 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
{
// Never assume anything about values stored in TT
unadjustedStaticEval = ttData.eval;
if (unadjustedStaticEval == VALUE_NONE)
if (!is_valid(unadjustedStaticEval))
unadjustedStaticEval =
evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]);
ss->staticEval = bestValue =
to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss);

// ttValue can be used as a better position evaluation (~13 Elo)
if (std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY
if (is_valid(ttData.value) && !is_decisive(ttData.value)
&& (ttData.bound & (ttData.value > bestValue ? BOUND_LOWER : BOUND_UPPER)))
bestValue = ttData.value;
}
Expand All @@ -1557,7 +1553,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
// Stand pat. Return immediately if static value is at least beta
if (bestValue >= beta)
{
if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY)
if (!is_decisive(bestValue))
bestValue = (bestValue + beta) / 2;
if (!ss->ttHit)
ttWriter.write(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER,
Expand Down Expand Up @@ -1598,10 +1594,10 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
moveCount++;

// Step 6. Pruning
if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY && pos.non_pawn_material(us))
if (!is_loss(bestValue) && pos.non_pawn_material(us))
{
// Futility pruning and moveCount pruning (~10 Elo)
if (!givesCheck && move.to_sq() != prevSq && futilityBase > VALUE_TB_LOSS_IN_MAX_PLY
if (!givesCheck && move.to_sq() != prevSq && !is_loss(futilityBase)
&& move.type_of() != PROMOTION)
{
if (moveCount > 2)
Expand Down Expand Up @@ -1688,7 +1684,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
return mated_in(ss->ply); // Plies to mate from the root
}

if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && bestValue >= beta)
if (!is_decisive(bestValue) && bestValue >= beta)
bestValue = (3 * bestValue + beta) / 4;

// Save gathered info in transposition table. The static evaluation
Expand Down Expand Up @@ -1726,11 +1722,7 @@ namespace {
// Adjusts a mate or TB score from "plies to mate from the root" to
// "plies to mate from the current position". Standard scores are unchanged.
// The function is called before storing a value in the transposition table.
Value value_to_tt(Value v, int ply) {

assert(v != VALUE_NONE);
return v >= VALUE_TB_WIN_IN_MAX_PLY ? v + ply : v <= VALUE_TB_LOSS_IN_MAX_PLY ? v - ply : v;
}
Value value_to_tt(Value v, int ply) { return is_win(v) ? v + ply : is_loss(v) ? v - ply : v; }


// Inverse of value_to_tt(): it adjusts a mate or TB score from the transposition
Expand All @@ -1740,11 +1732,11 @@ Value value_to_tt(Value v, int ply) {
// graph history interaction, we return the highest non-TB score instead.
Value value_from_tt(Value v, int ply, int r50c) {

if (v == VALUE_NONE)
if (!is_valid(v))
return VALUE_NONE;

// handle TB win or better
if (v >= VALUE_TB_WIN_IN_MAX_PLY)
if (is_win(v))
{
// Downgrade a potentially false mate score
if (v >= VALUE_MATE_IN_MAX_PLY && VALUE_MATE - v > 100 - r50c)
Expand All @@ -1758,7 +1750,7 @@ Value value_from_tt(Value v, int ply, int r50c) {
}

// handle TB loss or worse
if (v <= VALUE_TB_LOSS_IN_MAX_PLY)
if (is_loss(v))
{
// Downgrade a potentially false mate score.
if (v <= VALUE_MATED_IN_MAX_PLY && VALUE_MATE + v > 100 - r50c)
Expand Down Expand Up @@ -2097,7 +2089,7 @@ void SearchManager::pv(Search::Worker& worker,
bool isExact = i != pvIdx || tb || !updated; // tablebase- and previous-scores are exact

// Potentially correct and extend the PV, and in exceptional cases v
if (std::abs(v) >= VALUE_TB_WIN_IN_MAX_PLY && std::abs(v) < VALUE_MATE_IN_MAX_PLY
if (is_decisive(v) && std::abs(v) < VALUE_MATE_IN_MAX_PLY
&& ((!rootMoves[i].scoreLowerbound && !rootMoves[i].scoreUpperbound) || isExact))
syzygy_extend_pv(worker.options, worker.limits, pos, rootMoves[i], v);

Expand Down
10 changes: 5 additions & 5 deletions src/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,13 +329,13 @@ Thread* ThreadPool::get_best_thread() const {
const auto bestThreadMoveVote = votes[bestThreadPV[0]];
const auto newThreadMoveVote = votes[newThreadPV[0]];

const bool bestThreadInProvenWin = bestThreadScore >= VALUE_TB_WIN_IN_MAX_PLY;
const bool newThreadInProvenWin = newThreadScore >= VALUE_TB_WIN_IN_MAX_PLY;
const bool bestThreadInProvenWin = is_win(bestThreadScore);
const bool newThreadInProvenWin = is_win(newThreadScore);

const bool bestThreadInProvenLoss =
bestThreadScore != -VALUE_INFINITE && bestThreadScore <= VALUE_TB_LOSS_IN_MAX_PLY;
bestThreadScore != -VALUE_INFINITE && is_loss(bestThreadScore);
const bool newThreadInProvenLoss =
newThreadScore != -VALUE_INFINITE && newThreadScore <= VALUE_TB_LOSS_IN_MAX_PLY;
newThreadScore != -VALUE_INFINITE && is_loss(newThreadScore);

// We make sure not to pick a thread with truncated principal variation
const bool betterVotingValue =
Expand All @@ -355,7 +355,7 @@ Thread* ThreadPool::get_best_thread() const {
bestThread = th.get();
}
else if (newThreadInProvenWin || newThreadInProvenLoss
|| (newThreadScore > VALUE_TB_LOSS_IN_MAX_PLY
|| (!is_loss(newThreadScore)
&& (newThreadMoveVote > bestThreadMoveVote
|| (newThreadMoveVote == bestThreadMoveVote && betterVotingValue))))
bestThread = th.get();
Expand Down
15 changes: 15 additions & 0 deletions src/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,21 @@ constexpr Value VALUE_TB = VALUE_MATE_IN_MAX_PLY - 1;
constexpr Value VALUE_TB_WIN_IN_MAX_PLY = VALUE_TB - MAX_PLY;
constexpr Value VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY;


constexpr bool is_valid(Value value) { return value != VALUE_NONE; }

constexpr bool is_win(Value value) {
assert(is_valid(value));
return value >= VALUE_TB_WIN_IN_MAX_PLY;
}

constexpr bool is_loss(Value value) {
assert(is_valid(value));
return value <= VALUE_TB_LOSS_IN_MAX_PLY;
}

constexpr bool is_decisive(Value value) { return is_win(value) || is_loss(value); }

// In the code, we make the assumption that these values
// are such that non_pawn_material() can be used to uniquely
// identify the material on the board.
Expand Down
Loading