1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-30 16:53:09 +00:00

Added 'wdl' and 'dtz' UI debug commands

Probe WDL and DTZ tables on current position.
This commit is contained in:
Marco Costalba 2016-05-21 15:14:50 +02:00
parent 0700912dc5
commit de335433ea
3 changed files with 130 additions and 92 deletions

View file

@ -1277,98 +1277,6 @@ WDLScore search(Position& pos, WDLScore alpha, WDLScore beta, ProbeState* result
return alpha; return alpha;
} }
// Probe the DTZ table for a particular position.
// If *result != FAIL, the probe was successful.
// The return value is from the point of view of the side to move:
// n < -100 : loss, but draw under 50-move rule
// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0)
// 0 : draw
// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
// 100 < n : win, but draw under 50-move rule
//
// The return value n can be off by 1: a return value -n can mean a loss
// in n+1 ply and a return value +n can mean a win in n+1 ply. This
// cannot happen for tables with positions exactly on the "edge" of
// the 50-move rule.
//
// This implies that if dtz > 0 is returned, the position is certainly
// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine
// picks moves that preserve dtz + 50-move-counter <= 99.
//
// If n = 100 immediately after a capture or pawn move, then the position
// is also certainly a win, and during the whole phase until the next
// capture or pawn move, the inequality to be preserved is
// dtz + 50-movecounter <= 100.
//
// In short, if a move is available resulting in dtz + 50-move-counter <= 99,
// then do not accept moves leading to dtz + 50-move-counter == 100.
int probe_dtz(Position& pos, ProbeState* result)
{
WDLScore wdl = search<true>(pos, WDLLoss, WDLWin, result);
if (*result == FAIL)
return 0;
if (wdl == WDLDraw) // DTZ tables don't store draws
return 0;
if ( *result == WIN_CAPTURE // DTZ tables store a 'don't care' value in this case
|| *result == WIN_PAWN_MOVE)
return wdl == WDLWin ? 1 : 101; // DTZ scores for immediate win or cursed win
int dtz = 1 + probe_dtz_table(pos, wdl, result); // Probe the table!
if (*result != CHANGE_STM)
{
if (wdl == WDLCursedLoss || wdl == WDLCursedWin)
dtz += 100;
return wdl > WDLDraw ? dtz : -dtz;
}
// DTZ stores results for the other STM, so we need to do a 1-ply search and
// find the winning move that minimizes DTZ.
StateInfo st;
CheckInfo ci(pos);
int minDTZ = 0xFFFF;
for (const Move& move : MoveList<LEGAL>(pos))
{
// When winning, we don't need to probe for captures and pawn
// moves because we already know are losing.
if ( wdl > 0
&& (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN))
continue;
pos.do_move(move, st, pos.gives_check(move, ci));
if (wdl > 0)
dtz = -probe_dtz(pos, result);
else if (st.rule50 > 0) // Not a capture or pawn move
dtz = -probe_dtz(pos, result) - 1;
else if (wdl == WDLLoss)
dtz = -1;
else
dtz = search(pos, WDLCursedWin, WDLWin, result) == WDLWin ? 0 : -101;
pos.undo_move(move);
if (*result == FAIL)
return 0;
if (wdl > 0 && dtz > 0 && dtz + 1 < minDTZ)
minDTZ = dtz + 1;
else if (wdl < 0 && dtz < minDTZ)
minDTZ = dtz;
}
return minDTZ;
}
} // namespace } // namespace
void Tablebases::init(const std::string& paths) void Tablebases::init(const std::string& paths)
@ -1518,6 +1426,98 @@ WDLScore Tablebases::probe_wdl(Position& pos, ProbeState* result)
return search(pos, WDLLoss, WDLWin, result); return search(pos, WDLLoss, WDLWin, result);
} }
// Probe the DTZ table for a particular position.
// If *result != FAIL, the probe was successful.
// The return value is from the point of view of the side to move:
// n < -100 : loss, but draw under 50-move rule
// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0)
// 0 : draw
// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
// 100 < n : win, but draw under 50-move rule
//
// The return value n can be off by 1: a return value -n can mean a loss
// in n+1 ply and a return value +n can mean a win in n+1 ply. This
// cannot happen for tables with positions exactly on the "edge" of
// the 50-move rule.
//
// This implies that if dtz > 0 is returned, the position is certainly
// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine
// picks moves that preserve dtz + 50-move-counter <= 99.
//
// If n = 100 immediately after a capture or pawn move, then the position
// is also certainly a win, and during the whole phase until the next
// capture or pawn move, the inequality to be preserved is
// dtz + 50-movecounter <= 100.
//
// In short, if a move is available resulting in dtz + 50-move-counter <= 99,
// then do not accept moves leading to dtz + 50-move-counter == 100.
int Tablebases::probe_dtz(Position& pos, ProbeState* result)
{
WDLScore wdl = search<true>(pos, WDLLoss, WDLWin, result);
if (*result == FAIL)
return 0;
if (wdl == WDLDraw) // DTZ tables don't store draws
return 0;
if ( *result == WIN_CAPTURE // DTZ tables store a 'don't care' value in this case
|| *result == WIN_PAWN_MOVE)
return wdl == WDLWin ? 1 : 101; // DTZ scores for immediate win or cursed win
int dtz = 1 + probe_dtz_table(pos, wdl, result); // Probe the table!
if (*result != CHANGE_STM)
{
if (wdl == WDLCursedLoss || wdl == WDLCursedWin)
dtz += 100;
return wdl > WDLDraw ? dtz : -dtz;
}
// DTZ stores results for the other STM, so we need to do a 1-ply search and
// find the winning move that minimizes DTZ.
StateInfo st;
CheckInfo ci(pos);
int minDTZ = 0xFFFF;
for (const Move& move : MoveList<LEGAL>(pos))
{
// When winning, we don't need to probe for captures and pawn
// moves because we already know are losing.
if ( wdl > 0
&& (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN))
continue;
pos.do_move(move, st, pos.gives_check(move, ci));
if (wdl > 0)
dtz = -probe_dtz(pos, result);
else if (st.rule50 > 0) // Not a capture or pawn move
dtz = -probe_dtz(pos, result) - 1;
else if (wdl == WDLLoss)
dtz = -1;
else
dtz = search(pos, WDLCursedWin, WDLWin, result) == WDLWin ? 0 : -101;
pos.undo_move(move);
if (*result == FAIL)
return 0;
if (wdl > 0 && dtz > 0 && dtz + 1 < minDTZ)
minDTZ = dtz + 1;
else if (wdl < 0 && dtz < minDTZ)
minDTZ = dtz;
}
return minDTZ;
}
// Check whether there has been at least one repetition of positions // Check whether there has been at least one repetition of positions
// since the last capture or pawn move. // since the last capture or pawn move.
static int has_repeated(StateInfo *st) static int has_repeated(StateInfo *st)

View file

@ -20,6 +20,8 @@
#ifndef TBPROBE_H #ifndef TBPROBE_H
#define TBPROBE_H #define TBPROBE_H
#include <ostream>
#include "../search.h" #include "../search.h"
namespace Tablebases { namespace Tablebases {
@ -47,9 +49,32 @@ extern size_t MaxCardinality;
void init(const std::string& paths); void init(const std::string& paths);
WDLScore probe_wdl(Position& pos, ProbeState* result); WDLScore probe_wdl(Position& pos, ProbeState* result);
int probe_dtz(Position& pos, ProbeState* result);
bool root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score); bool root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score);
bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score); bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score);
inline std::ostream& operator<<(std::ostream& os, const WDLScore v) {
os << (v == WDLLoss ? "Loss" :
v == WDLCursedLoss ? "Cursed loss" :
v == WDLDraw ? "Draw" :
v == WDLCursedWin ? "Cursed win" :
v == WDLWin ? "Win" : "None");
return os;
}
inline std::ostream& operator<<(std::ostream& os, const ProbeState v) {
os << (v == FAIL ? "Failed" :
v == OK ? "Success" :
v == CHANGE_STM ? "Probed opponent side" :
v == WIN_CAPTURE ? "Found a winning capture" :
v == WIN_PAWN_MOVE ? "Found a winning pawn move" : "None");
return os;
}
} }
#endif #endif

View file

@ -29,6 +29,7 @@
#include "thread.h" #include "thread.h"
#include "timeman.h" #include "timeman.h"
#include "uci.h" #include "uci.h"
#include "syzygy/tbprobe.h"
using namespace std; using namespace std;
@ -188,6 +189,18 @@ void UCI::loop(int argc, char* argv[]) {
Search::clear(); Search::clear();
Time.availableNodes = 0; Time.availableNodes = 0;
} }
else if (token == "wdl")
{
Tablebases::ProbeState err;
Tablebases::WDLScore v = Tablebases::probe_wdl(pos, &err);
sync_cout << v << " (" << err << ")" << sync_endl;
}
else if (token == "dtz")
{
Tablebases::ProbeState err;
int dtz = Tablebases::probe_dtz(pos, &err);
sync_cout << dtz << " (" << err << ")" << sync_endl;
}
else if (token == "isready") sync_cout << "readyok" << sync_endl; else if (token == "isready") sync_cout << "readyok" << sync_endl;
else if (token == "go") go(pos, is); else if (token == "go") go(pos, is);
else if (token == "position") position(pos, is); else if (token == "position") position(pos, is);