Public
LLM teams play Codenames Duet — clue/guess benchmark + cost
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

codenames-duet-bench

AI models play cooperative Codenames against the deck, in two modes:

  • classic (primary): ONE key. Model A is the spymaster (sees the key, gives clues); model B guesses blind with up to N+1 guesses per clue. 9 agents, 3 assassins, 9 clues max. Every ordered (giver, guesser) pairing is a cell in the standings matrix — including a model cluing for itself.
  • duet: faithful Codenames Duet — two overlapping keys, 15 shared agents, guesses judged on the giver's key, both partners clue.

Every clue, guess, prompted "thinking", native reasoning trace, and per-call cost is recorded.

Live UI: main.ts endpoint (standings → tap a team → tap a game → full replay with collapsed chain-of-thought). Wears the house style (quatro direction), importing pen.tsx/directions.ts from dcm31/house-style.

Rules implemented (faithful Duet)

  • 25 words from the official 400-word list; official Duet key card overlap (9 green / 3 assassin / 13 neutral per side; 3 shared greens; 15 unique agents; 1 double assassin; 1 green-for-you-assassin-for-them each way).
  • 9 total clue turns shared by the team. Givers alternate, except a giver with no remaining greens hands the pen over (Duet allows clues in any order).
  • Guesses judged against the giver's key. Green → keep guessing (or stop); neutral → turn ends, word marked against that key; assassin → instant loss.
  • Simplifications: no sudden-death phase; 8-guess safety cap per turn; a model that can't produce a valid clue/guess in 2 attempts forfeits that action.

Scoring

Guess Efficiency Score (GES), 0-100%: raw = successful guesses (0-9), plus a win bonus of (10 - clues used); GES = raw / 18. Wins always outrank losses (win floor 56% vs loss ceiling 44%); the perfect game — all 9 words on the first clue — is 100%. Leaderboard shows each model's average GES combined and split by role. Per-game data also keeps win/loss, words found, clues used, full transcript, and OpenRouter usage cost.

Architecture

Rendering mermaid diagram...

Running more games

  1. Edit runner/config.ts: pick SEED_MODE ("classic" or "duet"), SEED_KEYS (model keys), SEED_TIER (a fresh tier name — seeding is guarded per tier), GAMES_PER_PAIRING. Classic seeds ORDERED pairs (model_a gives, model_b guesses); duet seeds unordered pairs.
  2. Run runner/seed.ts. The cron drains the queue automatically (~1 game/2–4 min, 3 concurrent). runner/run-next.ts advances it manually.
  3. When done benchmarking, pause the worker: set runner/cron.ts back to a long interval or change its file type to script (it's a cheap no-op when idle, but still).

runner/validate-models.ts checks ids + pricing; runner/check-credits.ts shows key limits/balance; runner/test-engine.ts is the offline engine test.

Tiers

  • free:free OpenRouter models ($0): Nemotron 550B/120B/30B + GPT-OSS 20B.
  • cheap / premium — configured (Haiku 4.5, GPT-5 mini, Gemini 2.5 Flash, DeepSeek V3.1 / Sonnet 4.5, GPT-5.1) but need OpenRouter credits topped up.

Env: OPENROUTER_API_KEY.

Word list note: the 400 base words as shipped by open-source Codenames clones — lists of individual common words aren't subject to copyright; used here for a personal benchmark.