An open API service indexing awesome lists of open source software.

https://github.com/niklasf/lichess-puzzle-tb-check

Verify Lichess puzzles against tablebase data
https://github.com/niklasf/lichess-puzzle-tb-check

chess lichess syzygy tablebases

Last synced: 10 days ago
JSON representation

Verify Lichess puzzles against tablebase data

Awesome Lists containing this project

README

          

# Lichess puzzle-tb-check

Verify Lichess puzzles against tablebase data.

Reads a [Lichess puzzle database](https://database.lichess.org/#puzzles) CSV,
looks at the positions that have tablebase coverage, probes the
[lila-tablebase](https://github.com/lichess-org/lila-tablebase) HTTP API, and
records — per puzzle — whether every puzzler move is the **unique winning move**
(or the **unique drawing move** for `equality` puzzles), plus a DTM check for
`mateInX` puzzles.

## Requirements

Python 3.14+ (uses the stdlib `compression.zstd` module), `uv`.

## Usage

Against public API (slow):

```sh
uv run puzzle-tb-check lichess_db_puzzle.csv.zst --out report.csv
```

Against a local instance:

```sh
uv run puzzle-tb-check lichess_db_puzzle.csv.zst --out report.csv --endpoint http://127.0.0.1:9000 --max-rps 0 --concurrency 120
```

The input may be a plain `.csv` or a `.csv.zst`.

Verification runs are **resumable**: any `PuzzleId` already present in `--out`
is skipped. So interrupt with Ctrl-C or check new puzzles from an updated
database at any time.

## Output

The `--out` CSV has columns `PuzzleId,PGN,CliCommand`. The latter are both empty
for valid puzzles.

Example excerpt:

```
0ddFb,,
0d6GM,,
0bGDG,[FEN "6rk/7p/2p1r3/5P1R/5Q1K/3B4/2P3qP/q7 b - - 2 37"] 37... Qe5 38. Rxh7+ Kxh7 39. fxe6+ Rg6 40. Bxg6+ Qxg6 41. Qxe5 Qxc2 42. e7 { NOT_UNIQUE:loss@9 },puzzle issue 0bGDG puzzle-tb-check:208fca81:NOT_UNIQUE:loss@9
0cVwQ,,
```

Extract `CliCommand` column:

```sh
tail -n +2 report.csv | cut -d, -f3 | grep .
```

Sample some random rejected puzzles as PGN for studying on Lichess:

```sh
tail -n +2 report.csv | awk -F, '$2 != ""' | shuf -n 64 | awk -F, '{print "[Site \"https://lichess.org/training/" $1 "\"]" $2 " *\n"}'
```

## Rejection reasons

| Code | Meaning |
|---|---|
| `NOT_WINNING:` | the played move is known not to win |
| `WIN_FRUSTRATED:` | the played move is only a frustrated win (`blessed-loss`) |
| `NOT_UNIQUE:` | a different move also wins/holds |
| `WRONG_MOVE:` | a different move wins/holds while the played is not known to |
| `EQUALITY_HAS_WIN:` | an `equality` puzzle where a winning move exists |
| `EQUALITY_NOT_DRAW:` | an `equality` puzzle whose played move does not draw |
| `DTM_MISMATCH:` | a `mateInX` puzzle whose DTM does not match the expected mate distance (exact for mateIn1–4; a lower bound for mateIn5 = "5 or more") |

## Tablebase coverage

A position is *verifiable* if it is directly covered (≤7 pieces via Syzygy, or an
8-piece **op1** position) or is a ≤9-piece boundary position with a capture into
covered territory (9→8-op1, or 8→7). The verifier checks every verifiable
puzzler-to-move position and ignores the rest; a puzzle with no verifiable
position is skipped.

## Unconditional vs. frustrated wins, the 50-move rule, and unknowns

`maybe-win`/`syzygy-win` (and their losing variants) are treated as
**unconditional** wins and losses, respectively.

`cursed-win`/`blessed-loss` are **frustrated** wins/losses under the 50-move rule.
We would reject a supposedly winning puzzle if the played move is a frustrated win.

For ambiguity, generally a frustrated win would prevent an unconditional win
from being considered unique. A puzzle with a winning move and a frustrated win
would not be rejected.

However, before the first capture, the puzzler can't
know the 50-move counter, so we conservatively reject puzzles with
frustrated alternative moves to avoid confusion.

Rejection is otherwise **evidence-based**: only *positive known* tablebase facts
reject a puzzle. An `unknown` move itself is not enough to reject a puzzle —
but incomplete information can still suffice (two known winning moves prove non-uniqueness
regardless of unknown moves). The precise category `` is always recorded in each
rejection reason.

## Generative AI usage

Considerable portions of this program were generated by AI.

## License

GPL-3.0-or-later. See [LICENSE](LICENSE).