{"slug": "how-i-make-murder-mystery-puzzles-that-can-t-be-guessed-built-by-directing-ai", "title": "How I make murder-mystery puzzles that can't be guessed (built by directing AI)", "summary": "A bank operational-resilience analyst built Whodunnit, a free browser logic game, by directing AI coding tools, enforcing a single rule that every case must have exactly one culprit provable from visible clues. The generator runs server-side, validates each case with a player-style solver and a brute-force uniqueness check, and rejects any case with zero or multiple answers; harder levels add suspect statements where the murderer always lies. The builder reports that writing the rules into project instructions and demanding tests and real runs mattered more than prompting, and that security hardening came after launch and should have come first.", "body_md": "I work in operational resilience at a bank, writing crisis plans and asking \"what breaks if this fails?\". I'm not a software developer. Over the last couple of weeks I built Whodunnit, a free browser logic game, by directing AI coding tools, and I learned more from the rules I gave it than from any prompt.\n\nPlay it: [https://whodunnit-game.vercel.app](https://whodunnit-game.vercel.app) (the daily case and the first three cases work without signing up)\n\nThe one rule: exactly one provable culprit\n\nLogic-grid mysteries are fun until the last step needs a guess. So before any UI, I wrote down one non-negotiable rule:\n\nEvery case must have exactly one culprit you can prove from the clues the player can see, with no guessing.\n\nEverything else follows from that rule.\n\nHow a case is made\n\n```\n1.  Pick a hidden truth. The generator assigns each suspect a place, an item and so on. One of them is the culprit.\n2.  Emit clues until the answer is forced. Clues are things like \"the cook was not in the pantry\" or \"whoever had the rope was in the library.\"\n3.  Check it twice:\n• a player-style solver (deduce()) works through the clues step by step, the way a person filling in the notebook would. If it can't reach the answer without guessing, the case is rejected;\n• a brute-force check confirms there is only one consistent answer overall.\n4.  Trim clues that aren't needed, so the case stays tight.\n```\n\nIf either check finds zero answers, or more than one, the case is thrown away and a new one is generated. Players never see the rejects.\n\nHarder cases: the murderer always lies\n\nLater levels add statements from suspects, with one twist: the murderer always lies. The solver handles this by trying \"what if suspect X is lying?\" for each suspect and following each branch. A case only ships if exactly one branch survives without a contradiction. That gives you the satisfying \"only one person could be lying here\" moment, and it can still be proved.\n\nKeeping a leaderboard honest\n\nThere's a global leaderboard, so answers can't live in the browser, where anyone can open dev tools.\n\n```\n• The generator runs only on the server. The browser gets the clues, never the solution.\n• Case seeds are derived from a server-side secret, so a level number or a date can't be turned back into an answer.\n• Accusations, timing, hints and scores are decided on the server, and a restart never resets the clock.\n• Each level has a pool of 25 cases, so friends can't just pass answers around. The daily case is the same for everyone and gets harder through the week, from an easy Monday to a hard Sunday.\n```\n\nWhat \"directing AI\" actually meant\n\n```\n• Rules first, prompts second. The uniqueness rule, \"answers never reach the browser\" and \"decide scores on the server\" were written into the project instructions, so every change had to respect them.\n• \"Done\" needed proof. The AI sometimes reported a feature as finished when it wasn't, so I asked for tests and real runs, not summaries. Most of my time went on checking, not typing.\n• Security came after launch, and that's a lesson. After a review I hardened player data. Emails are stored only as a keyed hash, passwords use scrypt, breached passwords are refused, and sign-in routes are rate-limited. I'd do that from day one next time.\n```\n\nStack: TypeScript, React and Vite, SVG stick figures, Supabase Postgres, and Vercel functions. Vitest and Playwright are used for tests.\n\nTry it and break it\n\nIf you find a case that feels guessable, tell me the level. Under the rule above, that would be a bug, and I'd like to find it.", "url": "https://wpnews.pro/news/how-i-make-murder-mystery-puzzles-that-can-t-be-guessed-built-by-directing-ai", "canonical_source": "https://dev.to/ravi_aiworkstation_e3d96/how-i-make-murder-mystery-puzzles-that-cant-be-guessed-built-by-directing-ai-4gkc", "published_at": "2026-10-23 08:39:09+00:00", "updated_at": "2026-10-23 08:41:12.928756+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "developer-tools"], "entities": ["Whodunnit", "TypeScript", "React", "Vite", "Supabase", "Vercel", "Vitest", "Playwright"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/how-i-make-murder-mystery-puzzles-that-can-t-be-guessed-built-by-directing-ai", "markdown": "https://wpnews.pro/news/how-i-make-murder-mystery-puzzles-that-can-t-be-guessed-built-by-directing-ai.md", "text": "https://wpnews.pro/news/how-i-make-murder-mystery-puzzles-that-can-t-be-guessed-built-by-directing-ai.txt", "jsonld": "https://wpnews.pro/news/how-i-make-murder-mystery-puzzles-that-can-t-be-guessed-built-by-directing-ai.jsonld"}}