with supplemental terms.
The seam between the two halves 🧵
The whole game balances on one boundary: Gemini interprets the player's words, but the engine never trusts blindly. The ; the config's own comment says raise the floors, never lower them.
Every push runs format, lint, typecheck, the unit suite, a Playwright e2e pass, SonarQube, and CodeQL.
The secret never leaves the server—not in a response, the client bundle, or the public board seed—and tests assert it, so a leak fails CI before it ships. The Gemini key is server-side only, in Secret Manager.
A kids' game should collect nothing, so this one doesn't: a session is one httpOnly cookie holding an opaque UUID—no accounts, no user data, nothing durable.
How I Built It
My first wolf was too good 🐺
Truth? My first version of Sköll was too good. Left alone, gemini-3.5-flash plays the board like a solver—it opens on the cleanest split, never forgets an elimination, and closes the round before a kid has found their footing—so the early games were just the wolf winning, fast and joyless. The hard part was never making him smart enough to win; it was making him lose like a person.
The fix wasn't a better model but a worse one on purpose. The deterministic floor—a seeded, hunch-weighted fallback that loses like a kid with no model at all—was the basis the wolf grew out of through v1; v2 is where the gemini-3.1-flash-lite brain finally gave him his character.
The engine owns the board ⚖️
The first rule I set, and never moved:
Gemini decides. The engine referees.
I made the engine own the board, the secret, whose turn it is, what's legal, and the win check. The secret surfaces exactly once—on a winning Cast—so everything Gemini touches is intent rather than fact. Even the shuffle is paranoid: the board's display order comes from its own public seed, separate from the secret's—linked seeds would let the layout leak the answer.
Here's the exact moment I set it, in an early planning chat with Claude:
—from an earned-only state: the public board, his own truthful answers, his own crossed-off sheet. The payload builder takes his state, never the engine's, so the secret is structurally unreachable. Reining him in came down to two levers: the lite model and a low thinking budget set the pace, and his
The voice I built twice 🪤
My first voice was a single Gemini Live session that owned everything at once—your words, the reading of them, the audio, the turn state—so the feature blinked out the moment the mic closed, and Sköll, who wasn't in that session, had nowhere to speak. That's structural, not a tuning problem: a turn-based game doesn't want a real-time session that owns the conversation, it wants every line composed once and spoken on demand. So Live came out.
Now one server-side gemini-3.5-flash interpreter reads every Ask, typed or spoken, into a single engine action. Speaking is a separate, lighter seam: every voiced line is written to the panel and, when audio is on, spoken through one tracks the whole migration.
That's : one deterministic round, a wolf who can't cheat, and an Oracle you can talk to. The round and the wolf are deployed and thoroughly tested; the voice is the half still in motion.
Prize Category
Best Google AI Usage 🪙
The interesting Gemini work here is backwards from the usual goal. I didn't need a model that wins—I needed one that loses like a kid, never cheats, and understands plain language. That broke into two problems.
Beatable, can't cheat 🔒
The difficulty dial isn't a setting—it's the model tier, split by job. The Oracle reads on full gemini-3.5-flash because a weaker parser misreads the gnarly cases; Sköll plays on gemini-3.1-flash-lite because full Flash solved the board in about five turns and shrugged off the persona. The engine referees both, re-checking everything either says and handing each the board in fixed order so they reason instead of compute. The wolf's budget is turned down so a twelve-year-old can actually beat him.
Each lever is named in the @google/genai SDK:
responseSchema is constrained JSON so neither role can speak outside the engine's vocabulary
- the model tier itself—
gemini-3.1-flash-lite for Sköll, full gemini-3.5-flash for the Oracle—is the difficulty dial
thinkingLevel tunes each seam—MINIMAL for the Oracle's read, LOW for the wolf's move: enough to track his sheet, never enough to solve the board
- two opposed
systemInstructions—the seer and the wolf
The Oracle side is scored against a his wins sprawl from a lucky three-turn blowout to a stubborn eleven-turn slog, and roughly a third ride an early lucky read—the tell a kid leaves and a solver never does. The deterministic floor reproduces the same sprawl with no API key at all, and the reading runs at temperature: 0, because interpretation should never be creative.
const response = await ai().models.generateContent({
model: 'gemini-3.5-flash',
contents: question,
config: {
systemInstruction: SYSTEM_INSTRUCTION,
responseMimeType: 'application/json',
responseSchema: RESPONSE_SCHEMA,
thinkingConfig: { thinkingLevel: ThinkingLevel.MINIMAL },
temperature: 0
}
});
Talk to it 🗣️
There's no query language to learn, because Gemini is the query language: you ask in your own words and it reads them into something the engine can answer. For a kid, that's the difference between a game and a homework assignment.
Voice is the same idea one step further: a spoken Ask runs the same pipeline a typed one takes, answered aloud in the Gacrux voice. Only audio leaves the browser, never the key—so the voice layer can fail without taking the game down.
Best Ode to Alan Turing 🤖
I didn't set out to reference Turing—I backed into it. To keep the secret rune away from Gemini, I split the game into a deterministic engine that decides everything it can, and an Oracle the engine asks only for the one thing it can't work out on its own: what a loose human sentence actually means. Then I really looked at my diagram and realized I'd drawn , running with a wolf in it.
And the mechanics earn it on their own. Strip the myth and the loop is deduction—code-breaking with better art: twenty-four candidates, one hidden answer, cracked by yes/no probes that each cut the field. It's an algorithm a kid runs by hand, against an AI running its own across the table, with a third model reading human intent in between. Algorithms, code-breaking, machine intelligence—Turing's whole estate, folded into a kids' game.
But the nod I'm proudest of isn't the mechanics or the myth—it's the architecture, and the fact that it was an accident is my favorite part.
Judge Validation 🧭
Each criterion, and the thing that earns it:
- ✅ Relevance to theme — the myth isn't paint, it's the rule set: the whole contest is Sól's rune against Sköll's jaws on solstice eve, and winning is the sunrise.
- ✅ Creativity — most game AI is tuned to win; this one is tuned to be beatable—and the structure keeping it honest turned out to be a 1939 Turing construction.
- ✅ Technical execution — fairness is enforced, not promised: coverage floors that fail the build on a single missed branch, the answer provably never leaving the server, full no-mouse play, every check public.
- ✅ Prize categories — two entries, one proof: Best Google AI Usage and Best Ode to Alan Turing, both laid bare in
/debug, where every line is tagged to its author—model or machine.
- ✅ Writing quality — not mine to grade; that's your call. I'll only say I wrote it to be fact-checked, not believed—nearly every claim here hangs off a link to the code or
/debug, so you never have to take my word for any of it.
The Light Is Yours to Keep 🪶
A bad deduction game feels like filling in a spreadsheet. All the ritual—the wolf, the rune, the one short night—is there to make the math feel like it matters. And the math is honest: a deterministic engine owns every fact, Gemini only ever the voice, and /debug proves it line by line. Name the true rune before dawn and Sól outruns Sköll for one more year—and the sun rises on the solstice, the longest day.
Run this footer through the debug view and it comes back badged gold—inference, not engine truth—because Claude and Codex wrote most of the code, argued the architecture, and tightened every paragraph, including this one. The calls are mine: the wolf, the worse-on-purpose model, every decision you'd argue with. Catch a mistake? Say it plainly—that's how the Oracle takes questions anyway.
SOCIAL SHARE CARD GENERATOR