<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ravi AI Workstation</title>
    <description>The latest articles on DEV Community by Ravi AI Workstation (@ravi_aiworkstation_e3d96).</description>
    <link>https://dev.to/ravi_aiworkstation_e3d96</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4202080%2F7857d6ea-a193-4cf6-9350-587cfe02b3af.png</url>
      <title>DEV Community: Ravi AI Workstation</title>
      <link>https://dev.to/ravi_aiworkstation_e3d96</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ravi_aiworkstation_e3d96"/>
    <language>en</language>
    <item>
      <title>How I make murder-mystery puzzles that can't be guessed (built by directing AI)</title>
      <dc:creator>Ravi AI Workstation</dc:creator>
      <pubDate>Fri, 23 Oct 2026 08:39:09 +0000</pubDate>
      <link>https://dev.to/ravi_aiworkstation_e3d96/how-i-make-murder-mystery-puzzles-that-cant-be-guessed-built-by-directing-ai-4gkc</link>
      <guid>https://dev.to/ravi_aiworkstation_e3d96/how-i-make-murder-mystery-puzzles-that-cant-be-guessed-built-by-directing-ai-4gkc</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Play it: &lt;a href="https://whodunnit-game.vercel.app" rel="noopener noreferrer"&gt;https://whodunnit-game.vercel.app&lt;/a&gt; (the daily case and the first three cases work without signing up)&lt;/p&gt;

&lt;p&gt;The one rule: exactly one provable culprit&lt;/p&gt;

&lt;p&gt;Logic-grid mysteries are fun until the last step needs a guess. So before any UI, I wrote down one non-negotiable rule:&lt;/p&gt;

&lt;p&gt;Every case must have exactly one culprit you can prove from the clues the player can see, with no guessing.&lt;/p&gt;

&lt;p&gt;Everything else follows from that rule.&lt;/p&gt;

&lt;p&gt;How a case is made&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  Pick a hidden truth. The generator assigns each suspect a place, an item and so on. One of them is the culprit.
2.  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."
3.  Check it twice:
• 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;
• a brute-force check confirms there is only one consistent answer overall.
4.  Trim clues that aren't needed, so the case stays tight.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If 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.&lt;/p&gt;

&lt;p&gt;Harder cases: the murderer always lies&lt;/p&gt;

&lt;p&gt;Later 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.&lt;/p&gt;

&lt;p&gt;Keeping a leaderboard honest&lt;/p&gt;

&lt;p&gt;There's a global leaderboard, so answers can't live in the browser, where anyone can open dev tools.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;• The generator runs only on the server. The browser gets the clues, never the solution.
• Case seeds are derived from a server-side secret, so a level number or a date can't be turned back into an answer.
• Accusations, timing, hints and scores are decided on the server, and a restart never resets the clock.
• 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.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What "directing AI" actually meant&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;• 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.
• "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.
• 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.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Stack: TypeScript, React and Vite, SVG stick figures, Supabase Postgres, and Vercel functions. Vitest and Playwright are used for tests.&lt;/p&gt;

&lt;p&gt;Try it and break it&lt;/p&gt;

&lt;p&gt;If 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.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://whodunnit-game.vercel.app" rel="noopener noreferrer"&gt;https://whodunnit-game.vercel.app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>puzzle</category>
      <category>murder</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
