Skip to content

Commit

Permalink
Tablebases root ranking
Browse files Browse the repository at this point in the history
This patch corrects both MultiPV behaviour and "go searchmoves" behaviour
for tablebases.

We change the logic of table base probing at root positions from filtering
to ranking. The ranking code is much more straightforward than the current
filtering code (this is a simplification), and also more versatile.

If the root is a TB position, each root move is probed and assigned a TB score
and a TB rank. The TB score is the Value to be displayed to the user for that
move (unless the search finds a mate score), while the TB rank determines which
moves should appear higher in a multi-pv search. In game play, the engine will
always pick a move with the highest rank.

Ranks run from -1000 to +1000:

901 to 1000   : TB win
900           : normally a TB win, in rare cases this could be a draw
1 to 899      : cursed TB wins
0             : draw
-1 to -899    : blessed TB losses
-900          : normally a TB loss, in rare cases this could be a draw
-901 to -1000 : TB loss

Normally all winning moves get rank 1000 (to let the search pick the best
among them). The exception is if there has been a first repetition. In that
case, moves are ranked strictly by DTZ so that the engine will play a move
that lowers DTZ (and therefore cannot repeat the position a second time).

Losing moves get rank -1000 unless they have relatively high DTZ, meaning
they have some drawing chances. Those get ranks towards -901 (when they
cross -900 the draw is certain).

Closes #1467

No functional change (without tablebases).
  • Loading branch information
syzygy1 authored and snicolet committed Apr 18, 2018
1 parent e9aeaad commit 108f0da
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 208 deletions.
29 changes: 29 additions & 0 deletions src/position.cpp
Expand Up @@ -1105,6 +1105,35 @@ bool Position::is_draw(int ply) const {
}


// Position::has_repeated() tests whether there has been at least one repetition
// of positions since the last capture or pawn move.

bool Position::has_repeated() const {

StateInfo* stc = st;
while (true)
{
int i = 4, e = std::min(stc->rule50, stc->pliesFromNull);

if (e < i)
return false;

StateInfo* stp = st->previous->previous;

do {
stp = stp->previous->previous;

if (stp->key == stc->key)
return true;

i += 2;
} while (i <= e);

stc = stc->previous;
}
}


/// Position::flip() flips position with the white and black sides reversed. This
/// is only useful for debugging e.g. for finding evaluation symmetry bugs.

Expand Down
1 change: 1 addition & 0 deletions src/position.h
Expand Up @@ -152,6 +152,7 @@ class Position {
bool is_chess960() const;
Thread* this_thread() const;
bool is_draw(int ply) const;
bool has_repeated() const;
int rule50_count() const;
Score psq_score() const;
Value non_pawn_material(Color c) const;
Expand Down
78 changes: 43 additions & 35 deletions src/search.cpp
Expand Up @@ -48,7 +48,6 @@ namespace Tablebases {
bool RootInTB;
bool UseRule50;
Depth ProbeDepth;
Value Score;
}

namespace TB = Tablebases;
Expand Down Expand Up @@ -354,9 +353,20 @@ void Thread::search() {
for (RootMove& rm : rootMoves)
rm.previousScore = rm.score;

size_t PVFirst = 0;
PVLast = 0;

// MultiPV loop. We perform a full root search for each PV line
for (PVIdx = 0; PVIdx < multiPV && !Threads.stop; ++PVIdx)
{
if (PVIdx == PVLast)
{
PVFirst = PVLast;
for (PVLast++; PVLast < rootMoves.size(); PVLast++)
if (rootMoves[PVLast].TBRank != rootMoves[PVFirst].TBRank)
break;
}

// Reset UCI info selDepth for each depth and each PV line
selDepth = 0;

Expand Down Expand Up @@ -388,7 +398,7 @@ void Thread::search() {
// and we want to keep the same order for all the moves except the
// new PV that goes to the front. Note that in case of MultiPV
// search the already searched PV lines are preserved.
std::stable_sort(rootMoves.begin() + PVIdx, rootMoves.end());
std::stable_sort(rootMoves.begin() + PVIdx, rootMoves.begin() + PVLast);

// If search has been stopped, we break immediately. Sorting is
// safe because RootMoves is still valid, although it refers to
Expand Down Expand Up @@ -428,7 +438,7 @@ void Thread::search() {
}

// Sort the PV lines searched so far and update the GUI
std::stable_sort(rootMoves.begin(), rootMoves.begin() + PVIdx + 1);
std::stable_sort(rootMoves.begin() + PVFirst, rootMoves.begin() + PVIdx + 1);

if ( mainThread
&& (Threads.stop || PVIdx + 1 == multiPV || Time.elapsed() > 3000))
Expand Down Expand Up @@ -843,9 +853,10 @@ namespace {

// At root obey the "searchmoves" option and skip moves not listed in Root
// Move List. As a consequence any illegal move is also skipped. In MultiPV
// mode we also skip PV moves which have been already searched.
// mode we also skip PV moves which have been already searched and those
// of lower "TB rank" if we are in a TB root position.
if (rootNode && !std::count(thisThread->rootMoves.begin() + thisThread->PVIdx,
thisThread->rootMoves.end(), move))
thisThread->rootMoves.begin() + thisThread->PVLast, move))
continue;

ss->moveCount = ++moveCount;
Expand Down Expand Up @@ -1557,7 +1568,7 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
Value v = updated ? rootMoves[i].score : rootMoves[i].previousScore;

bool tb = TB::RootInTB && abs(v) < VALUE_MATE - MAX_PLY;
v = tb ? TB::Score : v;
v = tb ? rootMoves[i].TBScore : v;

if (ss.rdbuf()->in_avail()) // Not at first line
ss << "\n";
Expand Down Expand Up @@ -1618,52 +1629,49 @@ bool RootMove::extract_ponder_from_tt(Position& pos) {
return pv.size() > 1;
}


void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) {
void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) {

RootInTB = false;
UseRule50 = Options["Syzygy50MoveRule"];
ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY;
Cardinality = Options["SyzygyProbeLimit"];
bool dtz_available = true;

// Skip TB probing when no TB found: !TBLargest -> !TB::Cardinality
// Tables with fewer pieces than SyzygyProbeLimit are searched with
// ProbeDepth == DEPTH_ZERO
if (Cardinality > MaxCardinality)
{
Cardinality = MaxCardinality;
ProbeDepth = DEPTH_ZERO;
}

if (Cardinality < popcount(pos.pieces()) || pos.can_castle(ANY_CASTLING))
return;

// Don't filter any moves if the user requested analysis on multiple
if (Options["MultiPV"] != 1)
return;
if (Cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING))
{
// Rank moves using DTZ tables
RootInTB = root_probe(pos, rootMoves);

// If the current root position is in the tablebases, then RootMoves
// contains only moves that preserve the draw or the win.
RootInTB = root_probe(pos, rootMoves, TB::Score);
if (!RootInTB)
{
// DTZ tables are missing; try to rank moves using WDL tables
dtz_available = false;
RootInTB = root_probe_wdl(pos, rootMoves);
}
}

if (RootInTB)
Cardinality = 0; // Do not probe tablebases during the search

else // If DTZ tables are missing, use WDL tables as a fallback
{
// Filter out moves that do not preserve the draw or the win.
RootInTB = root_probe_wdl(pos, rootMoves, TB::Score);
// Sort moves according to TB rank
std::sort(rootMoves.begin(), rootMoves.end(),
[](const RootMove &a, const RootMove &b) { return a.TBRank > b.TBRank; } );

// Only probe during search if winning
if (RootInTB && TB::Score <= VALUE_DRAW)
// Probe during search only if DTZ is not available and we are winning
if (dtz_available || rootMoves[0].TBScore <= VALUE_DRAW)
Cardinality = 0;
}

if (RootInTB && !UseRule50)
TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1
: TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1
: VALUE_DRAW;

// Since root_probe() and root_probe_wdl() dirty the root move scores,
// we reset them to -VALUE_INFINITE
for (RootMove& rm : rootMoves)
rm.score = -VALUE_INFINITE;
else
{
// Assign the same rank to all moves
for (auto& m : rootMoves)
m.TBRank = 0;
}
}
2 changes: 2 additions & 0 deletions src/search.h
Expand Up @@ -69,6 +69,8 @@ struct RootMove {
Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE;
int selDepth = 0;
int TBRank;
Value TBScore;
std::vector<Move> pv;
};

Expand Down

0 comments on commit 108f0da

Please sign in to comment.