A bug that hid for months by producing perfectly good puzzles.
Kludoku's expert cases are supposed to be the ones where nothing is anchored. Instead of "I was in the library", suspects tell you where they were relative to each other: north of somebody, sharing a room with somebody, three columns east of the Doctor. You have to build the whole cast into a chain and then find the one dull statement that pins the chain to the map.
They were not doing that. An expert board averaged 0.38 relational statements — which is to say, most expert boards had none at all, and the ones that did had one. The generator was producing them, testing them, and throwing nearly all of them away.
Every case Kludoku publishes is machine-verified before it ships: the solver is run against the board and the case is only accepted if it has exactly one solution reachable without guessing. That gate is the whole promise of the game, and it works — which is why nothing ever looked broken. A case that failed the gate was silently regenerated, and the player got a different, perfectly fair puzzle. The failure mode of this bug was a good puzzle you were not supposed to get.
What the gate was actually rejecting was any board that put a relational statement and an accomplice in the same case. And it rejected them for a reason that had been sitting in the solver since the accomplice was added.
Most testimony in Kludoku is about where: a room, a wall, a column, a half of the board. The solver represents those as a bitmask over squares, so making a suspect a liar is elegant — you invert the mask. "I was in the library" becomes every square that is not the library, in one operation.
Relational testimony has no mask. "I was north of the Curator" is not a set of squares; it is a relationship between two placements, and it cannot be evaluated at all until you know where the Curator is. So it was handled by a different code path — one that checked the relationship held, and, crucially, required it to hold for every suspect, including the liar. An accomplice who said anything relational made their own case unsolvable by construction. The gate did its job and threw the board away.
The obvious repair is to flip the check for the liar: where a truthful suspect's statement must hold, a liar's must fail. That is right, and it is not enough, because of what happens when one statement contains two claims.
"I was alone with the Colonel" says two things: that the speaker was alone with somebody, and that the somebody was the Colonel. When a truthful suspect says it, both must be true, and the solver can treat it as two separate requirements. When the liar says it, the statement is false as a whole — and a conjunction is false if either part fails. They might have been alone with the Doctor. They might have been in a crowd with the Colonel. Both make the sentence a lie.
The old code, having split the statement into parts for the truthful case, would have negated each part independently: not alone, and not with the Colonel. That is a much stronger claim than the liar actually made, and it eliminates squares the player has no right to eliminate. The case would have had exactly one solution according to the solver and two according to logic — the worst possible bug in a puzzle game, because it ships as a board that cannot be solved by reasoning correctly.
So statements that expand into several predicates are now grouped by the sentence they came from, and the liar's version is negated at the level of the sentence. The constraint propagator got the same treatment: a liar's multi-part statement no longer prunes candidates at all, because "at least one of these is false" tells you nothing about any particular one until the others are settled.
Expert cases went from 0.38 relational statements to 7.48. The distribution of solving difficulty did not move — the same seed produces a case graded the same as before — so this is not "expert got harder". Expert got to be the thing it was designed to be, and generation costs about 130 milliseconds more per board, which nobody will ever notice.
The part worth keeping is the shape of the bug. It never crashed, never produced a wrong answer, and never generated a complaint, because the safety gate caught every instance and quietly handed the player something else. A correctness check that is doing its job will hide a capability bug indefinitely — the only evidence was a number in a distribution nobody was looking at.
How to solve an expert case · the accomplice technique · Play free
Published · More from the devlog