mirror of
https://github.com/sockspls/badfish
synced 2025-04-30 08:43:09 +00:00
Added 'wdl' and 'dtz' UI debug commands
Probe WDL and DTZ tables on current position.
This commit is contained in:
parent
0700912dc5
commit
de335433ea
3 changed files with 130 additions and 92 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
13
src/uci.cpp
13
src/uci.cpp
|
@ -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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue