-
-
Pick Easy, Normal or Hard and open a new case. The AI writes a fresh Sri Lankan mystery every time.
-
A live AI-generated case on the detective's board: case file, suspect photos chosen by role, and your notebook of clues.
-
Click a suspect to interrogate them. Starter questions help you begin, and each question costs 50 points.
-
Suspects answer in character with live AI. Catch a lie, show the right clue, and watch them crack.
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 areby llm(). They return structuredobjtypes (CaseSpec,SuspectOut,RevealOut) and are described to the model withsemstrings. The model is Groqgpt-oss-120bthrough byLLM. - The graph is the game engine. Each game is a
Casenode attached toroot, withSuspect,ClueandChatLinenodes linked by typed edges:Knows(suspect → clue),Breaks(clue → suspect),HasSuspect,HasClue,HasChatandSaid.- Clues: a clue counts as found only if it sits on that suspect's
Knowsedge. - Cracks: a lie breaks only if a
Breaksedge links the shown clue to that suspect. - Persistence: Jac persists the graph automatically, so we wrote no database code.
- Clues: a clue counts as found only if it sits on that suspect's
- 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,hintandaccuseare public Jac functions thatjac startserves 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.jaccomponents, compiled to React) that calls the backend withsv 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 andsemgives 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
Log in or sign up for Devpost to join the conversation.