Skip to main content

Tic-Tac-Toe Gauntlet

A terminal tic-tac-toe game that gets harder the more you win. Beat the random Rookie to unlock the rule-based Contender, then the unbeatable Minimax Champion. Every game and move is streamed into a local DuckDB database.

Built with Python + uv + Pydantic + Textual + DuckDB.

Quickstart

No clone, no database setup — the database is created on first run.

uvx ttt-gauntlet play        # try it without installing anything

Or install it:

pip install ttt-gauntlet
ttt-gauntlet play            # play the gauntlet (interactive TUI)
ttt-gauntlet stats           # see your record

(uvx ships with uv; pipx run ttt-gauntlet play works the same way.)

How it plays

  • You are X and always move first.
  • Win to advance to the next tier. Lose or draw to replay the tier.
  • Progress is remembered across sessions — ttt-gauntlet play resumes just past your best win. Force a tier with ttt-gauntlet play --level 3.
  • A live stats panel sits to the right of the board — record, current/best win streak, average moves per win, win rate per tier, and a trend of your recent results — refreshed after every game.

Controls: click a square (or navigate with the keyboard), N to continue after a game, Q to quit.

The difficulty ladder

Level Tier Engine
1 Rookie Random legal move
2 Contender Heuristic: win → block → center → corner → edge
3 Champion Minimax — optimal, cannot be beaten

Architecture

The rules engine and the AI are pure and UI-agnostic, so the same logic backs the game, the opponents, and the tests.

src/ttt/
  models.py        # Pydantic models (Board, GameEvent, …)
  engine/game.py   # pure tic-tac-toe rules (single source of truth)
  opponents/       # Opponent abstraction + random / heuristic / minimax + ladder
  data/db.py       # DuckDB connection + GameRepository
  tui/app.py       # Textual UI (thin: renders + dispatches only)
  cli.py           # Typer commands: play / stats

Data model

game_events is an append-only event stream — the source of truth for everything that happens (game_started / move_made / game_ended). The games and moves tables are projections written alongside it to keep common queries simple.

The database lives at $XDG_DATA_HOME/ttt/ttt.duckdb (or ~/.local/share/ttt/ttt.duckdb). Override with TTT_DB_PATH.

Roadmap

Planned features are written up as one-file specs — these are the units of work fed to the OpenHands PR automation.

Development

Requires uv.

git clone https://github.com/cmacdonald0514/tictactoe.git
cd tictactoe
uv sync                     # install dependencies
uv run ttt-gauntlet play    # run from the working tree
uv run pytest          # unit tests (engine, opponents, persistence)
uv run ruff check      # lint
uv run ruff format     # format

Releasing

Bump version in pyproject.toml and merge to master. The release workflow compares that version against the previous commit and publishes to PyPI via Trusted Publishing when it changes; merges that leave the version alone publish nothing.

Metadata

Release files for ttt-gauntlet 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ttt-gauntlet 0.2.0
File Size Uploaded Provenance
ttt_gauntlet-0.2.0.tar.gz 35.8 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for ttt-gauntlet 0.2.0
File Interpreter ABI Platform
ttt_gauntlet-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 53.9 kB

Release files / ttt_gauntlet-0.2.0.tar.gz

Download URL ttt_gauntlet-0.2.0.tar.gz
Size 35.8 kB
Tags Source
SHA-256 checksum
How to use checksums
a870efedc84d6a83f631306a5ae82f3e32f72c0385ef7389b0ec9886a591b5a5
BLAKE2b-256 checksum
How to use checksums
d9db4eced8590a981cc66c99430dc4d6260f897c3392a0621bc78d358f00985c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.

Transparency log

Release files / ttt_gauntlet-0.2.0-py3-none-any.whl

Download URL ttt_gauntlet-0.2.0-py3-none-any.whl
Size 18.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
84086aa4f2c78b3776ba7352078955616b792769523efed5cf2ea4ec37a4dfec
BLAKE2b-256 checksum
How to use checksums
008411e92230ec6e2a9d9bccba566ec339a50a38d5ea30fad96e7c52a7149c0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.

Transparency log

Security for ttt-gauntlet 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ttt_gauntlet-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page