Building an Ace Attorney trial with no script
I built a fan prototype of Ace Attorney where you defend Larry Butz by typing whatever a lawyer would say, and the witness, the prosecutor and the judge answer live. This is how I designed it, what it took to keep a live AI fair and consistent for a whole trial, and what I learned.
What I built
- An AI layer for an existing fan-made Ren'Py engine: about 4,300 lines of Python, tests included.
- The case as data: crime facts, exhibits, and what each character privately knows, including secrets a witness only gives up under the right questions.
- A turn engine that decides who speaks next, keeps a ledger of the points before the court, and tracks each character's mood with a reason.
- Validation for every model reply: structured output that game code checks against the case, with a clean rollback when a turn fails.
- A second model that reviews each turn for continuity before it goes into the record.
- The player-facing UI: interview and court panels, a numbered record you can cite from, a verdict screen and a character studio for importing your own defendant.
- Testing: 43 Python tests, 13 in-engine tests, trial export, and a harness that replays saved trials through the live model.
Skills it shows
Why I built it
Courtroom games are puzzles: the testimony is written in advance, and you win by pressing the right line and presenting the right exhibit at the right moment. Say anything else and you lose a life. I wanted to find out if I could build the opposite: ask whatever you think will help your client, the way a lawyer would, and have the court answer what you actually said.
Getting a model to write one courtroom line is easy. The real problem is keeping a trial fair and consistent for a hundred turns, with a judge whose ruling follows the record. So I scoped it to one case, The Gatewater Blackout: an art dealer is shot in Room 704 of the Gatewater Hotel during a blackout, your client Larry Butz left a fingerprint on the balcony rail, and a photographer says she saw him climb down.
Designing the player's side
I kept the series' shape, a private interview with your client and then court, but replaced its fixed menus with five open actions. I also cut the penalty meter and the timer: a weak question costs nothing but a weaker case.
- Speak
- Type a question, a challenge, an objection or an argument. The court works out who you're talking to and who should answer.
- Present evidence
- Pick an exhibit and say what you want to ask or argue with it.
- Cite
- Quote an exact line from the record. Quoting a private conversation is your claim as counsel, not sworn testimony.
- Let it continue
- Hand the floor to the court: the prosecutor questions the witness, the judge rules or invites closing. You can step in whenever a question is pending.
- Close
- Argue your case, or let the judge decide on the record as it stands.
Writing a witness with secrets
I wrote the case as data, not as a script. Lotta's file holds four things she won't volunteer: the police showed her Larry's photo before she named him, she left her post for four minutes to move her van off a fire lane, a magazine pays more for a story with a named suspect, and her camera has a 26-minute gap right across the shooting. I never scripted when, or whether, any of it comes out. In the trial below, two came out because of how I questioned her. The other two were never found.
The hard part: keeping a live AI consistent
I split the work so the model only ever does what it's good at, and code holds it to the case:
- Fixed
- The facts of the crime, the exhibits, and what each character actually knows. My code checks that every turn only refers to evidence and facts that exist.
- Written live
- Every line of dialogue, what a witness chooses to disclose, the prosecution's arguments, the judge's rulings and the verdict.
- Checked
- A separate model reviews each proposed turn for continuity before it's committed: an admission can't be forgotten two turns later, and a private conversation can't leak into court unless you bring it up there. A rejected turn is retried without losing your place.
- Remembered
- The court keeps a ledger of the points before it, each open, disputed, conceded or resolved, with references to the record, so a concession still counts when the next person speaks.
A trial I played
Lines from one of my test trials, in order, numbered from the record. My lines are exactly as I typed them, typos included; everything else was written live. It shows the pieces working together: a client who lies until he's pressed, an exhibit that breaks a witness's story, a citation, a secret coming out, and a ruling that follows the record.
In private
- Phoenixso what happend?
- LarryNick! You gotta believe me, I didn't shoot anybody! I wouldn't even know how to fire a gun without dropping it on my foot!
- Phoenixthen how do you explain your finger prints in the balcony?
- LarryW-Wait, they found my prints out there?! How did they even… I mean, balcony?! What balcony?!
- Phoenixyou're lying to me aren't you? your finger prints are on the balcony, explain so i can help you
- LarryI did step out onto the balcony of Room 704 and touch the railing... but it's not what it looks like!
- LarryLook, I even kept the signed receipt from when he took the package! Here, take it!
- RecordLarry hands over the Delivery Receipt.
In court
- LottaRight after, I saw someone clambering down from Room 704's balcony. It was that fella Larry Butz, plain as day!
- LottaThat fella pulled out his cell phone, and the screen lit him right up in the dark!
- RecordPhoenix presents the Power Co. Report: the whole block, street lights included, was dark from 9:40 to 10:00 PM.
- Phoenixstreet lights were out, how could you be sure it was him?
- FranziskaYou foolishly repeat what we already know, Phoenix Wright; the blackout did not extinguish the battery of a handheld phone.
- LottaI couldn't rightly make out a clear face or his exact build from all the way down there.
- RecordPhoenix cites record #46.
- Phoenixis there anything else that would make you identiy the fact the shooter was larry?
- LottaWell... the police did come show me a photograph of that fella Larry the next mornin'.
- LottaAfter seein' his picture, I just figured he had to be the one, even if I couldn't truly tell from the silhouette alone.
- JudgeA lack of an alibi does not substitute for affirmative proof. The prosecution's remaining link is the undated railing print alongside the 8:30 PM delivery.
- JudgeThe State has failed to prove the defendant's guilt beyond a reasonable doubt, and I hereby find the accused, Larry Butz, not guilty.
From the 113-entry record. I played the first 99 entries; for the last few, my test harness picked up the saved trial, asked the judge for a ruling and let the court finish.
What I learned
- Facts in code, words from the model
- The model never decides what happened, only what people say about it. Game code rejects any turn that mentions a fact or exhibit the case doesn't have, which is what keeps a live trial fair to the player.
- A second model is a cheap editor
- Checking each turn for continuity before it's committed stops forgotten admissions and leaked private talk before the player sees them. A rejected turn just runs again.
- Watch the thinking budget
- Early live runs came back cut off because the model's reasoning used up the output budget. Lowering the reasoning effort and leaving room for the answer fixed it.
- Test against the real model too
- Mocked tests prove the plumbing. Replaying a saved trial through the live model is what showed whether the judge rules sensibly.
What I'd do next
An original case and cast, more than one witness, places to investigate before the trial, and a turn well under the current seven seconds, using the same write-ahead tricks I used to give Façade's characters a live voice.