Repository
Alibi
A puzzle you play once a day. Several suspects each account for where they were, exactly one is lying, and you work out who.
A daily logic puzzle. One alibi. One lie. Find it.
Private repository. Everything below is written from the code; none of it is a link you can follow.
What it is
A once-a-day deduction puzzle in the Wordle shape — everyone gets the same puzzle, one attempt, a streak, something to share. The puzzle is a set of statements from suspects where exactly one is false.
Why it exists
The daily format solved a real problem: one shared puzzle a day creates conversation and removes the infinite-content treadmill. Most imitators copied the format and not the thing that makes it work — a puzzle that is the same difficulty for everyone, every day, forever. This is an attempt at that harder half for deduction, where difficulty is far less tractable than in a word game.
As a business
There is an upgrade page, so monetisation was designed — the economics of daily puzzles are well understood: free daily, subscription for the archive. It was not pursued. The category is brutal; the successful ones were bought by newspapers rather than grown independently, and building one to completion without launching is a defensible read of that.
Stack
- Client
- React · Vite · shadcn/ui · Tailwind
- Backend
- Supabase
- Build
- Bun
The decision
The problem
A daily puzzle must be the same difficulty every day, and difficulty is a property of how humans solve it rather than of the puzzle's structure. A deduction puzzle with identical suspect and clue counts can be trivial or impossible depending on how the constraints interact. Generate them randomly and the difficulty curve is noise — and one impossible Tuesday costs a chunk of the streak-holding audience permanently.
What I did
A pipeline instead of a generator. Puzzles are produced in batches, previewed, calibrated, tested against real playtesters, and only then scheduled onto specific dates. Difficulty is measured on humans and used to place a puzzle in the calendar, not asserted when it is generated.
What it costs
Operationally heavy. You cannot ship a day without inventory, which means staying permanently ahead of the calendar, and playtesting adds human latency to every batch. It also makes the product depend on a supply of playtesters — exactly the resource an unlaunched game does not have.
Where it ended
Built and not launched. Five hundred and thirty-two commits compressed into five days, to a genuinely complete product including the operational machinery most people skip. It still builds.
On screen


A piece of the code
supabase/functions/batch-generate/index.ts
const pickWeightedDifficulty = ( dist: { easy: number; medium: number; hard: number },): Difficulty => { const total = dist.easy + dist.medium + dist.hard; const roll = Math.random() * (total > 0 ? total : 1); if (roll < dist.easy) return "easy"; if (roll < dist.easy + dist.medium) return "medium"; return "hard";};Excerpt — trimmed for reading, not a full file.