fixes the lowerbound/upperbound output by taking the alpha,beta bracket
into account also if a bestThread is selected that is different from the master thread.
Instead of keeping track which bounds where used in the specific search,
in this version we simply store the quality (exact, upperbound,
lowerbound) of the score along with the actual score as information on
rootMove.
closes https://github.com/official-stockfish/Stockfish/pull/4239
No functional change
// Send again PV info if we have a new best thread
if (bestThread != this)
// Send again PV info if we have a new best thread
if (bestThread != this)
- sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth, -VALUE_INFINITE, VALUE_INFINITE) << sync_endl;
+ sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth) << sync_endl;
sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960());
sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960());
&& multiPV == 1
&& (bestValue <= alpha || bestValue >= beta)
&& Time.elapsed() > 3000)
&& multiPV == 1
&& (bestValue <= alpha || bestValue >= beta)
&& Time.elapsed() > 3000)
- sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl;
+ sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl;
// In case of failing low/high increase aspiration window and
// re-search, otherwise exit the loop.
// In case of failing low/high increase aspiration window and
// re-search, otherwise exit the loop.
if ( mainThread
&& (Threads.stop || pvIdx + 1 == multiPV || Time.elapsed() > 3000))
if ( mainThread
&& (Threads.stop || pvIdx + 1 == multiPV || Time.elapsed() > 3000))
- sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl;
+ sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl;
{
rm.score = value;
rm.selDepth = thisThread->selDepth;
{
rm.score = value;
rm.selDepth = thisThread->selDepth;
+ rm.scoreLowerbound = value >= beta;
+ rm.scoreUpperbound = value <= alpha;
rm.pv.resize(1);
assert((ss+1)->pv);
rm.pv.resize(1);
assert((ss+1)->pv);
/// UCI::pv() formats PV information according to the UCI protocol. UCI requires
/// that all (if any) unsearched PV lines are sent using a previous search score.
/// UCI::pv() formats PV information according to the UCI protocol. UCI requires
/// that all (if any) unsearched PV lines are sent using a previous search score.
-string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
+string UCI::pv(const Position& pos, Depth depth) {
std::stringstream ss;
TimePoint elapsed = Time.elapsed() + 1;
std::stringstream ss;
TimePoint elapsed = Time.elapsed() + 1;
if (Options["UCI_ShowWDL"])
ss << UCI::wdl(v, pos.game_ply());
if (Options["UCI_ShowWDL"])
ss << UCI::wdl(v, pos.game_ply());
- if (!tb && i == pvIdx)
- ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
+ if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact
+ ss << (rootMoves[i].scoreLowerbound ? " lowerbound" : (rootMoves[i].scoreUpperbound ? " upperbound" : ""));
ss << " nodes " << nodesSearched
<< " nps " << nodesSearched * 1000 / elapsed
ss << " nodes " << nodesSearched
<< " nps " << nodesSearched * 1000 / elapsed
Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE;
Value averageScore = -VALUE_INFINITE;
Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE;
Value averageScore = -VALUE_INFINITE;
+ bool scoreLowerbound = false;
+ bool scoreUpperbound = false;
int selDepth = 0;
int tbRank = 0;
Value tbScore;
int selDepth = 0;
int tbRank = 0;
Value tbScore;
std::string value(Value v);
std::string square(Square s);
std::string move(Move m, bool chess960);
std::string value(Value v);
std::string square(Square s);
std::string move(Move m, bool chess960);
-std::string pv(const Position& pos, Depth depth, Value alpha, Value beta);
+std::string pv(const Position& pos, Depth depth);
std::string wdl(Value v, int ply);
Move to_move(const Position& pos, std::string& str);
std::string wdl(Value v, int ply);
Move to_move(const Position& pos, std::string& str);