Inspiration

Most detective games have one fixed story: once it's solved, it's over. We wanted a mystery that is new every time, set in places we know (Sri Lankan weddings, temples, tea estates, train rides), built around the best moment in any detective story: catching a liar.

What it does

Whodunit is a detective game where every case is new. When you start a game, an AI Game Master invents a Sri Lankan mystery from scratch: the crime, the suspects, their secrets and lies, the hidden clues, and one culprit.

You question the suspects in a chat. Each one is an AI agent that stays in character and lies to protect its secret. Clues land in your notebook as witnesses let them slip. The best moment is when you show a clue that breaks a suspect's lie, and they crack. When you're ready, you accuse someone and see the true story, your score and your rank.

There are three difficulty levels:

  • Easy: 3 cooperative suspects, hints cost 50, 3 guesses, score ×0.8
  • Normal: 4 suspects, hints cost 100, 2 guesses, score ×1
  • Hard: 5 evasive suspects with 2 red herrings, 1 hint costing 150, 1 guess, score ×1.5

Our core rule: the AI proposes, Jac decides

The model never decides whether a clue is found, a lie is broken or the score changes. Plain Jac code checks every one of those against the graph, so the AI can't cheat the game. Secrets (the culprit, lies, unfound clues) never reach the browser before the game ends.

How we used Jac and Jaseci

  • Four AI agents with by llm(). Game Master, Suspect, Hint and Reveal are typed Jac functions whose bodies are by llm(). They return structured obj types (CaseSpec, SuspectOut, RevealOut) and are described to the model with sem strings. The model is Groq gpt-oss-120b through byLLM.
  • The graph is the game engine. Each game is a Case node attached to root, with Suspect, Clue and ChatLine nodes linked by typed edges: Knows (suspect → clue), Breaks (clue → suspect), HasSuspect, HasClue, HasChat and Said.
    • Clues: a clue counts as found only if it sits on that suspect's Knows edge.
    • Cracks: a lie breaks only if a Breaks edge links the shown clue to that suspect.
    • Persistence: Jac persists the graph automatically, so we wrote no database code.
  • Validation and a safety net. Every AI-generated case is checked in Jac before it's used: the right suspect count, exactly one culprit, every clue known by a witness, no liar holding their own breaking clue, and the required number of red herrings. If generation fails twice, a hand-written backup case loads.
  • API with def:pub. new_case, question, hint and accuse are public Jac functions that jac start serves as endpoints. They return only public views, never the culprit or the secrets.
  • Frontend in Jac. The whole UI (corkboard, suspect polaroids, interrogation pop-up, notebook) is Jac client code (.cl.jac components, compiled to React) that calls the backend with sv import.
  • Hosted on Jac Hammer. Built and run on Jac 0.34.23 and deployed to a Jac Hammer sandbox.
  • One language end to end. The agents, game rules, graph, API and UI are all written in Jac.

Challenges we ran into

  • Making the AI follow the rules. Early generated cases had clues nobody knew, or a liar holding their own proof. Strict Jac validation, a retry and a backup case fixed it.
  • Keeping the AI honest. Suspects must keep lying until the graph says they've cracked. We give each agent only the facts it's allowed to know.
  • A fast-moving language. We upgraded to Jac 0.34 mid-hackathon and learned its client-side quirks as we went.
  • Three people, one day. We split the work by layer, agreed the API contract first, and merged through pull requests.

What we learned

  • How to model a game as a graph of Jac nodes and typed edges.
  • How by llm() with typed outputs and sem gives structured, reliable AI results.
  • That AI works best when code checks its work.

Team Zenith

  • Sithil: frontend and deploy
  • Nithika: AI agents and demo
  • Yohan: graph engine and rules

Built With

  • byllm
  • github
  • gpt-oss-120b
  • graph-database
  • groq
  • jac
  • jac-client
  • jac-hammer
  • jaclang
  • jaseci
  • llm
  • react
  • tailwindcss
  • unsplash
  • vite
Share this project:

Updates

Submission history