← Back to the game

Beyond Doubt

How it was built

A daily deduction puzzle with one rule: you are never asked to guess. That sentence is easy to write on a box and expensive to keep.

Most puzzles that call themselves logic puzzles will, sooner or later, hand you a fifty-fifty and hope you don't notice. This one can't. Every square you are allowed to turn over has already been decided by the clues in front of you, and if it hasn't, the board refuses the move and tells you why. Everything below is what that promise costs.

A board is a number

Twenty squares, one integer

A board is at most twenty squares, and each square is one of two states. That whole picture fits in a single 32-bit integer — one bit per square. Every clue compiles down to two or three bit operations against that number, which means the engine can ask "is this still true?" millions of times without anyone noticing.

That matters because of how the promise is kept. When you tap a square, the engine walks every arrangement of the board consistent with the clues you currently hold. If all of them agree that this square is sold, it's sold. If even one disagrees, nothing forces it yet, and the move is refused. There is no table of answers being consulted — the refusal is a proof, computed while your finger is still on the glass.

Nothing on the board is ever a lie

Generation

Boards are generated backwards. The solution is drawn first, and then the generator only ever considers clues that are true of that solution — so a clue can never be added that quietly eliminates the answer. From that pool it builds a chain: an opening fact, then a clue that unlocks a square, then the clue that square was holding, and onward until the whole board is forced.

If the chain can't be completed, the board is thrown away and another is drawn. Nothing ships that hasn't been solved by the machine first, which is why "there is always a next move" is a guarantee rather than a hope.

Every board is also a pure function of its seed. Two people opening the same day's puzzle on opposite sides of the world are handed the identical board, generated independently on each device, with nothing passed between them.

Your browser is never sent the answer

Server-verified play

Ranked play has an awkward requirement: the server has to verify every move, but the game has to feel instant. Sending the solution to the browser would solve the second problem and destroy the first.

The trick

The forced set is the answer. If every arrangement consistent with your clues agrees on a square, that agreement is the truth about it — so a browser holding only the clues can play a full board correctly while never being told a single answer.

So the client deduces locally and responds immediately, and the server independently re-derives the same thing and records it. The clock belongs to the server. So does the decision about which day it is, which is why a fast time can't be claimed by a slow player with the developer tools open.

One engine, seven settings

Themes

Nothing in the engine knows about paintings, or guests, or orchards. A theme is a data file — two state names, a handful of nouns, a palette, one drawing function — and registering it gets the generator, the solver, hints, scoring, the archive, the weekly edition, the leaderboard, accessibility and all three languages for free.

That constraint is what made it cheap to be strange. An auction, a supper party where someone is guilty, a diseased orchard, a film cut in the edit, a stage-door callboard, a service record, a plate of nineteenth-century sky — same puzzle underneath, every time.

Three languages, and not a translated sentence among them

English · Português · Español

Clues are never stored as text. They are structures — count, this column, this state, exactly two — and the sentence is composed at the moment you read it, in your language, agreeing with the noun the theme happens to use.

This is the part that can't be faked with string replacement. A tree is feminine in Portuguese and masculine in Spanish; a guest can be either, depending who they are. So the game carries the grammar rather than the sentences, and agreement is computed: Nadia é culpada, Owen é culpado. Getting this wrong is invisible to a monolingual reader and grating to everyone else.

The pictures are real

Auction Night

Twenty-one of the works in Auction Night are paintings and photographs of Georgian Bay and the Blue Mountains by James Portman. They aren't decoration bolted on at the end — the theme was reframed around them. A gallery marks a sold work with a small red dot beside the label, so that is what a decided square gets: a mark that leaves the picture alone, rather than a cross drawn through it.

What accessibility actually meant here

Derived, not declared

Dark mode, light mode, high contrast and a colour-blind-safe palette are all derived from each theme's own colours rather than hand-drawn per theme — so a new theme cannot ship with a broken variant. Contrast ratios are asserted in the test suite across every theme and mode combination.

Two genuine failures were caught that way and fixed at the source: one theme put its secondary text at 2.84:1 on a cork background, and high contrast wasn't helping because it only darkened the ink and left the background alone.

How it's checked

Testing

79tests, on SQLite
17end-to-end, in a browser
294boards fuzzed per run
83msworst generate-and-solve

The fuzz run generates hundreds of boards across every difficulty and theme, solves each one without guessing, and renders every clue in all three languages, looking for anything that reads badly or can't be finished. The whole suite runs twice — once against SQLite and once against a real Postgres — because the two disagree in small, expensive ways. One query passed happily on SQLite and failed on Postgres because a column that is a boolean in one is an integer in the other; nine tests went red the moment they met the real database.

Some of the better bugs were found by the tests rather than by players: a hint ladder that never escalated, so two presses returned the same clue and the whole hint budget could be spent on one fact; and a clue panel that rebuilt the board on hover, which was harmless until the clues moved onto the cards and started destroying the very element under the pointer.

Built in TypeScript with no runtime dependencies in the engine. Deployed on Vercel with Postgres. Artwork by James Portman.

Play Architecture Privacy Terms