Skip to content

Tablebase

dblike edited this page Jan 20, 2026 · 1 revision

Tablebase API

Lookup endgame positions from the Lichess tablebase server.

Namespace: LichessSharp.Api.Contracts Access: client.Tablebase


Overview

Tablebases provide perfect play information for endgame positions. The Lichess tablebase server supports:

  • Standard chess: Up to 7-piece positions (Syzygy tablebases)
  • Atomic chess: Up to 6-piece positions
  • Antichess: Up to 6-piece positions (with DTW for 4-piece)

Methods

LookupAsync

Look up a position in the standard chess tablebase.

Task<TablebaseResult> LookupAsync(
    string fen,
    CancellationToken cancellationToken = default)

Example:

using var client = new LichessClient();

// King and Rook vs King
var fen = "4k3/8/8/8/8/8/8/R3K3 w Q - 0 1";

var result = await client.Tablebase.LookupAsync(fen);

Console.WriteLine($"Category: {result.Category}");
Console.WriteLine($"DTZ: {result.Dtz}");
Console.WriteLine($"Checkmate: {result.Checkmate}");
Console.WriteLine($"Stalemate: {result.Stalemate}");

Console.WriteLine("\nBest moves:");
foreach (var move in result.Moves?.Take(5) ?? [])
{
    Console.WriteLine($"  {move.San} ({move.Uci}): {move.Category}, DTZ={move.Dtz}");
}

LookupAtomicAsync

Look up a position in the atomic chess tablebase.

Task<TablebaseResult> LookupAtomicAsync(
    string fen,
    CancellationToken cancellationToken = default)

Example:

using var client = new LichessClient();

// Atomic chess endgame
var fen = "4k3/8/8/8/8/8/4P3/4K3 w - - 0 1";

var result = await client.Tablebase.LookupAtomicAsync(fen);

Console.WriteLine($"Category: {result.Category}");
Console.WriteLine($"Variant win: {result.VariantWin}");
Console.WriteLine($"Variant loss: {result.VariantLoss}");

LookupAntichessAsync

Look up a position in the antichess tablebase.

Task<TablebaseResult> LookupAntichessAsync(
    string fen,
    CancellationToken cancellationToken = default)

Example:

using var client = new LichessClient();

// Antichess endgame (goal is to lose all pieces)
var fen = "8/8/8/8/8/8/p7/K7 w - - 0 1";

var result = await client.Tablebase.LookupAntichessAsync(fen);

Console.WriteLine($"Category: {result.Category}");
Console.WriteLine($"DTW (Depth to Win): {result.Dtw}");

// In antichess, winning means losing all your pieces
foreach (var move in result.Moves ?? [])
{
    Console.WriteLine($"  {move.San}: {move.Category}, DTW={move.Dtw}");
}

Position Categories

Category Description
win Side to move can force a win
loss Side to move will lose with best play
draw Position is drawn with best play
cursed-win Win, but 50-move rule could prevent it
blessed-loss Lost, but 50-move rule could save it
maybe-win Might be a win (insufficient tablebase coverage)
maybe-loss Might be a loss (insufficient tablebase coverage)
unknown Position not in tablebase

DTZ (Distance to Zeroing)

DTZ measures plies (half-moves) until the 50-move counter resets:

  • Zeroing moves: Pawn moves and captures
  • Positive DTZ = winning
  • Negative DTZ = losing
  • Dtz is rounded, PreciseDtz is exact when available

Other Metrics

Metric Description Availability
DTZ Distance to zeroing (plies) Standard (7pc), Variants (6pc)
DTC Distance to conversion Standard
DTM Distance to mate Standard (5pc max)
DTW Distance to win Antichess (4pc max)

Complete Example

using var client = new LichessClient();

async Task AnalyzeEndgame(string fen)
{
    var result = await client.Tablebase.LookupAsync(fen);

    Console.WriteLine($"Position: {fen}\n");

    // Game state
    if (result.Checkmate)
    {
        Console.WriteLine("CHECKMATE!");
        return;
    }
    if (result.Stalemate)
    {
        Console.WriteLine("STALEMATE!");
        return;
    }
    if (result.InsufficientMaterial)
    {
        Console.WriteLine("INSUFFICIENT MATERIAL - Draw");
        return;
    }

    // Evaluation
    Console.WriteLine($"Result: {result.Category}");

    if (result.Dtz.HasValue)
        Console.WriteLine($"DTZ: {result.Dtz} plies");
    if (result.Dtm.HasValue)
        Console.WriteLine($"DTM: {result.Dtm} plies (mate in {(result.Dtm + 1) / 2} moves)");

    // Best moves
    Console.WriteLine("\nMoves (best first):");
    foreach (var move in result.Moves ?? [])
    {
        var info = new List<string>();

        info.Add(move.Category);

        if (move.Dtz.HasValue)
            info.Add($"DTZ={move.Dtz}");
        if (move.Dtm.HasValue)
            info.Add($"DTM={move.Dtm}");
        if (move.Zeroing)
            info.Add("zeroing");
        if (move.Checkmate)
            info.Add("checkmate!");

        Console.WriteLine($"  {move.San}: {string.Join(", ", info)}");
    }
}

// Example positions
await AnalyzeEndgame("8/8/8/8/8/5k2/4p3/4K3 w - - 0 1");  // KvKP
await AnalyzeEndgame("8/8/8/8/8/k7/8/KQ6 w - - 0 1");       // KQvK
await AnalyzeEndgame("8/8/8/4k3/8/8/8/4KR2 w - - 0 1");    // KRvK

Response Types

TablebaseResult

Property Type Description
Category string Position category (win, loss, draw, etc.)
Dtz int? Distance to zeroing (plies)
PreciseDtz int? Exact DTZ if available
Dtc int? Distance to conversion
Dtm int? Distance to mate
Dtw int? Distance to win (antichess)
Checkmate bool Is checkmate
Stalemate bool Is stalemate
VariantWin bool Is variant win
VariantLoss bool Is variant loss
InsufficientMaterial bool Insufficient material
Moves IReadOnlyList<TablebaseMove>? Legal moves, best first

TablebaseMove

Property Type Description
Uci string Move in UCI notation
San string Move in SAN notation
Category string Category after this move
Dtz int? DTZ after this move
PreciseDtz int? Exact DTZ after this move
Dtc int? DTC after this move
Dtm int? DTM after this move
Dtw int? DTW after this move
Zeroing bool Is zeroing move
Conversion bool Is conversion move
Checkmate bool Delivers checkmate
Stalemate bool Causes stalemate
VariantWin bool Is variant win
VariantLoss bool Is variant loss
InsufficientMaterial bool Leads to insufficient material

See Also

Clone this wiki locally