Syzygy endgame tablebase probing

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

Warning

Ensure tablebase files match the known checksums. Maliciously crafted tablebase files may cause denial of service.

chess.syzygy.open_tablebase(directory: str, *, load_wdl: bool = True, load_dtz: bool = True, max_fds: Optional[int] = 128, VariantBoard: Type[chess.Board] = <class 'chess.Board'>)chess.syzygy.Tablebase[source]

Opens a collection of tables for probing. See Tablebase.

Note

Generally probing requires tablebase files for the specific material composition, as well as material compositions transitively reachable by captures and promotions. This is important because 6-piece and 5-piece (let alone 7-piece) files are often distributed separately, but are both required for 6-piece positions. Use add_directory() to load tables from additional directories.

class chess.syzygy.Tablebase(*, max_fds: Optional[int] = 128, VariantBoard: Type[chess.Board] = <class 'chess.Board'>)[source]

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: str, *, load_wdl: bool = True, load_dtz: bool = True) → int[source]

Adds 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 added.

The relevant files are lazily opened when the tablebase is actually probed.

Returns the number of table files that were found.

probe_wdl(board: chess.Board) → int[source]

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 position could not be found in the tablebase. Use get_wdl() if you prefer to get None instead of an exception.

Note that probing corrupted table files is undefined behavior.

probe_dtz(board: chess.Board) → int[source]

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 position could not be found in the tablebase. Use get_dtz() if you prefer to get None instead of an exception.

Note that probing corrupted table files is undefined behavior.

close() → None[source]

Closes all loaded tables.