← back to work

case study

Yapoleon's Court

An AI game the LLM can't game.

visit site →court.yapoleon.com

The authority boundary behind all of this work is stated once, in the model cannot own the money. This page covers what is specific to Yapoleon's Court.

Yapoleon's Court gameplay screen showing the AI opponent's in-voice reply beside a favor meter the model cannot write to.

01 the problem

A competitive game backed by an LLM the player is actively trying to game — running on a paid API, in front of minors — is a stack of production hazards. The model can be prompt-injected. It can be flattered into a high score. A bad actor can burn through your API bill. And the model can say things a family audience shouldn't hear.

02 the solution

Built the game so the model *can't* be gamed — structurally, not with hope. The Gemini call returns taste only (five bounded axes plus one in-voice line). The favor delta is derived server-side by a pure function the model can't see or reach. Prompt injection, sycophancy-gaming, and denial-of-wallet are handled at the architecture layer. A deterministic red-line pre-filter runs before any model call, so flagged input costs zero.

03 decisions & tradeoffs

Model returns taste, server derives the score

Making the favor delta a field the LLM can output makes it a field the LLM can be talked into inflating. Removing the field entirely is stronger than any prompt hardening.

Structured, low-temperature output only

Predictable schema across every turn, cheaper to reason about, easier to write invariant tests against. Voice comes from prompt, not temperature.

Deterministic pre-filter before model call

Red-line content should never spend a token. Rules-based short-circuit is faster, cheaper, and auditable in a way an LLM classifier isn't.

Cached degrade mode over hard failure under load

Concurrency damper serves an in-voice cached reaction with zero model calls when spend or load spikes. Still routes a neutral score through the same derive-favor function. Meter never breaks, character never breaks.

04 tech stack

Google Gemini

Server-side proxy, low-temperature structured taste-only output

React + TypeScript

Web app, share-card generator, favor meter UI

Vercel serverless

API routes with atomic-slot concurrency damper (TOCTOU-safe)

Supabase

Auth, standing and rivalry persistence across days

05 results

  • Judge calibrated against 30 demands × weak/mid/strong: 96.7% strong, 72.2% mid, 0% weak; a fixed rhetorical mold wins 0% of off-axis days; 1,095 calls, 0 errors
  • Denial-of-wallet bounded five ways: 3-turn daily cap, per-IP + per-user rate limits, atomic concurrency slot, cached degrade mode, Vercel spend hard-stop
  • Content pre-filter short-circuits red-line input before any model call — zero cost on flagged requests
  • Prompt injection handled structurally (player reply rides as data being judged, never in the system instruction)

Next step

Want something like this?

Book a 30-minute call. I will tell you plainly whether I can help and what I would test first.