Khronologika
A magazine puzzle that's new every time. An MVP built in four days to test demand, with a generator that rejects grids you can't solve without guessing.
- Role
- Author
- Period
- 2026 — now
- Status
- Live
- Code
- Closed
The task
The magazine «Panorama Kross» runs a puzzle called «Khronologika»: a quote is hidden in a grid of letters, its letters never touch, and arrows at the edges say how many letters each line holds. I wanted to play it in the browser, and a new one every time.
The key requirement: the solution has to be not just unique but reachable by reasoning, with no guessing. The MVP took four days, to test demand before investing more.
What I did
- 01
A grid generator
Shape, letter slots, filler, hints and arrows. Any grid can be reproduced from its seed, and arrows are placed by bipartite matching (Kuhn’s algorithm).
- 02
A solver that thinks like a person
Six techniques people use on the magazine version. A grid goes live only if the solver finishes it, and difficulty is just the list of techniques.
- 03
CP-SAT as a check on the checker
At first CP-SAT from OR-Tools proved uniqueness, but the human-style solver couldn't finish any of the 64 grids it approved. Now CP-SAT independently re-checks every grid before saving, in 3–160 ms.
- 04
Quotes without straining the source
Only paths robots.txt allows, a couple of pages every ~10 minutes with backoff. Puzzles are built ahead of time, and collection stops at 3,000.
- 05
Stats to test demand
Visits and solved grids are counted per day, fully anonymous and cookie-free, with a daily report sent to Telegram.
- 06
The whole stack in four days
FastAPI and PostgreSQL, background generation on Taskiq, a React frontend. Images are built by GitHub Actions, and the site runs behind Traefik and nginx with HSTS.
Stack
- OR-Tools
- FastAPI
- PostgreSQL
- React
- Python
- TypeScript
- SQLAlchemy
- Alembic
- Dishka
- Taskiq
- Redis
- Tailwind
- pytest
- testcontainers
- mypy
- ruff
- Docker Compose
- Traefik
- nginx
- GitHub Actions