Skip to content

Commit 3d6e930

Browse files
committed
Add arena creation skill
1 parent 09823d2 commit 3d6e930

1 file changed

Lines changed: 151 additions & 0 deletions

File tree

  • .claude/skills/create-arena-ladder
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
---
2+
name: create-arena-ladder
3+
description: >-
4+
Build a CC:Ladder for any CodeClash arena: import human-written solutions as git
5+
branches, rank them via round-robin PvP + Elo, and assemble the ladder configs.
6+
Use when asked to "create a ladder", "import human solutions", "push human bots as
7+
branches", or "make a CC:<arena> ladder" for arenas like BattleSnake, RobotRumble,
8+
CoreWar, Gomoku, RoboCode, SCML, etc.
9+
---
10+
11+
# Create an Arena Ladder (CC:Ladder)
12+
13+
A **ladder** turns a curated set of human-written bots into a ranked gauntlet, then
14+
measures how far up a model can climb. Two phases: **make the ladder** (rank the humans
15+
via round-robin), then **run the ladder** (a model climbs rung by rung until it loses).
16+
17+
This skill produces, for arena `<A>`: `human/<author>/<name>` branches on `CodeClash-ai/<A>`,
18+
a `make_<a>.yaml` (round-robin), a ranked `rungs/<a>.yaml`, and a run config `<a>.yaml`.
19+
20+
Work in the `repo/` clone. Reference implementations to mirror:
21+
`configs/ablations/ladder/{make_battlesnake,battlesnake,rungs/battlesnake}.yaml` and its
22+
`README.md`. For worked examples of the import step, the `john/*-ladder` branches show two
23+
shapes end-to-end (gomoku = function-port, robocode = copy-in).
24+
25+
---
26+
27+
## Mechanics you must respect (arena-agnostic)
28+
29+
- **Where human code lives:** each arena has its **own** repo under `CodeClash-ai`
30+
(hardcoded `GH_ORG` in `codeclash/constants.py`). The arena Dockerfile `git clone`s it
31+
into `/workspace`. Human bots are **branches of that per-arena repo**, NOT of this
32+
monorepo — so `git branch -a` here shows no `human/*`.
33+
- **How a branch becomes a player:** a player with `branch_init: human/foo/bar` makes
34+
`Player.__init__` (`codeclash/agents/player.py`) run `git fetch && git checkout` in the
35+
clone; that branch's files overlay `/workspace`. `agent: dummy` = static opponent.
36+
`push: True` (the climbing player) needs `GITHUB_TOKEN`.
37+
- **What a branch must contain:** the arena's **submission file(s)** at the path its
38+
`validate_code` expects; everything else (engine, assets) comes from the base clone.
39+
**The single source of truth is `codeclash/arenas/<a>/<a>.py` — read its `submission`
40+
attribute and `validate_code` method.** Those two define the contract. Examples:
41+
BattleSnake → `main.py` HTTP server (`info/start/end/move`); Gomoku → `main.py` with
42+
`get_move(board,color)`; SCML → agent with `decide(observation)`; RoboCode → Java class
43+
under `robots/custom/`; CoreWar → `warrior.red`.
44+
45+
---
46+
47+
## Phase 0 — Prerequisites
48+
49+
- Arena class `codeclash/arenas/<a>/<a>.py` exists; record its `submission` path + exact
50+
`validate_code` requirements.
51+
- Arena repo `github.com/CodeClash-ai/<A>` exists and its Dockerfile clones it.
52+
- Docker running; `GITHUB_TOKEN` set (public repos → `gh auth token` works for push + run).
53+
54+
## Phase 1 — Source many human solutions
55+
56+
The hard part is finding a large set. Best sources: official leaderboards and
57+
`awesome-<arena>` repos (BattleSnake used `awesome-battlesnake`; RobotRumble crawled
58+
`robotrumble.org/boards/2`; RoboCode drew from RoboWiki/GitHub). Capture author + bot name
59+
per candidate → these become the branch slugs. Keep a provenance record (source URL,
60+
author, license) as you go — a table plus a header comment in each imported file.
61+
62+
## Phase 2 — Adapt each solution to the arena's contract
63+
64+
Every bot must end up matching the one submission contract. Do NOT discard a bot merely for
65+
being in another language — pick the cheapest import shape:
66+
67+
- **Copy-in** — source is already in the arena's language/framework: drop the files in and
68+
rename/repackage (e.g. RoboCode: main class → `MyTank`, `package custom;`). Mechanical.
69+
- **Function-contract port** — reimplement the core "given state, choose a move" logic as
70+
the arena's single entry function (Gomoku `get_move`, SCML `decide`, BattleSnake `move`).
71+
Ignore the source's GUI/protocol/CLI wrapper; keep its evaluation + search faithful.
72+
73+
Porting hard rules (adapt per arena; worth writing up as a guide for any porting agents):
74+
- **Runtime-only deps** — match the arena image (often stdlib-only Python 3.10; re-express
75+
array math in pure Python). No trained weights / NN unless you can obtain the binary.
76+
- **One entry point, never raise** — a crash or illegal move = a forfeit; wrap the body and
77+
fall back to a safe legal move.
78+
- **Fast enough** — a round-robin plays many games; cap search depth / rollouts.
79+
- Only skip a bot if it truly can't run. **Log every skip with a reason — never silently
80+
drop bots.**
81+
82+
## Phase 3 — Validate (two-stage), then push
83+
84+
Local validation is necessary but NOT sufficient — a local shim skips Docker, the repo's
85+
`server.py`, and real payloads. Gate every bot in two stages before it earns a branch:
86+
87+
1. **Stage 1 (local):** syntax/import + the arena's `validate_code` legality check. Cheap;
88+
catches most breakage without Docker. Fix or drop failures.
89+
2. **Stage 2 (arena, REQUIRED):** play each stage-1 pass through the real arena image and
90+
confirm it completes a full game without erroring. This is the step that actually gates.
91+
Requires Docker. (Scripting both stages over a folder of candidates is worth it at scale.)
92+
3. **Push** each stage-2-healthy bot as `human/<author>/<name>` (dedupe identical content).
93+
Use consistent kebab/lowercase slugs; branches must be pushed before any arena run, since
94+
`branch_init` fetches from the remote.
95+
96+
## Phase 4 — Make the ladder (round-robin + Elo)
97+
98+
1. Write `configs/ablations/ladder/make_<a>.yaml` (mirror `make_battlesnake.yaml`):
99+
`tournament.rounds: 0`, a `game` block, and `players:` = every `human/*` branch as
100+
`{agent: dummy, branch_init: ...}`.
101+
2. Pre-build the image **once** to avoid a build stampede under many workers:
102+
`docker build -t codeclash/<a> -f codeclash/arenas/<a>/<A>.Dockerfile .`
103+
3. Run all-pairs PvP (resumable — skips pairs already logged; `--workers ≈ cores-2`):
104+
`GITHUB_TOKEN=$(gh auth token) uv run codeclash ladder make configs/ablations/ladder/make_<a>.yaml --workers N`
105+
→ logs land under `logs/ladder/<A>/`. Fast arenas run on a laptop; big ones (e.g. SCML's
106+
~1275 pairs) want an AWS box under `tmux`/`nohup`.
107+
4. Rank: `python -m codeclash.analysis.metrics.elo -d logs/ladder/<A> --output-dir assets/<a>_elo`
108+
→ prints the Bradley-Terry/Elo order (weakest → strongest); that ordering IS the ladder.
109+
Tip: run a cheap low-`sims` pilot first and eyeball that baselines sit near the bottom.
110+
111+
## Phase 5 — Assemble the ranked configs
112+
113+
1. `configs/ablations/ladder/rungs/<a>.yaml` — the ranked opponents, **weakest first,
114+
strongest last** (each `{agent: dummy, branch_init: human/...}`), in Elo order.
115+
2. `configs/ablations/ladder/<a>.yaml` — the climber `player` (starting at the weakest
116+
rung, `push: True`) + `ladder: !include ablations/ladder/rungs/<a>.yaml` + a
117+
`ladder_rules` block. Model on `battlesnake.yaml`.
118+
3. Optional `<a>__<model>.yaml` per-model variants (swap `model: !include mini/models/...`);
119+
they share the same `rungs/<a>.yaml` include.
120+
121+
`ladder_rules` (optional; defaults reproduce historical behavior):
122+
```yaml
123+
ladder_rules:
124+
min_round_win_fraction: 0.5 # must win strictly more than this fraction of rounds
125+
win_last_k: 1 # ...and must win the last K rounds
126+
```
127+
128+
## Phase 6 — Run the ladder
129+
130+
`uv run codeclash ladder run configs/ablations/ladder/<a>.yaml`
131+
→ prints the highest rung reached; logs under `LOCAL_LOG_DIR/<user>/LadderTournament.*`.
132+
133+
---
134+
135+
## Deliverables checklist
136+
- [ ] N validated `human/<author>/<name>` branches pushed to `CodeClash-ai/<A>`.
137+
- [ ] **Stage-2 arena smoke passed** (each bot plays a real game in Docker), skips logged.
138+
- [ ] `make_<a>.yaml` + round-robin run + Elo ranking (`assets/<a>_elo`).
139+
- [ ] `rungs/<a>.yaml` (weakest→strongest) + `<a>.yaml` (climber + `ladder_rules`).
140+
- [ ] `ladder run` executes end-to-end against a sample model.
141+
- [ ] Provenance recorded (source/author/license per bot); note in `ladder/README.md` if
142+
the arena is new.
143+
144+
## Gotchas
145+
- Human branches go to the **per-arena** repo, not the monorepo; `branch_init` fetches from
146+
the remote, so **push before any run** and keep Docker up.
147+
- A bot that fails `validate_code` silently forfeits. A local shim can pass yet fail in the
148+
arena — the **stage-2 Docker smoke** is the real gate, not stage 1.
149+
- Pre-build the arena image once before a `--workers N` run, or workers stampede the build.
150+
- Port aggressively into the single submission contract; skip only un-runnable bots, and
151+
log every skip. A port must reproduce the original's behavior.

0 commit comments

Comments
 (0)