mirror of
https://github.com/sockspls/badfish
synced 2025-04-30 00:33: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;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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
|
||||
// since the last capture or pawn move.
|
||||
static int has_repeated(StateInfo *st)
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#ifndef TBPROBE_H
|
||||
#define TBPROBE_H
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "../search.h"
|
||||
|
||||
namespace Tablebases {
|
||||
|
@ -47,9 +49,32 @@ extern size_t MaxCardinality;
|
|||
|
||||
void init(const std::string& paths);
|
||||
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_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
|
||||
|
|
13
src/uci.cpp
13
src/uci.cpp
|
@ -29,6 +29,7 @@
|
|||
#include "thread.h"
|
||||
#include "timeman.h"
|
||||
#include "uci.h"
|
||||
#include "syzygy/tbprobe.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -188,6 +189,18 @@ void UCI::loop(int argc, char* argv[]) {
|
|||
Search::clear();
|
||||
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 == "go") go(pos, is);
|
||||
else if (token == "position") position(pos, is);
|
||||
|
|
Loading…
Add table
Reference in a new issue