Syzygy endgame tablebase probing

Syzygy tablebases provide WDL (win/draw/loss) and DTZ (distance to zero) information for all endgame positions with up to 6 (and experimentally 7) pieces. Positions with castling rights are not included.

chess.syzygy.open_tablebase(directory, *, load_wdl=True, load_dtz=True, max_fds=128, VariantBoard=<class 'chess.Board'>)

Opens a collection of tables for probing. See Tablebase.

Note

Generally probing requires tablebase files for the specific material composition, as well as tablebase files with less pieces. This is important because 6-piece and 5-piece files are often distributed seperately, but are both required for 6-piece positions. Use add_directory() to load tables from additional directories.

class chess.syzygy.Tablebase(*, max_fds=128, VariantBoard=<class 'chess.Board'>)

Manages a collection of tablebase files for probing.

If max_fds is not None, will at most use max_fds open file descriptors at any given time. The least recently used tables are closed, if nescessary.

add_directory(directory, *, load_wdl=True, load_dtz=True)

Loads tables from a directory.

By default all available tables with the correct file names (e.g. WDL files like KQvKN.rtbw and DTZ files like KRBvK.rtbz) are loaded.

Returns the number of table files that were found.

open_directory(directory, *, load_wdl=True, load_dtz=True)

Loads tables from a directory.

By default all available tables with the correct file names (e.g. WDL files like KQvKN.rtbw and DTZ files like KRBvK.rtbz) are loaded.

Returns the number of table files that were found.

probe_wdl(board)

Probes WDL tables for win/draw/loss-information.

Probing is thread-safe when done with different board objects and if board objects are not modified during probing.

Returns 2 if the side to move is winning, 0 if the position is a draw and -2 if the side to move is losing.

Returns 1 in case of a cursed win and -1 in case of a blessed loss. Mate can be forced but the position can be drawn due to the fifty-move rule.

>>> import chess
>>> import chess.syzygy
>>>
>>> with chess.syzygy.open_tablebase("data/syzygy/regular") as tablebase:
...     board = chess.Board("8/2K5/4B3/3N4/8/8/4k3/8 b - - 0 1")
...     print(tablebase.probe_wdl(board))
...
-2
Raises:KeyError (or specifically chess.syzygy.MissingTableError) if the probe fails. Use get_wdl() if you prefer to get None instead of an exception.
probe_dtz(board)

Probes DTZ tables for distance to zero information.

Both DTZ and WDL tables are required in order to probe for DTZ.

Returns a positive value if the side to move is winning, 0 if the position is a draw and a negative value if the side to move is losing. More precisely:

WDL DTZ  
-2 -100 <= n <= -1 Unconditional loss (assuming 50-move counter is zero), where a zeroing move can be forced in -n plies.
-1 n < -100 Loss, but draw under the 50-move rule. A zeroing move can be forced in -n plies or -n - 100 plies (if a later phase is responsible for the blessed loss).
0 0 Draw.
1 100 < n Win, but draw under the 50-move rule. A zeroing move can be forced in n plies or n - 100 plies (if a later phase is responsible for the cursed win).
2 1 <= n <= 100 Unconditional win (assuming 50-move counter is zero), where a zeroing move can be forced in n plies.

The return value can be off by one: a return value -n can mean a losing zeroing move in in n + 1 plies and a return value +n can mean a winning zeroing move in n + 1 plies. This is guaranteed not to happen for positions exactly on the edge of the 50-move rule, so that (with some care) this never impacts the result of practical play.

Minmaxing the DTZ values guarantees winning a won position (and drawing a drawn position), because it makes progress keeping the win in hand. However the lines are not always the most straightforward ways to win. Engines like Stockfish calculate themselves, checking with DTZ, but only play according to DTZ if they can not manage on their own.

>>> import chess
>>> import chess.syzygy
>>>
>>> with chess.syzygy.open_tablebase("data/syzygy/regular") as tablebase:
...     board = chess.Board("8/2K5/4B3/3N4/8/8/4k3/8 b - - 0 1")
...     print(tablebase.probe_dtz(board))
...
-53

Probing is thread-safe when done with different board objects and if board objects are not modified during probing.

Raises:KeyError (or specifically chess.syzygy.MissingTableError) if the probe fails. Use get_dtz() if you prefer to get None instead of an exception.
close()

Closes all loaded tables.