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 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
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.
Beyond Doubt
Como foi construído
Um quebra-cabeça diário de dedução com uma única regra: nunca se pede que você adivinhe. É uma frase fácil de imprimir na caixa e cara de cumprir.
Quase todo quebra-cabeça que se diz lógico acaba, mais cedo ou mais tarde, entregando um cara ou coroa e torcendo para você não perceber. Este não consegue. Toda casa que você tem permissão de virar já foi decidida pelas pistas que estão diante de você — e, se não foi, o tabuleiro recusa a jogada e explica por quê. Tudo o que vem abaixo é o preço dessa promessa.
Um tabuleiro é um número
Vinte casas, um inteiro
Um tabuleiro tem no máximo vinte casas, e cada casa está em um de dois estados. Essa imagem inteira cabe num único inteiro de 32 bits — um bit por casa. Cada pista se compila em duas ou três operações de bits sobre esse número, o que permite ao motor perguntar "isto continua verdadeiro?" milhões de vezes sem que ninguém perceba.
Isso importa por causa de como a promessa é mantida. Quando você toca numa casa, o motor percorre todos os arranjos do tabuleiro compatíveis com as pistas que você tem neste momento. Se todos concordam que esta casa está vendida, ela está vendida. Se um único discorda, nada a força ainda, e a jogada é recusada. Não existe tabela de respostas sendo consultada — a recusa é uma prova, calculada com o seu dedo ainda no vidro.
Nada no tabuleiro é mentira
Geração
Os tabuleiros são gerados de trás para frente. A solução é sorteada primeiro, e a partir daí o gerador só considera pistas que sejam verdadeiras para aquela solução — de modo que nenhuma pista pode, sem querer, eliminar a resposta. Desse conjunto ele monta uma corrente: um fato inicial, depois uma pista que destrava uma casa, depois a pista que aquela casa guardava, e assim por diante até o tabuleiro inteiro estar forçado.
Se a corrente não fecha, o tabuleiro é descartado e outro é sorteado. Nada é publicado sem ter sido resolvido antes pela máquina — é por isso que "existe sempre uma próxima jogada" é uma garantia, e não uma esperança.
Todo tabuleiro é também uma função pura da sua semente. Duas pessoas que abrem o quebra-cabeça do mesmo dia em lados opostos do mundo recebem o tabuleiro idêntico, gerado de forma independente em cada aparelho, sem nada trafegando entre elas.
O seu navegador nunca recebe a resposta
Jogo verificado no servidor
O jogo classificado tem uma exigência incômoda: o servidor precisa verificar cada jogada, mas o jogo precisa parecer instantâneo. Mandar a solução para o navegador resolveria o segundo problema e destruiria o primeiro.
O conjunto forçado é a resposta. Se todos os arranjos compatíveis com as suas pistas concordam sobre uma casa, essa concordância é a verdade sobre ela — então um navegador que só tem as pistas joga um tabuleiro inteiro corretamente sem jamais receber uma única resposta.
Assim o cliente deduz localmente e responde na hora, e o servidor rederiva a mesma coisa por conta própria e registra. O relógio pertence ao servidor. A decisão sobre que dia é hoje também — e é por isso que um tempo rápido não pode ser reivindicado por um jogador lento com as ferramentas de desenvolvedor abertas.
Um motor, sete cenários
Temas
Nada no motor sabe o que são quadros, convidados ou pomares. Um tema é um arquivo de dados — dois nomes de estado, um punhado de substantivos, uma paleta, uma função de desenho — e registrá-lo já traz de graça o gerador, o solucionador, as dicas, a pontuação, o arquivo, a edição semanal, o placar, a acessibilidade e os três idiomas.
Foi essa restrição que tornou barato ser estranho. Um leilão, um jantar em que alguém é culpado, um pomar doente, um filme cortado na ilha de edição, um quadro de avisos de bastidores, uma folha de serviço, uma placa de céu do século XIX — sempre o mesmo quebra-cabeça por baixo.
Três idiomas, e nenhuma frase traduzida entre eles
English · Português · Español
As pistas nunca são guardadas como texto. São estruturas — contar, esta coluna, este estado, exatamente duas — e a frase é composta no instante em que você a lê, no seu idioma, concordando com o substantivo que o tema usa.
Esta é a parte que não dá para fingir com substituição de texto. Árvore é feminino em português e masculino em espanhol; um convidado pode ser um ou outro, dependendo de quem seja. Então o jogo carrega a gramática em vez das frases, e a concordância é calculada: Nadia é culpada, Owen é culpado. Errar isso é invisível para quem lê um idioma só e irritante para todo o resto.
Os quadros são reais
Noite de Leilão
Vinte e uma das obras da Noite de Leilão são pinturas e fotografias da Baía Georgiana e das Blue Mountains, de James Portman. Não são enfeite pregado no fim — o tema foi reconstruído em torno delas. Uma galeria marca uma obra vendida com um pequeno ponto vermelho ao lado da etiqueta, e é isso que uma casa decidida ganha: uma marca que deixa a imagem em paz, em vez de um X riscado por cima dela.
O que acessibilidade quis dizer aqui
Derivada, não declarada
Modo escuro, modo claro, alto contraste e uma paleta segura para daltonismo são todos derivados das cores do próprio tema, em vez de desenhados à mão tema a tema — de modo que um tema novo não pode nascer com uma variante quebrada. As razões de contraste são verificadas na bateria de testes em toda combinação de tema e modo.
Duas falhas reais foram pegas assim e corrigidas na origem: um tema deixava o texto secundário em 2,84:1 sobre um fundo de cortiça, e o alto contraste não ajudava porque só escurecia a tinta e não mexia no fundo.
Como isso é verificado
Testes
A rodada de fuzz gera centenas de tabuleiros em todas as dificuldades e temas, resolve cada um sem adivinhar e renderiza cada pista nos três idiomas, procurando qualquer coisa que leia mal ou não possa ser terminada. A bateria inteira roda duas vezes — uma contra SQLite e outra contra um Postgres de verdade — porque os dois discordam de maneiras pequenas e caras. Uma consulta passava tranquila no SQLite e falhava no Postgres porque uma coluna que é booleana num é inteira no outro; nove testes ficaram vermelhos no instante em que encontraram o banco real.
Alguns dos melhores defeitos foram encontrados pelos testes, e não pelos jogadores: uma escada de dicas que nunca subia, de modo que dois toques devolviam a mesma pista e todo o orçamento de dicas podia ser gasto num único fato; e um painel de pistas que reconstruía o tabuleiro ao passar o mouse — inofensivo até as pistas irem para dentro das cartas e começarem a destruir justamente o elemento sob o ponteiro.
Feito em TypeScript, com o motor sem nenhuma dependência de execução. Publicado na Vercel com Postgres. Obras de James Portman.
Beyond Doubt
Cómo se construyó
Un rompecabezas diario de deducción con una sola regla: nunca se te pide adivinar. Es una frase fácil de imprimir en la caja y cara de cumplir.
Casi todos los rompecabezas que se llaman lógicos terminan, tarde o temprano, entregándote un cara o cruz y esperando que no te des cuenta. Este no puede. Cada casilla que se te permite voltear ya ha sido decidida por las pistas que tienes delante, y si no lo ha sido, el tablero rechaza la jugada y te explica por qué. Todo lo que sigue es lo que cuesta esa promesa.
Un tablero es un número
Veinte casillas, un entero
Un tablero tiene como mucho veinte casillas, y cada casilla está en uno de dos estados. Esa imagen completa cabe en un solo entero de 32 bits — un bit por casilla. Cada pista se compila en dos o tres operaciones de bits sobre ese número, lo que permite al motor preguntar "¿esto sigue siendo cierto?" millones de veces sin que nadie lo note.
Eso importa por cómo se mantiene la promesa. Cuando tocas una casilla, el motor recorre todas las disposiciones del tablero compatibles con las pistas que tienes en este momento. Si todas coinciden en que esta casilla está vendida, está vendida. Si una sola discrepa, todavía nada la obliga, y la jugada se rechaza. No hay ninguna tabla de respuestas que se consulte: el rechazo es una demostración, calculada con tu dedo aún sobre el cristal.
Nada en el tablero es mentira
Generación
Los tableros se generan al revés. Primero se sortea la solución, y a partir de ahí el generador solo considera pistas que sean verdaderas para esa solución, de modo que ninguna pista puede eliminar la respuesta sin querer. De ese conjunto arma una cadena: un hecho inicial, luego una pista que desbloquea una casilla, luego la pista que esa casilla guardaba, y así hasta que el tablero entero queda forzado.
Si la cadena no se cierra, el tablero se descarta y se sortea otro. No se publica nada que la máquina no haya resuelto antes, y por eso "siempre hay una jugada siguiente" es una garantía y no una esperanza.
Cada tablero es además una función pura de su semilla. Dos personas que abren el rompecabezas del mismo día en lados opuestos del mundo reciben el tablero idéntico, generado de forma independiente en cada dispositivo, sin que nada viaje entre ellas.
Tu navegador nunca recibe la respuesta
Juego verificado en el servidor
El juego clasificado tiene una exigencia incómoda: el servidor tiene que verificar cada jugada, pero el juego tiene que sentirse instantáneo. Mandar la solución al navegador resolvería el segundo problema y destruiría el primero.
El conjunto forzado es la respuesta. Si todas las disposiciones compatibles con tus pistas coinciden en una casilla, esa coincidencia es la verdad sobre ella: un navegador que solo tiene las pistas puede jugar un tablero entero correctamente sin que jamás se le diga una sola respuesta.
Así que el cliente deduce localmente y responde de inmediato, y el servidor vuelve a derivar lo mismo por su cuenta y lo registra. El reloj es del servidor. La decisión sobre qué día es hoy, también — y por eso un tiempo rápido no lo puede reclamar un jugador lento con las herramientas de desarrollo abiertas.
Un motor, siete escenarios
Temas
Nada en el motor sabe qué son los cuadros, los invitados o los huertos. Un tema es un archivo de datos — dos nombres de estado, un puñado de sustantivos, una paleta, una función de dibujo — y registrarlo trae gratis el generador, el solucionador, las pistas, la puntuación, el archivo, la edición semanal, la clasificación, la accesibilidad y los tres idiomas.
Esa restricción es lo que hizo barato ser raro. Una subasta, una cena en la que alguien es culpable, un huerto enfermo, una película cortada en el montaje, un tablón de anuncios de camerinos, una hoja de servicios, una placa de cielo del siglo XIX: siempre el mismo rompecabezas por debajo.
Tres idiomas, y ni una frase traducida entre ellos
English · Português · Español
Las pistas nunca se guardan como texto. Son estructuras — contar, esta columna, este estado, exactamente dos — y la frase se compone en el instante en que la lees, en tu idioma, concordando con el sustantivo que use el tema.
Esta es la parte que no se puede fingir con reemplazo de cadenas. Árbol es femenino en portugués y masculino en español; un invitado puede ser lo uno o lo otro, según quién sea. Así que el juego lleva la gramática en vez de las frases, y la concordancia se calcula: Nadia es culpable, Owen es culpable, pero Nadia está vendida y Owen está vendido. Equivocarse en esto es invisible para quien lee un solo idioma e irritante para todos los demás.
Los cuadros son reales
Noche de Subasta
Veintiuna de las obras de Noche de Subasta son pinturas y fotografías de la bahía Georgian y de las Blue Mountains, de James Portman. No son adorno pegado al final: el tema se rehízo alrededor de ellas. Una galería marca una obra vendida con un pequeño punto rojo junto a la cartela, y eso es lo que recibe una casilla decidida: una marca que deja la imagen en paz, en lugar de una cruz trazada encima.
Qué significó aquí la accesibilidad
Derivada, no declarada
El modo oscuro, el modo claro, el alto contraste y una paleta segura para el daltonismo están todos derivados de los colores del propio tema en lugar de dibujarse a mano tema por tema, de manera que un tema nuevo no puede salir con una variante rota. Las razones de contraste se comprueban en la batería de pruebas en cada combinación de tema y modo.
Así se detectaron dos fallos reales, corregidos en el origen: un tema dejaba el texto secundario en 2,84:1 sobre un fondo de corcho, y el alto contraste no ayudaba porque solo oscurecía la tinta y dejaba el fondo igual.
Cómo se comprueba
Pruebas
La ronda de fuzz genera cientos de tableros en todas las dificultades y temas, resuelve cada uno sin adivinar y renderiza cada pista en los tres idiomas, buscando cualquier cosa que se lea mal o no se pueda terminar. La batería entera se ejecuta dos veces — una contra SQLite y otra contra un Postgres de verdad — porque los dos discrepan de maneras pequeñas y caras. Una consulta pasaba tan tranquila en SQLite y fallaba en Postgres porque una columna que es booleana en uno es entera en el otro; nueve pruebas se pusieron en rojo en cuanto se encontraron con la base de datos real.
Algunos de los mejores errores los encontraron las pruebas y no los jugadores: una escalera de pistas que nunca subía, de modo que dos pulsaciones devolvían la misma pista y todo el presupuesto de pistas podía gastarse en un solo dato; y un panel de pistas que reconstruía el tablero al pasar el ratón, algo inofensivo hasta que las pistas se mudaron a las cartas y empezaron a destruir justo el elemento que estaba bajo el puntero.
Hecho en TypeScript, con el motor sin dependencias en tiempo de ejecución. Publicado en Vercel con Postgres. Obras de James Portman.