UNO ARENA

In 2013, a first-year engineering assignment crowned a champion Uno bot — top of ~800 students.
Six AI challengers and fourteen million games later, it still hadn't lost.
Your turn.

Download the starter pack  Submit an entry

01The story so far

The bot is mgim006 — a few hundred lines of hand-tuned heuristics from ENGGEN 131, University of Auckland, 2013 — winner of the class tournament across roughly 800 first-year engineering entries. The metric is the official one from the assignment: cumulative points over millions of four-player games (winner takes the sum of the losers' hands).

Since then it has faced six increasingly sophisticated AI-written challengers — scored heuristics, card counting, tempo control, probabilistic modelling. Best honest result before this year: a tie. It took an evolutionary search — thirty tuned constants, half a billion simulated games of selection pressure — to finally produce a bot that beats it. Pure reasoning never did: a fresh Claude given only the spec, one attempt, no testing, lost by 1.8 points-share and finished dead last. Given three blind attempts, its best still lost by 1.1.

So that's the invitation. Write a bot — by hand, with your favourite model, however you like — and see if you can do what six challengers couldn't.

02The bar you're clearing

house · incumbent

mgim006

human, 2013
±0.00

The champion. Hand-calibrated to within one mutation of a local optimum, twelve years early.

house · champion

Ouroboros

evolved by self-play, 2026
+1.34

200 generations of eating its own tail. Never saw mgim006 until the verdict. Beat it anyway.

house · cautionary tale

Oneshot

Claude, best of 3 blind attempts
−1.11

Colour starvation with full-history card counting. Legal, clever, zero penalties in 40M games. Still lost — reasoning alone doesn't calibrate weights.

Numbers are points-share margin vs mgim006 in the main-event ring, 20M games, 24 seat permutations, ≥3σ decision rule (σ ≈ 0.017pp at 20M). Anything within ±0.05pp is a tie.

03How it works

  1. Download the pack. The exact referee, the tournament engine, three sparring bots, a legal starter bot, and the full rules — one gcc command and you're simulating ~75,000 games a second.
  2. Build your bot. One C file, 2013 student rules: no globals, no statics, no malloc, no extra includes. Entry point int youralias_Play(GameState *). The interface contract is documented in mystrat.c — and yes, the whole spec is written so you can paste it straight into an LLM.
  3. Test locally. The pack's README tells you the statistics you need: differences under 0.5pp need millions of games. Don't trust a 10,000-game hunch.
  4. Submit it. Your bot runs two official rings of 20,000,000 games each against the house. Results by reply, leaderboard below.
The two rings.
Rookie ring: you · mgim006 · naive · naive — can you beat the incumbent with weak players feeding the pot?
Main event: you · mgim006 · Ouroboros · Oneshot — the full table.

Beat mgim006 on points at ≥3σ in both rings: you've done what no hand-written bot ever has. Beat Ouroboros too: the top spot is yours.

04Submit

Paste your .c file into the body of an email (no attachment needed):

To: entries@seventh.rodeo
Subject: UNO ENTRY: <your alias>
Body: your complete .c file, pasted as plain text

Open a pre-addressed email

05Leaderboard

#botauthor rookie Δ vs mgim006main Δ vs mgim006verdict
1Ouroboros houseevolution +1.29+1.34beat mgim006
2mgim006 houseMark, 2013 incumbent
3Oneshot houseClaude, one shot −1.12−1.11fell short
?your botyou ?? 

Points-share difference vs mgim006, percentage points. Positive = ahead. Updated as entries are judged.

06The spec, for you or your model

Everything below is also in the pack's README. It is deliberately complete — paste it into your LLM of choice and it can write a legal bot first try. Whether that bot is any good is a different matter entirely.

RULES (exactly as the referee implements them)
- 4 players, 7 cards dealt. 108-card deck: per colour one 0, two each 1-9,
  two Skips, two Reverses, two Draw-Twos; plus 4 Wilds, 4 Wild-Draw-Fours.
- Flip until a non-wild tops the pile. Random first player and direction.
  The first flipped card's effect is NOT applied.
- You MUST play if able. Playable = wild (always) / colour match (top card's
  colour, or called colour if top is a wild) / NUMBER matching top NUMBER's
  value / special (Skip, Reverse, Draw-Two) matching top special's type.
- Skip: next misses a turn. Reverse: direction flips. Draw-Two: next draws 2,
  misses turn. Wild-Draw-Four: next draws 4, misses turn; playable anytime.
- Cannot play: return -1, draw one. Illegal return or wrongful -1 = PENALTY
  (draw 2, forfeit turn).
- Deck empty: reshuffle discards except top card.
- First to 0 cards wins immediately, scores the sum of the other three hands
  (numbers face value, specials 20, wilds 50). METRIC = cumulative points.

INTERFACE
  int youralias_Play(GameState *gameInfo);
- Return the 0-based index into yourCards of the card to play.
- Wild or Wild-Draw-Four: return colourConstant + index, e.g. GREEN + i.
  Colour constants: RED 10000, GREEN 20000, BLUE 30000, YELLOW 40000.
- Return -1 only when nothing is playable.
- GameState gives you: your hand, full discard pile, direction (LEFT means
  next player is (pos+3)%4, RIGHT means (pos+1)%4), everyone's card counts,
  every player's last called wild colour, cumulative scores.

CODE RULES (2013 student rules, enforced)
- One file, plain C89. No globals, no statics, no malloc, no extra includes,
  no I/O. All functions prefixed youralias_. Compiles with gcc -O2.