From 1e00795e6393a4c52d6e2c9bd199f45fd4182956 Mon Sep 17 00:00:00 2001 From: John Yang Date: Mon, 6 Jul 2026 16:40:52 -0400 Subject: [PATCH 1/5] Add RoboCode CC:Ladder (116 imported classic-Robocode bots + build tooling) - 116 open-source classic Robocode bots imported to CodeClash-ai/RoboCode human/robocode/* branches (source of truth, not committed here) from ~30 repos: shipped sample bots + mgalushka Super* variants; the full RoboWiki/PEZ collection (micro/mini/nano/femto + Marshmallow/GF1/Swiffer); flat multi-file bots (Mb2, BerendBotje, MaximBot, TheCarver, BobTheBuilder, StarterBot, npcomplete); flattened subpackage bots (ur4no, kokomo); single-file DrussGT/HawkOnFire (strong top rungs); and ~35 more from a broad GitHub sweep. All copy-in (0 reimplementations) -- verbatim, only repackaged to the arena package + main class renamed to MyTank (helpers flattened where needed). yngwie/chomsky skipped (unresolvable external deps). - Each of the 116 verified to COMPILE and play a real battle in the arena image (stage 2, 116/116). - Provenance: every port file carries a source-URL/author/license header; scripts/ladder/SOURCES.md is the committed manifest (slug -> GitHub URL). See stamp_sources.py. - Fix arena description: it mislabeled itself Tank Royale but the engine is classic Robocode. - make_robocode.yaml: round-robin (100 rounds, ~2h local at -w6) for Elo; README note. - scripts/ladder/: build tooling (copy-in porting guide, structural validator, compile+battle smoke, push script, provenance stamper, reference bot); ports/ regenerated, not committed. --- codeclash/arenas/robocode/robocode.py | 8 +- configs/ablations/ladder/README.md | 13 + configs/ablations/ladder/make_robocode.yaml | 252 ++++++++++++++++++ scripts/ladder/.gitignore | 4 + scripts/ladder/PORTING_GUIDE.md | 45 ++++ scripts/ladder/README.md | 33 +++ scripts/ladder/RUN.md | 36 +++ scripts/ladder/SOURCES.md | 128 +++++++++ .../ladder/examples/robots/custom/MyTank.java | 59 ++++ scripts/ladder/push_branches.sh | 35 +++ scripts/ladder/run_smoke_all.sh | 87 ++++++ scripts/ladder/stamp_sources.py | 233 ++++++++++++++++ scripts/ladder/validate_ports.py | 74 +++++ 13 files changed, 1005 insertions(+), 2 deletions(-) create mode 100644 configs/ablations/ladder/make_robocode.yaml create mode 100644 scripts/ladder/.gitignore create mode 100644 scripts/ladder/PORTING_GUIDE.md create mode 100644 scripts/ladder/README.md create mode 100644 scripts/ladder/RUN.md create mode 100644 scripts/ladder/SOURCES.md create mode 100644 scripts/ladder/examples/robots/custom/MyTank.java create mode 100644 scripts/ladder/push_branches.sh create mode 100644 scripts/ladder/run_smoke_all.sh create mode 100644 scripts/ladder/stamp_sources.py create mode 100644 scripts/ladder/validate_ports.py diff --git a/codeclash/arenas/robocode/robocode.py b/codeclash/arenas/robocode/robocode.py index 1c595270..262b7912 100644 --- a/codeclash/arenas/robocode/robocode.py +++ b/codeclash/arenas/robocode/robocode.py @@ -18,8 +18,12 @@ class RoboCodeArena(CodeArena): name: str = "RoboCode" - description: str = f"""Robocode (Tank Royale) is a programming game where your code is the tank: each turn your bot sends intents—speed plus body/gun/radar turn rates and firepower—based on the game state it perceives via radar. -Your program decides how to move, aim, and fire in a deterministic, turn-based arena to outlast other bots. + description: str = f"""Robocode is a programming game where your code IS the tank. This is classic +Robocode (the `robocode.*` API compiled against robocode.jar) — NOT Robocode Tank Royale. +Your bot is a Java class that `extends robocode.Robot` (or `robocode.AdvancedRobot` for non-blocking +control): its `run()` method drives the tank in a loop (e.g. `ahead(100)`, `turnGunRight(90)`, +`fire(3)`), and it reacts to events like `onScannedRobot(ScannedRobotEvent)`, `onHitByBullet(...)`, +and `onHitWall(...)`. Move, aim the gun, sweep the radar, and fire to outlast other bots. Your bot logic must be written in Java and located in the `robots/custom/` directory. Keep the main bot class named `{str(RC_FILE)}`, but you can include additional Java files if you'd like.""" default_args: dict = { diff --git a/configs/ablations/ladder/README.md b/configs/ablations/ladder/README.md index 99d5e6c4..8b148e1a 100644 --- a/configs/ablations/ladder/README.md +++ b/configs/ablations/ladder/README.md @@ -12,6 +12,19 @@ You can follow these steps to create your own "CC:" ladder. The tricky part is typically finding a large collection of human solutions for a particular arena. We've typically found that googling for online leaderboards or awesome- repositories (e.g. [BattleSnake](https://github.com/BattlesnakeOfficial/awesome-battlesnake)) is a good strategy. +### RoboCode (newly added) + +The [CC:RoboCode](https://github.com/CodeClash-ai/RoboCode) repo hosts 116 human bots on `human/robocode/*` +branches (bot code lives only on the branches, not in this repo). This arena is **classic Robocode** +(`robocode.*` API compiled against `robocode.jar`), so importing open-source bots is mostly a +mechanical copy-in + rename rather than a strategy rewrite: each bot's Java class(es) are placed in +`robots/custom/`, the main class renamed to `MyTank`, `package custom;`. The set spans the shipped +sample bots (SittingDuck/Walls/Corners/…) through famous RoboWiki/PEZ micro-mini bots (Aristocles, +Pugilist, HawkOnFire) up to a single-file **DrussGT** (a world-class bot) as the top rung; a few use +Robocode's sanctioned `getDataFile` persistence (degrades gracefully without cross-battle saves). +Diamond/BeepBoop remain as future top rungs (nested-package multi-file → need flattening). Each import +was verified to **compile and play a real battle**. Build tooling + per-bot source provenance (`SOURCES.md`) live in `scripts/ladder/`. + ## Config layout Each arena has a few kinds of config in this folder: diff --git a/configs/ablations/ladder/make_robocode.yaml b/configs/ablations/ladder/make_robocode.yaml new file mode 100644 index 00000000..c815c880 --- /dev/null +++ b/configs/ablations/ladder/make_robocode.yaml @@ -0,0 +1,252 @@ +# Round-robin over the human classic-Robocode bots (imported from ~30 open-source repos; see +# scripts/ladder/SOURCES.md for each bot's origin URL/author/license), used to RANK them via +# Elo/Bradley-Terry -> see robocode.yaml. rounds:0 = dummy players play baseline battles (no code +# edits). N bots -> N*(N-1)/2 pairs; each pair plays sims_per_round rounds (batched 10 at a time, +# sim_concurrency parallel). Bots live on CodeClash-ai/RoboCode human/* branches; Docker required. +# COST (measured ~3.4s for a 50-round pair headless): at sims_per_round=100, 116 bots -> 6670 pairs +# ~= 13 core-hours -> ~2 h on a laptop (--workers 6), ~26 min on a 32-core box (-w 30). +# Run: uv run codeclash ladder make configs/ablations/ladder/make_robocode.yaml --workers 6 +tournament: + rounds: 0 +game: + name: RoboCode + sims_per_round: 100 + sim_concurrency: 5 + args: + nodisplay: true + nosound: true +players: +- agent: dummy + branch_init: human/robocode/antiwalls +- agent: dummy + branch_init: human/robocode/aristocles +- agent: dummy + branch_init: human/robocode/barbiescript +- agent: dummy + branch_init: human/robocode/berendbotje +- agent: dummy + branch_init: human/robocode/blackwidow +- agent: dummy + branch_init: human/robocode/blackwidow_mini +- agent: dummy + branch_init: human/robocode/bobthebuilder +- agent: dummy + branch_init: human/robocode/bt7274 +- agent: dummy + branch_init: human/robocode/cham +- agent: dummy + branch_init: human/robocode/chilibot +- agent: dummy + branch_init: human/robocode/chironex_micro +- agent: dummy + branch_init: human/robocode/chironex_mini +- agent: dummy + branch_init: human/robocode/corners +- agent: dummy + branch_init: human/robocode/crawler +- agent: dummy + branch_init: human/robocode/crazy +- agent: dummy + branch_init: human/robocode/dacruzer +- agent: dummy + branch_init: human/robocode/deepthought +- agent: dummy + branch_init: human/robocode/dodgebot_jnk +- agent: dummy + branch_init: human/robocode/dodgebot2 +- agent: dummy + branch_init: human/robocode/dominatorx +- agent: dummy + branch_init: human/robocode/droidpoet +- agent: dummy + branch_init: human/robocode/drussgt +- agent: dummy + branch_init: human/robocode/exterminador +- agent: dummy + branch_init: human/robocode/fire +- agent: dummy + branch_init: human/robocode/frankie +- agent: dummy + branch_init: human/robocode/genetic +- agent: dummy + branch_init: human/robocode/gf1 +- agent: dummy + branch_init: human/robocode/gntest +- agent: dummy + branch_init: human/robocode/gouldingi +- agent: dummy + branch_init: human/robocode/gruffalo +- agent: dummy + branch_init: human/robocode/haikupoet +- agent: dummy + branch_init: human/robocode/haikuwalls +- agent: dummy + branch_init: human/robocode/hawkonfire +- agent: dummy + branch_init: human/robocode/hero_pm +- agent: dummy + branch_init: human/robocode/hugbot +- agent: dummy + branch_init: human/robocode/hunter +- agent: dummy + branch_init: human/robocode/hypoleach +- agent: dummy + branch_init: human/robocode/ianstank +- agent: dummy + branch_init: human/robocode/icarus +- agent: dummy + branch_init: human/robocode/infinitylock +- agent: dummy + branch_init: human/robocode/ite_bomax +- agent: dummy + branch_init: human/robocode/ite_claptrap +- agent: dummy + branch_init: human/robocode/ite_cliffbot2 +- agent: dummy + branch_init: human/robocode/ite_ctbot +- agent: dummy + branch_init: human/robocode/ite_florian2 +- agent: dummy + branch_init: human/robocode/ite_m9 +- agent: dummy + branch_init: human/robocode/ite_simple +- agent: dummy + branch_init: human/robocode/ite_terminator +- agent: dummy + branch_init: human/robocode/jeujdapeu +- agent: dummy + branch_init: human/robocode/juggernaut +- agent: dummy + branch_init: human/robocode/kokomo +- agent: dummy + branch_init: human/robocode/leach +- agent: dummy + branch_init: human/robocode/leachpmc +- agent: dummy + branch_init: human/robocode/littlebrother +- agent: dummy + branch_init: human/robocode/littleevilbrother +- agent: dummy + branch_init: human/robocode/mako +- agent: dummy + branch_init: human/robocode/markiv +- agent: dummy + branch_init: human/robocode/markrobo +- agent: dummy + branch_init: human/robocode/marshmallow +- agent: dummy + branch_init: human/robocode/maximbot +- agent: dummy + branch_init: human/robocode/mb2 +- agent: dummy + branch_init: human/robocode/megaborsten +- agent: dummy + branch_init: human/robocode/meow +- agent: dummy + branch_init: human/robocode/myfirstkiller +- agent: dummy + branch_init: human/robocode/myfirstrobot +- agent: dummy + branch_init: human/robocode/nagisphere +- agent: dummy + branch_init: human/robocode/npcomplete +- agent: dummy + branch_init: human/robocode/npcsniper +- agent: dummy + branch_init: human/robocode/oppswantmedead +- agent: dummy + branch_init: human/robocode/paolo +- agent: dummy + branch_init: human/robocode/pikachu +- agent: dummy + branch_init: human/robocode/poet +- agent: dummy + branch_init: human/robocode/propiavancat +- agent: dummy + branch_init: human/robocode/pugilist +- agent: dummy + branch_init: human/robocode/quadwall +- agent: dummy + branch_init: human/robocode/ramfire +- agent: dummy + branch_init: human/robocode/regullarmonk +- agent: dummy + branch_init: human/robocode/reimu +- agent: dummy + branch_init: human/robocode/robrrrat +- agent: dummy + branch_init: human/robocode/roleksii +- agent: dummy + branch_init: human/robocode/sadbot +- agent: dummy + branch_init: human/robocode/shreker +- agent: dummy + branch_init: human/robocode/sittingduck +- agent: dummy + branch_init: human/robocode/smallpoet +- agent: dummy + branch_init: human/robocode/spinbot +- agent: dummy + branch_init: human/robocode/starterbot +- agent: dummy + branch_init: human/robocode/supercorners +- agent: dummy + branch_init: human/robocode/supercrazy +- agent: dummy + branch_init: human/robocode/supermercutio +- agent: dummy + branch_init: human/robocode/superramfire +- agent: dummy + branch_init: human/robocode/superspinbot +- agent: dummy + branch_init: human/robocode/supertracker +- agent: dummy + branch_init: human/robocode/superwalls +- agent: dummy + branch_init: human/robocode/swiffer +- agent: dummy + branch_init: human/robocode/tannerbot1 +- agent: dummy + branch_init: human/robocode/tarektank +- agent: dummy + branch_init: human/robocode/tearsofsteel +- agent: dummy + branch_init: human/robocode/thecarver +- agent: dummy + branch_init: human/robocode/tirolio +- agent: dummy + branch_init: human/robocode/tityus +- agent: dummy + branch_init: human/robocode/tracker +- agent: dummy + branch_init: human/robocode/trackfire +- agent: dummy + branch_init: human/robocode/trianglehunter +- agent: dummy + branch_init: human/robocode/ultron +- agent: dummy + branch_init: human/robocode/ur4no +- agent: dummy + branch_init: human/robocode/velocirobot +- agent: dummy + branch_init: human/robocode/vergere +- agent: dummy + branch_init: human/robocode/vertileach +- agent: dummy + branch_init: human/robocode/vertimicro +- agent: dummy + branch_init: human/robocode/walls +- agent: dummy + branch_init: human/robocode/wallspoet +- agent: dummy + branch_init: human/robocode/wallspoetas +- agent: dummy + branch_init: human/robocode/wallspoethaiku +- agent: dummy + branch_init: human/robocode/wavesurfing +- agent: dummy + branch_init: human/robocode/wilde +- agent: dummy + branch_init: human/robocode/wrecker +prompts: + game_description: RoboCode ladder diff --git a/scripts/ladder/.gitignore b/scripts/ladder/.gitignore new file mode 100644 index 00000000..b715ef09 --- /dev/null +++ b/scripts/ladder/.gitignore @@ -0,0 +1,4 @@ +# Regenerated build artifacts — bot ports live on the CodeClash-ai/ branches +# (source of truth), not in this repo. Populate ports/ locally when (re)building a ladder. +ports/ +out/ diff --git a/scripts/ladder/PORTING_GUIDE.md b/scripts/ladder/PORTING_GUIDE.md new file mode 100644 index 00000000..485cff0a --- /dev/null +++ b/scripts/ladder/PORTING_GUIDE.md @@ -0,0 +1,45 @@ +# Importing a classic Robocode bot into the CodeClash arena + +This arena is **classic Robocode** (Java, `robocode.jar`), NOT Tank Royale. Importing an +open-source classic bot is mostly a mechanical **copy-in + rename**, not a strategy rewrite. + +## The submission contract +The submission is a directory `robots/custom/` containing Java source. The arena harness then: +1. copies `robots/custom/*` into `robots//`, +2. runs `sed 's/custom//g'` over every `.java` (this renames the `package custom` → + `package ` so bots are namespaced), +3. compiles `javac -cp "libs/robocode.jar" robots//*.java` ← **flat glob, one directory**, +4. runs battles selecting `.MyTank*`. + +`validate_code` requires `robots/custom/MyTank.java` to exist, compile, and produce `MyTank.class`. + +## Hard rules for a port (`ports//`) +Produce a directory `scripts/ladder/ports//` containing: +- **`MyTank.java`** — the bot's main class, **renamed to `public class MyTank`** (rename the source's + main class everywhere: declaration, constructors, and any self-references). It must + `extends robocode.Robot` (or `AdvancedRobot` / `TeamRobot` / `RateControlRobot` / `JuniorRobot`). +- **`package custom;`** as the package line in `MyTank.java` and in *every* helper file. Replace any + original `package a.b.c;`. +- **Helper classes** (if the bot is multi-file) as additional flat `.java` files in the same dir, + also `package custom;`. **No nested subdirectories** — the compile is a flat `*.java` glob, so a + bot that uses nested packages (e.g. `voidious/gun/…`) must be flattened or it won't compile. + Inner classes inside one file are fine (preferred). +- **The literal substring `custom` must NOT appear anywhere except the `package custom;` lines.** + The harness's `sed s/custom//g` is a blind global replace, so any identifier, comment, or + string containing "custom" would be corrupted. Rename such tokens. + +## What's allowed / what breaks +- Only the classic `robocode.*` API. No Tank Royale (`dev.robocode.tankroyale.*`). +- Robocode runs under a security manager: **raw `java.io`, threads, reflection, and network are + blocked.** Persistent gun/enemy stats via the sanctioned `getDataFile()` + + `RobocodeFileOutputStream` are allowed but **may not persist** across our battles — such a bot + still compiles and plays, it just degrades to no saved data (fine; note it). If the source uses + raw `java.io`/threads/reflection, it will fail — flag and skip, or strip that path if it's optional. +- Keep behavior faithful: do not "improve" the bot; only rename/repackage/flatten. + +## Deliverable + report +Write the port dir `scripts/ladder/ports//` (at minimum `MyTank.java`). It must be valid Java +that would compile against `robocode.jar`. In your report note per bot: source repo/author, base +class, #files, and any caveat (data-file use, "custom"-token rename you had to do, multi-file, or +anything you had to strip). A reference `MyTank.java` lives at +`scripts/ladder/examples/robots/custom/MyTank.java`. diff --git a/scripts/ladder/README.md b/scripts/ladder/README.md new file mode 100644 index 00000000..8bffb89b --- /dev/null +++ b/scripts/ladder/README.md @@ -0,0 +1,33 @@ +# Ladder build tooling (RoboCode) + +Operational one-off scripts for constructing a CC:Ladder for **classic Robocode**. Unlike the +SCML/Gomoku ladders (which re-express bots into a function contract), Robocode importing is mostly +a mechanical **copy-in + rename**: classic bots are Java classes that drop into `robots/custom/`. + +This is scaffolding, not product code — nothing under `codeclash/` imports it. The durable outputs +are the `human/*` branches on `CodeClash-ai/RoboCode` (the bots) and the +`configs/ablations/ladder/*.yaml` configs. Ports are **not** committed here (see `.gitignore`). + +## Files +- `PORTING_GUIDE.md` — the classic-Robocode submission contract + copy-in/rename rules (main class + → `MyTank`, `package custom;`, flat files, no stray "custom" token). Hand to an import agent with + `examples/robots/custom/MyTank.java` as the reference. +- `examples/robots/custom/MyTank.java` — reference bot (AdvancedRobot) + porting example. +- `validate_ports.py` — stage 1 (structural, local): each `ports//` has `MyTank.java` with + `class MyTank extends `, `package custom;`, flat, no sed-hazard token. Writes `_stage1.json`. +- `run_smoke_all.sh` — **stage 2 (the real gate):** in the arena Docker image, compile each port + (`javac -cp libs/robocode.jar`) and run a real 3-round battle vs `sample.Walls`; confirm it + compiles AND plays. Writes `_stage2.json` + per-bot compile/battle logs in `out/`. +- `push_branches.sh` — push each stage-2-healthy `ports//` to `CodeClash-ai/RoboCode` as + `human/robocode/` with the files under `robots/custom/`. +- `RUN.md` — round-robin (`ladder make`) + Elo ranking instructions. + +## Typical flow +1. Populate `ports//MyTank.java` (fan out import agents with `PORTING_GUIDE.md`). +2. `python3 scripts/ladder/validate_ports.py` → stage 1 (structural) +3. `bash scripts/ladder/run_smoke_all.sh` → stage 2 (compile + battle each; needs Docker) +4. `bash scripts/ladder/push_branches.sh` → push healthy ports to branches +5. Rank + assemble configs — see `RUN.md`. + +Note: `ports/` and `out/` are gitignored and shared on disk across branches — clean stale files +(`rm -rf scripts/ladder/ports/* scripts/ladder/out`) when switching between arena ladder branches. diff --git a/scripts/ladder/RUN.md b/scripts/ladder/RUN.md new file mode 100644 index 00000000..d7a95c60 --- /dev/null +++ b/scripts/ladder/RUN.md @@ -0,0 +1,36 @@ +# Running the RoboCode round-robin (Elo ranking) + +Rank the human bots in `configs/ablations/ladder/make_robocode.yaml` via all-pairs battles, then fit +Elo. Each pair plays `sims_per_round` battle-rounds (the arena batches them `SIMS_PER_RUN=10` at a +time, `sim_concurrency` in parallel). Battles are headless Java; cost is moderate — measure one pair +first (below) to size the full run. Bots live on `CodeClash-ai/RoboCode` `human/*` branches; +`branch_init` fetches them at runtime, so you only need this repo branch + Docker + a GitHub token. + +## Prerequisites +- Docker running, git, `uv`, and a token for `CodeClash-ai/RoboCode` (public → `gh auth token`). + +## Step 0 — pre-build the arena image once (avoids a build stampede under -w N) +```bash +docker build -t codeclash/robocode -f codeclash/arenas/robocode/RoboCode.Dockerfile . +``` + +## Step 1 — round-robin +`N` bots → `N*(N-1)/2` pairs. Keep `--workers ≈ cores-2`. Resumable (skips logged pairs). +```bash +GITHUB_TOKEN=$(gh auth token) \ + uv run codeclash ladder make configs/ablations/ladder/make_robocode.yaml --workers 6 +``` +Tip: time one pair first to size the run — e.g. run a 2-player `codeclash run` config and note the +wall time, then multiply by pair count / workers. + +## Step 2 — rank +```bash +python -m codeclash.analysis.metrics.elo -d logs/ladder/RoboCode --output-dir assets/robocode_elo +``` +Prints the Bradley-Terry/Elo ordering and writes `assets/robocode_elo/elo_results.log`. + +## Step 3 — assemble the ladder configs (once you have the ranking) +1. `configs/ablations/ladder/rungs/robocode.yaml` — ranked opponents, weakest first, strongest last. +2. `configs/ablations/ladder/robocode.yaml` — climber `player` + `ladder: !include ablations/ladder/rungs/robocode.yaml` + + a `ladder_rules` block (model on `battlesnake.yaml`). +3. Optional `robocode__.yaml` per-model variants. diff --git a/scripts/ladder/SOURCES.md b/scripts/ladder/SOURCES.md new file mode 100644 index 00000000..be338b23 --- /dev/null +++ b/scripts/ladder/SOURCES.md @@ -0,0 +1,128 @@ +# RoboCode ladder — bot sources + +Every `human/robocode/` bot on `CodeClash-ai/RoboCode` was imported (copy-in) from an +open-source **classic Robocode** repo below — imported verbatim, only repackaged to +`package custom;` and the main class renamed to `MyTank` (helper files flattened where needed). +Each port file also carries this provenance as a header comment. URLs use `/blob/HEAD/`. + +| slug | source | author | license | files | +|---|---|---|---|---| +| `antiwalls` | [kinnla/misc](https://github.com/kinnla/misc/blob/HEAD/robocode/AntiWalls.java) | kinnla | unspecified | 1 | +| `aristocles` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/micro/Aristocles.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `barbiescript` | [vftheodoro/BarbieScript-RoboCode](https://github.com/vftheodoro/BarbieScript-RoboCode/blob/HEAD/Source Code BarbieScript/BarbieScript.java) | vftheodoro | unspecified | 1 | +| `berendbotje` | [johan-adriaans/BerendBotje](https://github.com/johan-adriaans/BerendBotje/blob/HEAD/src/hackersNL/BerendBotje.java) | Johan Adriaans | unspecified | 8 | +| `blackwidow` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/micro/BlackWidow.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `blackwidow_mini` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/mini/BlackWidow.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `bobthebuilder` | [winstliu/robocode](https://github.com/winstliu/robocode/blob/HEAD/exam2016/BobTheBuilder.java) | winstliu | unspecified | 6 | +| `bt7274` | [lucasgch/titan](https://github.com/lucasgch/titan/blob/HEAD/BT_7274_V6.6.java) | lucasgch | unspecified | 1 | +| `cham` | [namnguyenthanhwork/RoboCode-GuessFactor-MeleeStrategy](https://github.com/namnguyenthanhwork/RoboCode-GuessFactor-MeleeStrategy/blob/HEAD/Cham.java) | namnguyenthanhwork | unspecified | 1 | +| `chilibot` | [looklazy/robocode](https://github.com/looklazy/robocode/blob/HEAD/src/chili/ChiliBot.java) | looklazy | unspecified | 1 | +| `chironex_micro` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/micro/ChironexFleckeri.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `chironex_mini` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/mini/ChironexFleckeri.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `corners` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sample/Corners.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `crawler` | [txeverson/robocode](https://github.com/txeverson/robocode/blob/HEAD/Crawler.java) | txeverson | unspecified | 1 | +| `crazy` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sample/Crazy.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `dacruzer` | [philipmjohnson/robocode-pmj-dacruzer](https://github.com/philipmjohnson/robocode-pmj-dacruzer/blob/HEAD/src/main/java/pmj/DaCruzer.java) | Philip Johnson | unspecified | 1 | +| `deepthought` | [TRex22/DeepThought](https://github.com/TRex22/DeepThought/blob/HEAD/src/DeepThought.java) | TRex22 | unspecified | 1 | +| `dodgebot2` | [LoganCSC/robocode-robots](https://github.com/LoganCSC/robocode-robots/blob/HEAD/src/com/barrybecker4/DodgeBot2.java) | LoganCSC | unspecified | 1 | +| `dodgebot_jnk` | [pseminatore/jnk](https://github.com/pseminatore/jnk/blob/HEAD/Dodge_Bot.java) | pseminatore | unspecified | 1 | +| `dominatorx` | [vikdov/RoboCodeEvent](https://github.com/vikdov/RoboCodeEvent/blob/HEAD/DominatorX.java) | vikdov | unspecified | 1 | +| `droidpoet` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/femto/DroidPoet.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `drussgt` | [AdmiralRasmussen/robocode](https://github.com/AdmiralRasmussen/robocode/blob/HEAD/bot-collection/DrussGT.java) | collected classic bots (see repo) | unspecified | 1 | +| `exterminador` | [andrekorol/my-robocode-robots](https://github.com/andrekorol/my-robocode-robots/blob/HEAD/src/univap/ExterminadorPrimeiroGrau.java) | andrekorol | unspecified | 1 | +| `fire` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sample/Fire.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `frankie` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/frankie/Frankie.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `genetic` | [linuxuser0/genetic](https://github.com/linuxuser0/genetic/blob/HEAD/Genetic.java) | linuxuser0 | unspecified | 1 | +| `gf1` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/etc/GF1.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `gntest` | [josephjeon/RoboCode](https://github.com/josephjeon/RoboCode/blob/HEAD/GNtest.java) | Joseph Jeon | unspecified | 1 | +| `gouldingi` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/mini/Gouldingi.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `gruffalo` | [kylebennett/Robocode](https://github.com/kylebennett/Robocode/blob/HEAD/BasicRobots/kylebennett/TheGruffalo.java) | Kyle Bennett | unspecified | 1 | +| `haikupoet` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/femto/HaikuPoet.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `haikuwalls` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/femto/HaikuWalls.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `hawkonfire` | [AdmiralRasmussen/robocode](https://github.com/AdmiralRasmussen/robocode/blob/HEAD/bot-collection/HawkOnFireOS.java) | collected classic bots (see repo) | unspecified | 1 | +| `hero_pm` | [zhiwei121/robocode-hero](https://github.com/zhiwei121/robocode-hero/blob/HEAD/Hero.java) | zhiwei121 | unspecified | 1 | +| `hugbot` | [kylebennett/Robocode](https://github.com/kylebennett/Robocode/blob/HEAD/BasicRobots/kylebennett/HugBot.java) | Kyle Bennett | unspecified | 1 | +| `hunter` | [mcd8604/robocode](https://github.com/mcd8604/robocode/blob/HEAD/src/mcd/Hunter.java) | mcd8604 | unspecified | 1 | +| `hypoleach` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/mini/HypoLeach.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `ianstank` | [alpian/robocode](https://github.com/alpian/robocode/blob/HEAD/src/main/java/com/github/alpian/robocode/tanks/IansTank.java) | alpian | unspecified | 1 | +| `icarus` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/nano/Icarus.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `infinitylock` | [WouterJoosse/Robocode](https://github.com/WouterJoosse/Robocode/blob/HEAD/src/wiki/InfinityLock.java) | WouterJoosse | unspecified | 1 | +| `ite_bomax` | [it-economics/robocode](https://github.com/it-economics/robocode/blob/HEAD/src/main/java/com/ite/robocode/BomaxBot.java) | it-economics | unspecified | 1 | +| `ite_claptrap` | [it-economics/robocode](https://github.com/it-economics/robocode/blob/HEAD/src/main/java/com/ite/robocode/ClapTrap.java) | it-economics | unspecified | 1 | +| `ite_cliffbot2` | [it-economics/robocode](https://github.com/it-economics/robocode/blob/HEAD/src/main/java/com/ite/robocode/CliffBot2.java) | it-economics | unspecified | 1 | +| `ite_ctbot` | [it-economics/robocode](https://github.com/it-economics/robocode/blob/HEAD/src/main/java/com/ite/robocode/CtBot.java) | it-economics | unspecified | 1 | +| `ite_florian2` | [it-economics/robocode](https://github.com/it-economics/robocode/blob/HEAD/src/main/java/com/ite/robocode/Florian2Bot.java) | it-economics | unspecified | 1 | +| `ite_m9` | [it-economics/robocode](https://github.com/it-economics/robocode/blob/HEAD/src/main/java/com/ite/robocode/MMMMMMMMM.java) | it-economics | unspecified | 1 | +| `ite_simple` | [it-economics/robocode](https://github.com/it-economics/robocode/blob/HEAD/src/main/java/com/ite/robocode/SimpleBot.java) | it-economics | unspecified | 1 | +| `ite_terminator` | [it-economics/robocode](https://github.com/it-economics/robocode/blob/HEAD/src/main/java/com/ite/robocode/Terminator.java) | it-economics | unspecified | 1 | +| `jeujdapeu` | [joaomcarvalho/robocode](https://github.com/joaomcarvalho/robocode/blob/HEAD/robots/jeuj/JeujDaPeu.java) | joaomcarvalho | unspecified | 1 | +| `juggernaut` | [dankraemer/robocode](https://github.com/dankraemer/robocode/blob/HEAD/Juggernaut.java) | dankraemer | unspecified | 1 | +| `kokomo` | [0x65-e/Kokomo](https://github.com/0x65-e/Kokomo/blob/HEAD/src/california/surf/Kokomo.java) | 0x65-e | unspecified | 8 | +| `leach` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/mini/Leach.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `leachpmc` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/etc/LeachPMC.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `littlebrother` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/nano/LittleBrother.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `littleevilbrother` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/nano/LittleEvilBrother.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `mako` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/mako/Mako.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `markiv` | [Tibola/robocode](https://github.com/Tibola/robocode/blob/HEAD/miv/MarkIV.java) | Tibola | unspecified | 1 | +| `markrobo` | [zcjerry229/RoboCode](https://github.com/zcjerry229/RoboCode/blob/HEAD/src/MarkRobo/MarkRobo.java) | zcjerry229 | unspecified | 1 | +| `marshmallow` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/Marshmallow.java) | PEZ (Peter Strömberg) et al. | RWPCL | 15 | +| `maximbot` | [mgalushka/robocode-robots](https://github.com/mgalushka/robocode-robots/blob/HEAD/src/main/java/com/maximgalushka/robocode/MaximBot.java) | Maxim Galushka / CrazyBassoonist et al. | unspecified | 3 | +| `mb2` | [gjgomez/RoboCodeSample](https://github.com/gjgomez/RoboCodeSample/blob/HEAD/Mb2.java) | gjgomez | unspecified | 9 | +| `megaborsten` | [denssle/Robocode](https://github.com/denssle/Robocode/blob/HEAD/MegaBorsten2000.java) | denssle | unspecified | 1 | +| `meow` | [Team488/RobocodeRobots](https://github.com/Team488/RobocodeRobots/blob/HEAD/Robots/src/YYK/Meow.java) | Team488 | unspecified | 1 | +| `myfirstkiller` | [andrekorol/my-robocode-robots](https://github.com/andrekorol/my-robocode-robots/blob/HEAD/src/andykbr/MyFirstKiller.java) | andrekorol | unspecified | 1 | +| `myfirstrobot` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sample/MyFirstRobot.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `nagisphere` | [Luke-F-W/NagiSphere-Games-Fleadh-2026](https://github.com/Luke-F-W/NagiSphere-Games-Fleadh-2026/blob/HEAD/Nagisphere.java) | Luke-F-W | unspecified | 1 | +| `npcomplete` | [andr3eee1/Robocode-Nerdvana](https://github.com/andr3eee1/Robocode-Nerdvana/blob/HEAD/src/npcompute/NPcomplete.java) | andr3eee1 | unspecified | 17 | +| `npcsniper` | [iagomonteiro13579/robocode.NPC](https://github.com/iagomonteiro13579/robocode.NPC/blob/HEAD/npc/NPCSniperBot.java) | iagomonteiro13579 | unspecified | 1 | +| `oppswantmedead` | [andrekorol/my-robocode-robots](https://github.com/andrekorol/my-robocode-robots/blob/HEAD/src/againstallodds/OppsWantMeDead.java) | andrekorol | unspecified | 2 | +| `paolo` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/mini/Paolo.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `pikachu` | [kcanida/robocode-kkc-pikachu](https://github.com/kcanida/robocode-kkc-pikachu/blob/HEAD/src/main/java/kkc/Pikachu.java) | kcanida | unspecified | 1 | +| `poet` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/femto/Poet.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `propiavancat` | [pmontp19/robocode](https://github.com/pmontp19/robocode/blob/HEAD/src/robots/PropiAvancat.java) | pmontp19 | unspecified | 1 | +| `pugilist` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/mini/Pugilist.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `quadwall` | [gabriel-lw/QuadWall_robocode](https://github.com/gabriel-lw/QuadWall_robocode/blob/HEAD/quadwall/QuadWall.java) | gabriel-lw | unspecified | 1 | +| `ramfire` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sample/RamFire.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `regullarmonk` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sampleex/ProxyOfGreyEminence.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `reimu` | [g-otn/robocode-reimu](https://github.com/g-otn/robocode-reimu/blob/HEAD/src/Alpha/Reimu.java) | g-otn | unspecified | 1 | +| `robrrrat` | [sacdalance/robrrrat](https://github.com/sacdalance/robrrrat/blob/HEAD/RobrrRat.java) | sacdalance | unspecified | 1 | +| `roleksii` | [MiradoConsulting/roleksii](https://github.com/MiradoConsulting/roleksii/blob/HEAD/src/main/java/ROLEKSII.java) | MiradoConsulting | unspecified | 1 | +| `sadbot` | [avsthiago/SadBot-Robocode](https://github.com/avsthiago/SadBot-Robocode/blob/HEAD/aps_robocode/SadBot.java) | avsthiago | unspecified | 1 | +| `shreker` | [Alexbay218/Robocode-612](https://github.com/Alexbay218/Robocode-612/blob/HEAD/robots/Alexbay218/Shreker.java) | Alexbay218 | unspecified | 1 | +| `sittingduck` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sample/SittingDuck.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `smallpoet` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/femto/SmallPoet.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `spinbot` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sample/SpinBot.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `starterbot` | [JonHarder/RoboCodeCompetition](https://github.com/JonHarder/RoboCodeCompetition/blob/HEAD/src/starterbot/StarterBot.java) | JonHarder | unspecified | 3 | +| `supercorners` | [mgalushka/robocode-robots](https://github.com/mgalushka/robocode-robots/blob/HEAD/src/main/java/sample/SuperCorners.java) | Maxim Galushka / CrazyBassoonist et al. | unspecified | 1 | +| `supercrazy` | [mgalushka/robocode-robots](https://github.com/mgalushka/robocode-robots/blob/HEAD/src/main/java/sample/SuperCrazy.java) | Maxim Galushka / CrazyBassoonist et al. | unspecified | 1 | +| `supermercutio` | [mgalushka/robocode-robots](https://github.com/mgalushka/robocode-robots/blob/HEAD/src/main/java/sample/SuperMercutio.java) | Maxim Galushka / CrazyBassoonist et al. | unspecified | 1 | +| `superramfire` | [mgalushka/robocode-robots](https://github.com/mgalushka/robocode-robots/blob/HEAD/src/main/java/sample/SuperRamFire.java) | Maxim Galushka / CrazyBassoonist et al. | unspecified | 1 | +| `superspinbot` | [mgalushka/robocode-robots](https://github.com/mgalushka/robocode-robots/blob/HEAD/src/main/java/sample/SuperSpinBot.java) | Maxim Galushka / CrazyBassoonist et al. | unspecified | 1 | +| `supertracker` | [mgalushka/robocode-robots](https://github.com/mgalushka/robocode-robots/blob/HEAD/src/main/java/sample/SuperTracker.java) | Maxim Galushka / CrazyBassoonist et al. | unspecified | 1 | +| `superwalls` | [mgalushka/robocode-robots](https://github.com/mgalushka/robocode-robots/blob/HEAD/src/main/java/sample/SuperWalls.java) | Maxim Galushka / CrazyBassoonist et al. | unspecified | 1 | +| `swiffer` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/clean/Swiffer.java) | PEZ (Peter Strömberg) et al. | RWPCL | 3 | +| `tannerbot1` | [TannerRogalsky/Robocode](https://github.com/TannerRogalsky/Robocode/blob/HEAD/Tannerbot1.java) | Tanner Rogalsky | unspecified | 1 | +| `tarektank` | [alpian/robocode](https://github.com/alpian/robocode/blob/HEAD/src/main/java/com/github/alpian/robocode/tanks/TarekTank.java) | alpian | unspecified | 1 | +| `tearsofsteel` | [TechnischeInformatica/Robocode2013](https://github.com/TechnischeInformatica/Robocode2013/blob/HEAD/src/TearsofSteel.java) | TechnischeInformatica | unspecified | 1 | +| `thecarver` | [pranav-prakash/TheCarverBot](https://github.com/pranav-prakash/TheCarverBot/blob/HEAD/pt/TheCarver.java) | Pranav Prakash | unspecified | 8 | +| `tirolio` | [barriosnahuel/algorithms-robocode-kenchu](https://github.com/barriosnahuel/algorithms-robocode-kenchu/blob/HEAD/src/main/java/pnt/TiroLioYCoshaGolda3.java) | barriosnahuel | unspecified | 1 | +| `tityus` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/mini/Tityus.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `tracker` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sample/Tracker.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `trackfire` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sample/TrackFire.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `trianglehunter` | [muzardo/robocode-trianglehunter](https://github.com/muzardo/robocode-trianglehunter/blob/HEAD/Main.java) | muzardo | unspecified | 1 | +| `ultron` | [rafaeljdesa/robocode](https://github.com/rafaeljdesa/robocode/blob/HEAD/Ultron.java) | rafaeljdesa | unspecified | 1 | +| `ur4no` | [UR4N0-235/UR4NO](https://github.com/UR4N0-235/UR4NO/blob/HEAD/UR4NO.java) | UR4N0-235 | unspecified | 9 | +| `velocirobot` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sample/VelociRobot.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `vergere` | [John-Paul-R/Vergere](https://github.com/John-Paul-R/Vergere/blob/HEAD/origin/nano/Vergere.java) | John-Paul-R | unspecified | 1 | +| `vertileach` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/mini/VertiLeach.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `vertimicro` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/micro/VertiMicro.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `walls` | [robo-code/robocode](https://github.com/robo-code/robocode/blob/HEAD/robocode.samples/src/main/java/sample/Walls.java) | Robocode (Mathew Nelson / Flemming N. Larsen et al.) | EPL-1.0 | 1 | +| `wallspoet` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/femto/WallsPoet.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `wallspoetas` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/femto/WallsPoetAS.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `wallspoethaiku` | [PEZ/Bots](https://github.com/PEZ/Bots/blob/HEAD/pez/femto/WallsPoetHaiku.java) | PEZ (Peter Strömberg) et al. | RWPCL | 1 | +| `wavesurfing` | [AdmiralRasmussen/robocode](https://github.com/AdmiralRasmussen/robocode/blob/HEAD/bot-collection/WaveSurfing.java) | collected classic bots (see repo) | unspecified | 1 | +| `wilde` | [alexjamesmacpherson/robocode](https://github.com/alexjamesmacpherson/robocode/blob/HEAD/Wilde.java) | alexjamesmacpherson | unspecified | 3 | +| `wrecker` | [joaocarpim/robocode_CPS](https://github.com/joaocarpim/robocode_CPS/blob/HEAD/Wrecker.java) | joaocarpim | unspecified | 1 | + +Licenses marked *unspecified* had no explicit LICENSE in the source repo; classic RoboWiki +bots are conventionally under the RWPCL. Verify before any redistribution beyond research use. diff --git a/scripts/ladder/examples/robots/custom/MyTank.java b/scripts/ladder/examples/robots/custom/MyTank.java new file mode 100644 index 00000000..fd272d9c --- /dev/null +++ b/scripts/ladder/examples/robots/custom/MyTank.java @@ -0,0 +1,59 @@ +package custom; + +import robocode.AdvancedRobot; +import robocode.ScannedRobotEvent; +import robocode.HitByBulletEvent; +import robocode.HitWallEvent; + +/** + * Reference bot for the CodeClash classic-Robocode arena contract and the porting example. + * + * The main class MUST be named MyTank and live in robots/custom/MyTank.java with `package custom;`. + * This is a simple AdvancedRobot: oscillate, spin the radar to keep a lock, and fire with power + * scaled to distance. Kept intentionally modest so it's a fair smoke-test opponent, not a wall. + */ +public class MyTank extends AdvancedRobot { + private int moveDirection = 1; + + public void run() { + // Decouple gun+radar from the body so we can aim while moving. + setAdjustGunForRobotTurn(true); + setAdjustRadarForGunTurn(true); + while (true) { + setTurnRadarRight(360); + setAhead(120 * moveDirection); + setTurnRight(20); + execute(); + } + } + + public void onScannedRobot(ScannedRobotEvent e) { + double distance = e.getDistance(); + double power = distance > 400 ? 1.0 : (distance > 200 ? 2.0 : 3.0); + + // Simple head-on aim: turn gun toward the absolute bearing of the target. + double absoluteBearing = getHeading() + e.getBearing(); + double gunTurn = absoluteBearing - getGunHeading(); + setTurnGunRight(normalizeBearing(gunTurn)); + if (Math.abs(gunTurn) < 10) { + setFire(power); + } + // Keep the radar locked on the target. + setTurnRadarRight(normalizeBearing(absoluteBearing - getRadarHeading()) * 2); + } + + public void onHitByBullet(HitByBulletEvent e) { + moveDirection = -moveDirection; + setAhead(80 * moveDirection); + } + + public void onHitWall(HitWallEvent e) { + moveDirection = -moveDirection; + } + + private double normalizeBearing(double angle) { + while (angle > 180) angle -= 360; + while (angle < -180) angle += 360; + return angle; + } +} diff --git a/scripts/ladder/push_branches.sh b/scripts/ladder/push_branches.sh new file mode 100644 index 00000000..cada7c43 --- /dev/null +++ b/scripts/ladder/push_branches.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Push every stage-2-healthy port to CodeClash-ai/RoboCode as a human/robocode/ branch, +# placing the port's .java files in the arena submission dir robots/custom/. +# Port dir scripts/ladder/ports// -> branch human/robocode/ with robots/custom/*.java. +# bash scripts/ladder/push_branches.sh +set -euo pipefail +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +PORTS="$REPO_ROOT/scripts/ladder/ports" +ARENA_REPO="CodeClash-ai/RoboCode" +SKIP="" # space-separated slugs to defer, if any + +TMP=$(mktemp -d) +git clone -q "https://x-access-token:$(gh auth token)@github.com/${ARENA_REPO}.git" "$TMP" +cd "$TMP" +DEFAULT=$(git symbolic-ref --short HEAD) + +pushed=0; skipped=0 +PY="$(command -v python3 || command -v python)" +OKS=$("$PY" -c "import json;d=json.load(open('$PORTS/_stage2.json'));print('\n'.join(k for k,v in sorted(d.items()) if v.get('ok')))") + +for slug in $OKS; do + case " $SKIP " in *" $slug "*) echo "SKIP $slug (deferred)"; skipped=$((skipped+1)); continue;; esac + branch="human/robocode/$slug" + git checkout -q "$DEFAULT" + git checkout -q -B "$branch" + rm -rf robots/custom; mkdir -p robots/custom + cp "$PORTS/$slug/"*.java robots/custom/ + git add robots/custom + git -c user.email=player@codeclash.com -c user.name="CodeClash" commit -qm "Import $slug (RoboCode ladder)" + git push -q -f -u origin "$branch" 2>/dev/null + echo "PUSH $branch" + pushed=$((pushed+1)) +done +cd "$REPO_ROOT"; rm -rf "$TMP" +echo "=== pushed $pushed, skipped $skipped ===" diff --git a/scripts/ladder/run_smoke_all.sh b/scripts/ladder/run_smoke_all.sh new file mode 100644 index 00000000..3f2c44d7 --- /dev/null +++ b/scripts/ladder/run_smoke_all.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# Stage 2 (the real gate): for EVERY port, replicate exactly what the arena does — copy into +# robots//, sed custom->slug, `javac -cp libs/robocode.jar`, then RUN A REAL BATTLE +# (.MyTank vs sample.Walls, 3 rounds) headless in the arena Docker image. Confirms each bot +# actually COMPILES and PLAYS. Writes ports/_stage2.json + per-bot compile/results logs in out/. +# bash scripts/ladder/run_smoke_all.sh +set -euo pipefail +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +PORTS="$REPO_ROOT/scripts/ladder/ports" +OUT="$REPO_ROOT/scripts/ladder/out" +IMAGE="robocode-smoke:latest" +PY="$(command -v python3 || command -v python)" + +cd "$REPO_ROOT" +echo "==> Building RoboCode arena image (clones CodeClash-ai/RoboCode; Maven/Temurin — may take a few min)" +docker build -q -f codeclash/arenas/robocode/RoboCode.Dockerfile -t "$IMAGE" . >/dev/null +mkdir -p "$OUT" + +# container-side loop: compile + battle each ports// exactly like the arena +cat > "$OUT/_loop.sh" <<'LOOP' +#!/bin/bash +cd /workspace +for d in /ports/*/; do + slug=$(basename "$d") + rm -rf robots/$slug; mkdir -p robots/$slug + cp "$d"*.java robots/$slug/ 2>/dev/null + find robots/$slug/ -name '*.java' -exec sed -i "s/custom/$slug/g" {} + + comp=fail + if javac -cp "libs/robocode.jar" robots/$slug/*.java >/out/$slug.compile.log 2>&1; then + [ -f robots/$slug/MyTank.class ] && comp=ok + fi + batt=skip + if [ "$comp" = ok ]; then + cat > battles/smoke_$slug.battle </dev/null || true + if timeout 90 ./robocode.sh -nodisplay -nosound -battle battles/smoke_$slug.battle -results /out/$slug.results >/out/$slug.battle.log 2>&1; then + batt=ok + else + batt=fail + fi + fi + echo "SMOKE $slug compile=$comp battle=$batt" +done +LOOP + +echo "==> Compiling + battling each port in the arena image ..." +docker run --rm -v "$PORTS:/ports:ro" -v "$OUT:/out" "$IMAGE" bash /out/_loop.sh | tee "$OUT/_smoke.out" + +"$PY" - "$PORTS" "$OUT" <<'PY' +import json, re, sys +from pathlib import Path +ports, out = Path(sys.argv[1]), Path(sys.argv[2]) +status = {} +for line in (out / "_smoke.out").read_text().splitlines(): + m = re.match(r"SMOKE (\S+) compile=(\S+) battle=(\S+)", line) + if m: + status[m.group(1)] = {"compile": m.group(2), "battle": m.group(3)} +verdict = {} +for slug, s in sorted(status.items()): + scored = False + rf = out / f"{slug}.results" + if rf.exists(): + scored = "MyTank" in rf.read_text() + good = s["compile"] == "ok" and s["battle"] == "ok" and scored + reason = "" + if s["compile"] != "ok": + clog = out / f"{slug}.compile.log" + reason = "compile FAILED: " + (clog.read_text().strip().splitlines()[-1][:120] if clog.exists() else "") + elif s["battle"] != "ok": + reason = f"battle {s['battle']}" + elif not scored: + reason = "no MyTank score in results" + verdict[slug] = {"compile": s["compile"], "battle": s["battle"], "scored": scored, "ok": good, "reason": reason} +(ports / "_stage2.json").write_text(json.dumps(verdict, indent=2, sort_keys=True)) +print("\n=== STAGE 2 (compile + real battle vs sample.Walls) ===") +ok = bad = 0 +for slug in sorted(verdict): + v = verdict[slug]; ok += v["ok"]; bad += not v["ok"] + print(f" {'OK ' if v['ok'] else 'BAD '} {slug}" + ("" if v["ok"] else f" <- {v['reason']}")) +print(f"\nStage 2: {ok} healthy / {bad} problematic of {len(verdict)}") +PY diff --git a/scripts/ladder/stamp_sources.py b/scripts/ladder/stamp_sources.py new file mode 100644 index 00000000..be04a698 --- /dev/null +++ b/scripts/ladder/stamp_sources.py @@ -0,0 +1,233 @@ +"""Stamp source provenance onto every RoboCode port and emit scripts/ladder/SOURCES.md. + +Each bot was imported (copy-in) from an open-source classic-Robocode repo. This records where, +in two consistent places: (1) a header comment prepended to every ports//*.java so the +attribution travels with the bot onto its human/* branch; (2) a committed SOURCES.md table. +Source blob URLs use /blob/HEAD/ (resolves to each repo's default branch). +""" + +import json +from pathlib import Path + +PORTS = Path(__file__).parent / "ports" + +# repo -> (author, license) +REPO_META = { + "robo-code/robocode": ("Robocode (Mathew Nelson / Flemming N. Larsen et al.)", "EPL-1.0"), + "PEZ/Bots": ("PEZ (Peter Strömberg) et al.", "RWPCL"), + "AdmiralRasmussen/robocode": ("collected classic bots (see repo)", "unspecified"), + "namnguyenthanhwork/RoboCode-GuessFactor-MeleeStrategy": ("namnguyenthanhwork", "unspecified"), + "philipmjohnson/robocode-pmj-dacruzer": ("Philip Johnson", "unspecified"), + "mgalushka/robocode-robots": ("Maxim Galushka / CrazyBassoonist et al.", "unspecified"), + "gjgomez/RoboCodeSample": ("gjgomez", "unspecified"), + "johan-adriaans/BerendBotje": ("Johan Adriaans", "unspecified"), + "winstliu/robocode": ("winstliu", "unspecified"), + "pranav-prakash/TheCarverBot": ("Pranav Prakash", "unspecified"), + "JonHarder/RoboCodeCompetition": ("JonHarder", "unspecified"), + "g-otn/robocode-reimu": ("g-otn", "unspecified"), + "TannerRogalsky/Robocode": ("Tanner Rogalsky", "unspecified"), + "txeverson/robocode": ("txeverson", "unspecified"), + "Tibola/robocode": ("Tibola", "unspecified"), + "kylebennett/Robocode": ("Kyle Bennett", "unspecified"), + "linuxuser0/genetic": ("linuxuser0", "unspecified"), + "josephjeon/RoboCode": ("Joseph Jeon", "unspecified"), + "zcjerry229/RoboCode": ("zcjerry229", "unspecified"), + "alpian/robocode": ("alpian", "unspecified"), + "kcanida/robocode-kkc-pikachu": ("kcanida", "unspecified"), + "it-economics/robocode": ("it-economics", "unspecified"), +} + +S = "robocode.samples/src/main/java/sample/" +SEX = "robocode.samples/src/main/java/sampleex/" +# slug -> (repo, primary source path) +SRC = { + # robo-code samples (EPL) + "sittingduck": ("robo-code/robocode", S+"SittingDuck.java"), "walls": ("robo-code/robocode", S+"Walls.java"), + "corners": ("robo-code/robocode", S+"Corners.java"), "ramfire": ("robo-code/robocode", S+"RamFire.java"), + "crazy": ("robo-code/robocode", S+"Crazy.java"), "spinbot": ("robo-code/robocode", S+"SpinBot.java"), + "tracker": ("robo-code/robocode", S+"Tracker.java"), "trackfire": ("robo-code/robocode", S+"TrackFire.java"), + "fire": ("robo-code/robocode", S+"Fire.java"), "myfirstrobot": ("robo-code/robocode", S+"MyFirstRobot.java"), + "velocirobot": ("robo-code/robocode", S+"VelociRobot.java"), + "regullarmonk": ("robo-code/robocode", SEX+"ProxyOfGreyEminence.java"), + # PEZ/Bots (RWPCL) + "aristocles": ("PEZ/Bots", "pez/micro/Aristocles.java"), "blackwidow": ("PEZ/Bots", "pez/micro/BlackWidow.java"), + "pugilist": ("PEZ/Bots", "pez/mini/Pugilist.java"), "gouldingi": ("PEZ/Bots", "pez/mini/Gouldingi.java"), + "leach": ("PEZ/Bots", "pez/mini/Leach.java"), "icarus": ("PEZ/Bots", "pez/nano/Icarus.java"), + "littlebrother": ("PEZ/Bots", "pez/nano/LittleBrother.java"), "tityus": ("PEZ/Bots", "pez/mini/Tityus.java"), + "frankie": ("PEZ/Bots", "pez/frankie/Frankie.java"), "mako": ("PEZ/Bots", "pez/mako/Mako.java"), + "chironex_micro": ("PEZ/Bots", "pez/micro/ChironexFleckeri.java"), "vertimicro": ("PEZ/Bots", "pez/micro/VertiMicro.java"), + "chironex_mini": ("PEZ/Bots", "pez/mini/ChironexFleckeri.java"), "hypoleach": ("PEZ/Bots", "pez/mini/HypoLeach.java"), + "paolo": ("PEZ/Bots", "pez/mini/Paolo.java"), "vertileach": ("PEZ/Bots", "pez/mini/VertiLeach.java"), + "littleevilbrother": ("PEZ/Bots", "pez/nano/LittleEvilBrother.java"), "poet": ("PEZ/Bots", "pez/femto/Poet.java"), + "wallspoet": ("PEZ/Bots", "pez/femto/WallsPoet.java"), "haikuwalls": ("PEZ/Bots", "pez/femto/HaikuWalls.java"), + "droidpoet": ("PEZ/Bots", "pez/femto/DroidPoet.java"), "gf1": ("PEZ/Bots", "pez/etc/GF1.java"), + "leachpmc": ("PEZ/Bots", "pez/etc/LeachPMC.java"), "swiffer": ("PEZ/Bots", "pez/clean/Swiffer.java"), + "smallpoet": ("PEZ/Bots", "pez/femto/SmallPoet.java"), "haikupoet": ("PEZ/Bots", "pez/femto/HaikuPoet.java"), + "blackwidow_mini": ("PEZ/Bots", "pez/mini/BlackWidow.java"), "wallspoetas": ("PEZ/Bots", "pez/femto/WallsPoetAS.java"), + "wallspoethaiku": ("PEZ/Bots", "pez/femto/WallsPoetHaiku.java"), "marshmallow": ("PEZ/Bots", "pez/Marshmallow.java"), + # AdmiralRasmussen bot-collection + "drussgt": ("AdmiralRasmussen/robocode", "bot-collection/DrussGT.java"), + "hawkonfire": ("AdmiralRasmussen/robocode", "bot-collection/HawkOnFireOS.java"), + "wavesurfing": ("AdmiralRasmussen/robocode", "bot-collection/WaveSurfing.java"), + # others + "cham": ("namnguyenthanhwork/RoboCode-GuessFactor-MeleeStrategy", "Cham.java"), + "dacruzer": ("philipmjohnson/robocode-pmj-dacruzer", "src/main/java/pmj/DaCruzer.java"), + "supermercutio": ("mgalushka/robocode-robots", "src/main/java/sample/SuperMercutio.java"), + "supercrazy": ("mgalushka/robocode-robots", "src/main/java/sample/SuperCrazy.java"), + "superwalls": ("mgalushka/robocode-robots", "src/main/java/sample/SuperWalls.java"), + "supertracker": ("mgalushka/robocode-robots", "src/main/java/sample/SuperTracker.java"), + "superspinbot": ("mgalushka/robocode-robots", "src/main/java/sample/SuperSpinBot.java"), + "supercorners": ("mgalushka/robocode-robots", "src/main/java/sample/SuperCorners.java"), + "superramfire": ("mgalushka/robocode-robots", "src/main/java/sample/SuperRamFire.java"), + "maximbot": ("mgalushka/robocode-robots", "src/main/java/com/maximgalushka/robocode/MaximBot.java"), + "mb2": ("gjgomez/RoboCodeSample", "Mb2.java"), + "berendbotje": ("johan-adriaans/BerendBotje", "src/hackersNL/BerendBotje.java"), + "thecarver": ("pranav-prakash/TheCarverBot", "pt/TheCarver.java"), + "bobthebuilder": ("winstliu/robocode", "exam2016/BobTheBuilder.java"), + "starterbot": ("JonHarder/RoboCodeCompetition", "src/starterbot/StarterBot.java"), + "reimu": ("g-otn/robocode-reimu", "src/Alpha/Reimu.java"), + "tannerbot1": ("TannerRogalsky/Robocode", "Tannerbot1.java"), + "crawler": ("txeverson/robocode", "Crawler.java"), + "markiv": ("Tibola/robocode", "miv/MarkIV.java"), + "hugbot": ("kylebennett/Robocode", "BasicRobots/kylebennett/HugBot.java"), + "gruffalo": ("kylebennett/Robocode", "BasicRobots/kylebennett/TheGruffalo.java"), + "genetic": ("linuxuser0/genetic", "Genetic.java"), + "gntest": ("josephjeon/RoboCode", "GNtest.java"), + "markrobo": ("zcjerry229/RoboCode", "src/MarkRobo/MarkRobo.java"), + "ianstank": ("alpian/robocode", "src/main/java/com/github/alpian/robocode/tanks/IansTank.java"), + "tarektank": ("alpian/robocode", "src/main/java/com/github/alpian/robocode/tanks/TarekTank.java"), + "pikachu": ("kcanida/robocode-kkc-pikachu", "src/main/java/kkc/Pikachu.java"), + "ite_bomax": ("it-economics/robocode", "src/main/java/com/ite/robocode/BomaxBot.java"), + "ite_claptrap": ("it-economics/robocode", "src/main/java/com/ite/robocode/ClapTrap.java"), + "ite_cliffbot2": ("it-economics/robocode", "src/main/java/com/ite/robocode/CliffBot2.java"), + "ite_ctbot": ("it-economics/robocode", "src/main/java/com/ite/robocode/CtBot.java"), + "ite_florian2": ("it-economics/robocode", "src/main/java/com/ite/robocode/Florian2Bot.java"), + "ite_m9": ("it-economics/robocode", "src/main/java/com/ite/robocode/MMMMMMMMM.java"), + "ite_simple": ("it-economics/robocode", "src/main/java/com/ite/robocode/SimpleBot.java"), + "ite_terminator": ("it-economics/robocode", "src/main/java/com/ite/robocode/Terminator.java"), +} + +# --- Wave-2 discovery imports (34 more repos) ------------------------------------------------- +for _r in ["TRex22/DeepThought", "zhiwei121/robocode-hero", "muzardo/robocode-trianglehunter", + "joaocarpim/robocode_CPS", "gabriel-lw/QuadWall_robocode", "avsthiago/SadBot-Robocode", + "John-Paul-R/Vergere", "Luke-F-W/NagiSphere-Games-Fleadh-2026", "andrekorol/my-robocode-robots", + "mcd8604/robocode", "LoganCSC/robocode-robots", "sacdalance/robrrrat", "rafaeljdesa/robocode", + "dankraemer/robocode", "iagomonteiro13579/robocode.NPC", "vikdov/RoboCodeEvent", + "denssle/Robocode", "lucasgch/titan", "kinnla/misc", "pmontp19/robocode", + "MiradoConsulting/roleksii", "barriosnahuel/algorithms-robocode-kenchu", "Team488/RobocodeRobots", + "joaomcarvalho/robocode", "WouterJoosse/Robocode", "looklazy/robocode", + "TechnischeInformatica/Robocode2013", "Alexbay218/Robocode-612", "pseminatore/jnk", + "vftheodoro/BarbieScript-RoboCode", "alexjamesmacpherson/robocode", "andr3eee1/Robocode-Nerdvana", + "UR4N0-235/UR4NO", "0x65-e/Kokomo"]: + REPO_META[_r] = (_r.split("/")[0], "unspecified") + +SRC.update({ + "deepthought": ("TRex22/DeepThought", "src/DeepThought.java"), + "hero_pm": ("zhiwei121/robocode-hero", "Hero.java"), + "trianglehunter": ("muzardo/robocode-trianglehunter", "Main.java"), + "wrecker": ("joaocarpim/robocode_CPS", "Wrecker.java"), + "quadwall": ("gabriel-lw/QuadWall_robocode", "quadwall/QuadWall.java"), + "sadbot": ("avsthiago/SadBot-Robocode", "aps_robocode/SadBot.java"), + "vergere": ("John-Paul-R/Vergere", "origin/nano/Vergere.java"), + "nagisphere": ("Luke-F-W/NagiSphere-Games-Fleadh-2026", "Nagisphere.java"), + "myfirstkiller": ("andrekorol/my-robocode-robots", "src/andykbr/MyFirstKiller.java"), + "exterminador": ("andrekorol/my-robocode-robots", "src/univap/ExterminadorPrimeiroGrau.java"), + "oppswantmedead": ("andrekorol/my-robocode-robots", "src/againstallodds/OppsWantMeDead.java"), + "hunter": ("mcd8604/robocode", "src/mcd/Hunter.java"), + "dodgebot2": ("LoganCSC/robocode-robots", "src/com/barrybecker4/DodgeBot2.java"), + "robrrrat": ("sacdalance/robrrrat", "RobrrRat.java"), + "ultron": ("rafaeljdesa/robocode", "Ultron.java"), + "juggernaut": ("dankraemer/robocode", "Juggernaut.java"), + "npcsniper": ("iagomonteiro13579/robocode.NPC", "npc/NPCSniperBot.java"), + "dominatorx": ("vikdov/RoboCodeEvent", "DominatorX.java"), + "megaborsten": ("denssle/Robocode", "MegaBorsten2000.java"), + "bt7274": ("lucasgch/titan", "BT_7274_V6.6.java"), + "antiwalls": ("kinnla/misc", "robocode/AntiWalls.java"), + "propiavancat": ("pmontp19/robocode", "src/robots/PropiAvancat.java"), + "roleksii": ("MiradoConsulting/roleksii", "src/main/java/ROLEKSII.java"), + "tirolio": ("barriosnahuel/algorithms-robocode-kenchu", "src/main/java/pnt/TiroLioYCoshaGolda3.java"), + "meow": ("Team488/RobocodeRobots", "Robots/src/YYK/Meow.java"), + "jeujdapeu": ("joaomcarvalho/robocode", "robots/jeuj/JeujDaPeu.java"), + "infinitylock": ("WouterJoosse/Robocode", "src/wiki/InfinityLock.java"), + "chilibot": ("looklazy/robocode", "src/chili/ChiliBot.java"), + "tearsofsteel": ("TechnischeInformatica/Robocode2013", "src/TearsofSteel.java"), + "shreker": ("Alexbay218/Robocode-612", "robots/Alexbay218/Shreker.java"), + "dodgebot_jnk": ("pseminatore/jnk", "Dodge_Bot.java"), + "barbiescript": ("vftheodoro/BarbieScript-RoboCode", "Source Code BarbieScript/BarbieScript.java"), + "wilde": ("alexjamesmacpherson/robocode", "Wilde.java"), + "npcomplete": ("andr3eee1/Robocode-Nerdvana", "src/npcompute/NPcomplete.java"), + "ur4no": ("UR4N0-235/UR4NO", "UR4NO.java"), + "kokomo": ("0x65-e/Kokomo", "src/california/surf/Kokomo.java"), +}) + +HDR_TAG = "// CodeClash ladder import" + + +def url(repo, path): + return f"https://github.com/{repo}/blob/HEAD/{path}" + + +def header(slug): + repo, path = SRC[slug] + author, lic = REPO_META[repo] + return ( + f"{HDR_TAG}\n" + f"// Source: {url(repo, path)}\n" + f"// Author: {author} License: {lic}\n" + f"// Imported verbatim; only repackaged to the arena package + main class renamed to MyTank" + + (" (+ helper files flattened)." if len(list((PORTS/slug).glob('*.java'))) > 1 else ".") + + "\n" + ) + + +def strip_header(text): + """Remove a previously-stamped CodeClash header block (contiguous leading // lines).""" + lines = text.splitlines(keepends=True) + if not lines or HDR_TAG not in (lines[0] if lines else ""): + return text + i = 0 + while i < len(lines) and lines[i].lstrip().startswith("//"): + i += 1 + return "".join(lines[i:]) + + +def main(): + slugs = sorted(d.name for d in PORTS.iterdir() if d.is_dir() and not d.name.startswith("_")) + missing = [s for s in slugs if s not in SRC] + if missing: + print("WARNING: no source mapping for:", missing) + stamped = 0 + for slug in slugs: + if slug not in SRC: + continue + h = header(slug) + for j in sorted((PORTS / slug).glob("*.java")): + t = strip_header(j.read_text()) # idempotent: drop any prior header, re-stamp + j.write_text(h + t) + stamped += 1 + # SOURCES.md + rows = [] + for slug in slugs: + if slug not in SRC: + continue + repo, path = SRC[slug] + author, lic = REPO_META[repo] + nf = len(list((PORTS / slug).glob("*.java"))) + rows.append(f"| `{slug}` | [{repo}]({url(repo, path)}) | {author} | {lic} | {nf} |") + md = ( + "# RoboCode ladder — bot sources\n\n" + "Every `human/robocode/` bot on `CodeClash-ai/RoboCode` was imported (copy-in) from an\n" + "open-source **classic Robocode** repo below — imported verbatim, only repackaged to\n" + "`package custom;` and the main class renamed to `MyTank` (helper files flattened where needed).\n" + "Each port file also carries this provenance as a header comment. URLs use `/blob/HEAD/`.\n\n" + "| slug | source | author | license | files |\n|---|---|---|---|---|\n" + + "\n".join(rows) + "\n\n" + "Licenses marked *unspecified* had no explicit LICENSE in the source repo; classic RoboWiki\n" + "bots are conventionally under the RWPCL. Verify before any redistribution beyond research use.\n" + ) + (Path(__file__).parent / "SOURCES.md").write_text(md) + print(f"Stamped {stamped} files across {len([s for s in slugs if s in SRC])} bots; wrote SOURCES.md ({len(rows)} rows).") + + +if __name__ == "__main__": + main() diff --git a/scripts/ladder/validate_ports.py b/scripts/ladder/validate_ports.py new file mode 100644 index 00000000..8091c65a --- /dev/null +++ b/scripts/ladder/validate_ports.py @@ -0,0 +1,74 @@ +"""Stage 1 (structural) validator for classic-Robocode ports. No javac here (robocode.jar lives +in the arena image) — the real compile+battle gate is run_smoke_all.sh (stage 2). This checks each +ports// dir: a MyTank.java exists, declares `public class MyTank` extending a classic Robocode +base, has `package custom;` in every .java, and warns on the two things that bite this arena: +the literal token "custom" outside the package line (the harness sed would mangle it) and raw +java.io/threads/reflection (blocked by Robocode's security manager). Writes ports/_stage1.json. +""" + +import json +import re +import sys +from pathlib import Path + +PORTS = Path(__file__).parent / "ports" +BASES = ("AdvancedRobot", "JuniorRobot", "TeamRobot", "RateControlRobot", "Robot") +RAW_IO = (r"\bnew\s+File\b", r"\bFileReader\b", r"\bFileWriter\b", r"\bFileInputStream\b", + r"\bnew\s+Thread\b", r"\bClass\.forName\b", r"java\.lang\.reflect") + + +def check(slug_dir: Path): + warns = [] + javas = sorted(slug_dir.glob("*.java")) + if not javas: + return False, "no .java files", warns + subdirs = [d for d in slug_dir.iterdir() if d.is_dir()] + if subdirs: + return False, f"nested subdirs not allowed (flat glob compile): {[d.name for d in subdirs]}", warns + main = slug_dir / "MyTank.java" + if not main.exists(): + return False, "missing MyTank.java", warns + mtext = main.read_text() + if not re.search(r"\bclass\s+MyTank\b", mtext): + return False, "MyTank.java has no `class MyTank`", warns + if not re.search(r"\bclass\s+MyTank\b[^{]*\bextends\s+\w+", mtext): + return False, "MyTank must `extends` something (a Robocode base, directly or via a helper class)", warns + # A classic Robocode base must appear somewhere in the port (MyTank directly, or a helper it extends). + all_text = "\n".join(j.read_text() for j in javas) + if not re.search(r"\bextends\s+(robocode\.)?(" + "|".join(BASES) + r")\b", all_text): + return False, "no class in the port extends a classic Robocode base (AdvancedRobot/Robot/...)", warns + for j in javas: + t = j.read_text() + if not re.search(r"^\s*package\s+custom\s*;", t, re.M): + return False, f"{j.name} missing `package custom;`", warns + # "custom" outside the package line -> sed-mangle risk + for ln in t.splitlines(): + if "custom" in ln and not re.match(r"\s*package\s+custom\s*;", ln): + warns.append(f"{j.name}: 'custom' token outside package line -> sed risk: {ln.strip()[:60]}") + break + if "dev.robocode.tankroyale" in t: + return False, f"{j.name} uses Tank Royale API (not classic)", warns + for pat in RAW_IO: + if re.search(pat, t): + warns.append(f"{j.name}: uses {pat} — may be blocked by security manager") + break + return True, "ok", warns + + +def main(): + slugs = sorted(d for d in PORTS.iterdir() if d.is_dir() and not d.name.startswith("_")) if PORTS.exists() else [] + results, npass = {}, 0 + for d in slugs: + ok, msg, warns = check(d) + results[d.name] = {"pass": ok, "msg": msg, "warnings": warns} + line = f" {'PASS' if ok else 'FAIL'} {d.name}" + ("" if ok else f" <- {msg}") + print(line) + for w in warns: + print(f" ! {w}") + npass += ok + (PORTS / "_stage1.json").write_text(json.dumps(results, indent=2, sort_keys=True)) + print(f"\nStage 1: {npass} pass / {len(slugs) - npass} fail of {len(slugs)} ports (compile+battle gate is run_smoke_all.sh)") + + +if __name__ == "__main__": + main() From 366177972665fa957c8aedb98fc2a28e11ea34aa Mon Sep 17 00:00:00 2001 From: John Yang Date: Tue, 7 Jul 2026 04:54:56 +0000 Subject: [PATCH 2/5] Fix ladder player names for RoboCode; bump make_robocode sims to 300 `ladder make` set the per-player name to the raw branch_init (e.g. `human/robocode/walls`), which the RoboCode arena turns into an on-disk dir, a Java package via `sed s/custom//`, and a robot-selector token. Slashes broke the sed (terminating the s/// command) and produced invalid robot names, so battles loaded 0 robots and get_results crashed on max() of an empty score dict. Sanitizing to `robocode_` still failed: RoboCode reserves the `robocode` package namespace for its engine, so its robot classloader refuses any robot whose package starts with it (ClassNotFoundException at battle time). Add `_player_slug()` (shared by make + run) that strips the `human/` prefix and a leading path segment equal to the game name, yielding a bare `` package (e.g. `human/robocode/walls` -> `walls`). Non-RoboCode branches (`human//`, `human/`) are unaffected -- their leading segment never matches the game name, so slugs are identical to before. Bump make_robocode.yaml sims_per_round 100 -> 300 for lower Elo variance (~41s/pair solo, ~25s/pair throughput under -w 6; still far under the 5-min/pair budget) and refresh the cost comment with measured numbers. Co-Authored-By: Claude Opus 4.8 (1M context) --- codeclash/cli/ladder.py | 29 ++++++++++++++++++--- configs/ablations/ladder/make_robocode.yaml | 8 +++--- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/codeclash/cli/ladder.py b/codeclash/cli/ladder.py index 80bd3c35..365fabce 100644 --- a/codeclash/cli/ladder.py +++ b/codeclash/cli/ladder.py @@ -18,6 +18,27 @@ logger = get_logger("ladder") +def _player_slug(branch_init: str, game_name: str) -> str: + """Turn a ``human/<...>/`` init branch into a bare, package-safe player name. + + The name is used as an on-disk directory and — in the RoboCode arena — as a Java package name + and robot-selector token (``robots//``, ``sed s/custom//``, + ``selectedRobots=.MyTank``), so it must contain no slashes. RoboCode additionally reserves + the ``robocode`` package namespace for its engine and its robot classloader refuses to load any + robot whose package starts with it, so a leading path segment equal to the game name (RoboCode + branches are ``human/robocode/``) is dropped rather than folded into a ``robocode_`` + token that would raise ClassNotFoundException at battle time. Non-RoboCode branches + (``human//``, ``human/``) are unaffected: their leading segment never matches + the game name, so the result is the same ``_`` slug as before. + """ + parts = [p for p in branch_init.split("/") if p] + if parts and parts[0] == "human": + parts = parts[1:] + if parts and parts[0].lower() == game_name.lower(): + parts = parts[1:] + return "_".join(parts) + + def _resolve_ladder_rules(ladder_rules: dict, rounds: int) -> tuple[float, int]: """Validate the optional ``ladder_rules`` block and return ``(min_round_win_fraction, win_last_k)``. @@ -83,15 +104,17 @@ def make( players = config["players"] num_players = len(players) + game_name = config["game"]["name"] + # Build one fully independent (deep-copied) config per pair up front so concurrent runs # never share or mutate the same player/config dicts. jobs: list[tuple[dict, Path]] = [] for i in range(num_players): for j in range(i + 1, num_players): player1 = copy.deepcopy(players[i]) - player1["name"] = player1["branch_init"] + player1["name"] = _player_slug(player1["branch_init"], game_name) player2 = copy.deepcopy(players[j]) - player2["name"] = player2["branch_init"] + player2["name"] = _player_slug(player2["branch_init"], game_name) pvp_config = {**copy.deepcopy(config), "players": [player1, player2]} vs = f"PvpTournament.{player1['name']}_vs_{player2['name']}".replace("/", "_") output_dir = LOCAL_LOG_DIR / "ladder" / config["game"]["name"] / vs @@ -157,7 +180,7 @@ def run( for idx, opponent in enumerate(ladder): opponent_rank = len(ladder) - idx - opponent["name"] = opponent["branch_init"].replace("human/", "").replace("/", "_") + opponent["name"] = _player_slug(opponent["branch_init"], config["game"]["name"]) if "branch_init" in player and idx > 0: # After first opponent, remove branch_init so that player continues from previous tournament's codebase del player["branch_init"] diff --git a/configs/ablations/ladder/make_robocode.yaml b/configs/ablations/ladder/make_robocode.yaml index c815c880..7191d298 100644 --- a/configs/ablations/ladder/make_robocode.yaml +++ b/configs/ablations/ladder/make_robocode.yaml @@ -3,14 +3,16 @@ # Elo/Bradley-Terry -> see robocode.yaml. rounds:0 = dummy players play baseline battles (no code # edits). N bots -> N*(N-1)/2 pairs; each pair plays sims_per_round rounds (batched 10 at a time, # sim_concurrency parallel). Bots live on CodeClash-ai/RoboCode human/* branches; Docker required. -# COST (measured ~3.4s for a 50-round pair headless): at sims_per_round=100, 116 bots -> 6670 pairs -# ~= 13 core-hours -> ~2 h on a laptop (--workers 6), ~26 min on a 32-core box (-w 30). +# COST (measured on an 8-core box: a pair is ~11.8s fixed overhead + ~9.7s per 100 sims, so at +# sims_per_round=300 a single pair is ~41s solo and ~25s/pair of throughput under --workers 6). +# 116 bots -> 6670 pairs -> ~46 core-hours -> ~2 days on an 8-core box (-w 6). Resumable: skips +# already-logged pairs, so a killed run can be re-launched. Scale --workers with cores (~cores-2). # Run: uv run codeclash ladder make configs/ablations/ladder/make_robocode.yaml --workers 6 tournament: rounds: 0 game: name: RoboCode - sims_per_round: 100 + sims_per_round: 300 sim_concurrency: 5 args: nodisplay: true From 7a6feb6bfa26bf8ad5c3374068ef65bbf611d1ae Mon Sep 17 00:00:00 2001 From: John Yang Date: Tue, 7 Jul 2026 05:29:09 +0000 Subject: [PATCH 3/5] Disable replay recording in make_robocode (record_ratio: 0) The round-robin only needs per-battle scores to fit Elo; recording every sim wrote ~8.7MB of compressed replay XML per pair (~59GB over 6670 pairs, risking disk exhaustion mid-run) and pinned a gzip core on each log copy. Scores come from results_*.txt, which is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) --- configs/ablations/ladder/make_robocode.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configs/ablations/ladder/make_robocode.yaml b/configs/ablations/ladder/make_robocode.yaml index 7191d298..860f2fe1 100644 --- a/configs/ablations/ladder/make_robocode.yaml +++ b/configs/ablations/ladder/make_robocode.yaml @@ -14,6 +14,10 @@ game: name: RoboCode sims_per_round: 300 sim_concurrency: 5 + # Ranking only needs per-battle scores (results_*.txt), not replays. Recording every sim writes + # ~8.7MB of compressed replay XML per pair (~59GB over 6670 pairs) and pins a gzip core on every + # log copy, so disable it here. + record_ratio: 0 args: nodisplay: true nosound: true From d590a9a8e66c96ed809bdbe32391bf14943f1d79 Mon Sep 17 00:00:00 2001 From: John Yang Date: Tue, 7 Jul 2026 21:10:25 +0000 Subject: [PATCH 4/5] Fix "/" bug in agent naming --- codeclash/cli/ladder.py | 36 ++--- configs/ladder/make_robocode.yaml | 232 +++++++++++++++--------------- 2 files changed, 129 insertions(+), 139 deletions(-) diff --git a/codeclash/cli/ladder.py b/codeclash/cli/ladder.py index 3177d4f3..bc2f3c04 100644 --- a/codeclash/cli/ladder.py +++ b/codeclash/cli/ladder.py @@ -19,25 +19,17 @@ logger = get_logger("ladder") -def _player_slug(branch_init: str, game_name: str) -> str: - """Turn a ``human/<...>/`` init branch into a bare, package-safe player name. - - The name is used as an on-disk directory and — in the RoboCode arena — as a Java package name - and robot-selector token (``robots//``, ``sed s/custom//``, - ``selectedRobots=.MyTank``), so it must contain no slashes. RoboCode additionally reserves - the ``robocode`` package namespace for its engine and its robot classloader refuses to load any - robot whose package starts with it, so a leading path segment equal to the game name (RoboCode - branches are ``human/robocode/``) is dropped rather than folded into a ``robocode_`` - token that would raise ClassNotFoundException at battle time. Non-RoboCode branches - (``human//``, ``human/``) are unaffected: their leading segment never matches - the game name, so the result is the same ``_`` slug as before. +def _player_slug(branch_init: str) -> str: + """Turn a ``human//`` init branch into a bare, filesystem-safe player name: + strip the ``human/`` prefix and join the rest with ``_`` (e.g. ``human/aleksiy325/snek-two`` + -> ``aleksiy325_snek-two``). + + RoboCode also uses this name as a Java *package* (``.MyTank``), which must be a bare + identifier that does not start with the engine's reserved ``robocode`` namespace. The RoboCode + ladder branches are named so the plain slug already satisfies that (``human/robo_code/walls`` + -> ``robo_code__walls``), so no game-specific handling is needed here. """ - parts = [p for p in branch_init.split("/") if p] - if parts and parts[0] == "human": - parts = parts[1:] - if parts and parts[0].lower() == game_name.lower(): - parts = parts[1:] - return "_".join(parts) + return branch_init.replace("human/", "").replace("/", "__") def _resolve_ladder_rules(ladder_rules: dict, rounds: int) -> tuple[int, int]: @@ -115,17 +107,15 @@ def make( players = config["players"] num_players = len(players) - game_name = config["game"]["name"] - # Build one fully independent (deep-copied) config per pair up front so concurrent runs # never share or mutate the same player/config dicts. jobs: list[tuple[dict, Path]] = [] for i in range(num_players): for j in range(i + 1, num_players): player1 = copy.deepcopy(players[i]) - player1["name"] = _player_slug(player1["branch_init"], game_name) + player1["name"] = _player_slug(player1["branch_init"]) player2 = copy.deepcopy(players[j]) - player2["name"] = _player_slug(player2["branch_init"], game_name) + player2["name"] = _player_slug(player2["branch_init"]) pvp_config = {**copy.deepcopy(config), "players": [player1, player2]} vs = f"PvpTournament.{player1['name']}_vs_{player2['name']}".replace("/", "_") output_dir = LOCAL_LOG_DIR / "ladder" / config["game"]["name"] / vs @@ -196,7 +186,7 @@ def run( advanced = False for idx, opponent in enumerate(ladder): opponent_rank = len(ladder) - idx - opponent["name"] = _player_slug(opponent["branch_init"], config["game"]["name"]) + opponent["name"] = _player_slug(opponent["branch_init"]) if "branch_init" in player and idx > 0: # After first opponent, remove branch_init so that player continues from previous tournament's codebase del player["branch_init"] diff --git a/configs/ladder/make_robocode.yaml b/configs/ladder/make_robocode.yaml index a1d72710..c44fdef9 100644 --- a/configs/ladder/make_robocode.yaml +++ b/configs/ladder/make_robocode.yaml @@ -23,236 +23,236 @@ game: nosound: true players: - agent: dummy - branch_init: human/robocode/antiwalls + branch_init: human/kinnla/antiwalls - agent: dummy - branch_init: human/robocode/aristocles + branch_init: human/pez/aristocles - agent: dummy - branch_init: human/robocode/barbiescript + branch_init: human/vftheodoro/barbiescript - agent: dummy - branch_init: human/robocode/berendbotje + branch_init: human/johan_adriaans/berendbotje - agent: dummy - branch_init: human/robocode/blackwidow + branch_init: human/pez/blackwidow - agent: dummy - branch_init: human/robocode/blackwidow_mini + branch_init: human/pez/blackwidow_mini - agent: dummy - branch_init: human/robocode/bobthebuilder + branch_init: human/winstliu/bobthebuilder - agent: dummy - branch_init: human/robocode/bt7274 + branch_init: human/lucasgch/bt7274 - agent: dummy - branch_init: human/robocode/cham + branch_init: human/namnguyenthanhwork/cham - agent: dummy - branch_init: human/robocode/chilibot + branch_init: human/looklazy/chilibot - agent: dummy - branch_init: human/robocode/chironex_micro + branch_init: human/pez/chironex_micro - agent: dummy - branch_init: human/robocode/chironex_mini + branch_init: human/pez/chironex_mini - agent: dummy - branch_init: human/robocode/corners + branch_init: human/robo_code/corners - agent: dummy - branch_init: human/robocode/crawler + branch_init: human/txeverson/crawler - agent: dummy - branch_init: human/robocode/crazy + branch_init: human/robo_code/crazy - agent: dummy - branch_init: human/robocode/dacruzer + branch_init: human/philipmjohnson/dacruzer - agent: dummy - branch_init: human/robocode/deepthought + branch_init: human/trex22/deepthought - agent: dummy - branch_init: human/robocode/dodgebot_jnk + branch_init: human/pseminatore/dodgebot_jnk - agent: dummy - branch_init: human/robocode/dodgebot2 + branch_init: human/logancsc/dodgebot2 - agent: dummy - branch_init: human/robocode/dominatorx + branch_init: human/vikdov/dominatorx - agent: dummy - branch_init: human/robocode/droidpoet + branch_init: human/pez/droidpoet - agent: dummy - branch_init: human/robocode/drussgt + branch_init: human/admiralrasmussen/drussgt - agent: dummy - branch_init: human/robocode/exterminador + branch_init: human/andrekorol/exterminador - agent: dummy - branch_init: human/robocode/fire + branch_init: human/robo_code/fire - agent: dummy - branch_init: human/robocode/frankie + branch_init: human/pez/frankie - agent: dummy - branch_init: human/robocode/genetic + branch_init: human/linuxuser0/genetic - agent: dummy - branch_init: human/robocode/gf1 + branch_init: human/pez/gf1 - agent: dummy - branch_init: human/robocode/gntest + branch_init: human/josephjeon/gntest - agent: dummy - branch_init: human/robocode/gouldingi + branch_init: human/pez/gouldingi - agent: dummy - branch_init: human/robocode/gruffalo + branch_init: human/kylebennett/gruffalo - agent: dummy - branch_init: human/robocode/haikupoet + branch_init: human/pez/haikupoet - agent: dummy - branch_init: human/robocode/haikuwalls + branch_init: human/pez/haikuwalls - agent: dummy - branch_init: human/robocode/hawkonfire + branch_init: human/admiralrasmussen/hawkonfire - agent: dummy - branch_init: human/robocode/hero_pm + branch_init: human/zhiwei121/hero_pm - agent: dummy - branch_init: human/robocode/hugbot + branch_init: human/kylebennett/hugbot - agent: dummy - branch_init: human/robocode/hunter + branch_init: human/mcd8604/hunter - agent: dummy - branch_init: human/robocode/hypoleach + branch_init: human/pez/hypoleach - agent: dummy - branch_init: human/robocode/ianstank + branch_init: human/alpian/ianstank - agent: dummy - branch_init: human/robocode/icarus + branch_init: human/pez/icarus - agent: dummy - branch_init: human/robocode/infinitylock + branch_init: human/wouterjoosse/infinitylock - agent: dummy - branch_init: human/robocode/ite_bomax + branch_init: human/it_economics/ite_bomax - agent: dummy - branch_init: human/robocode/ite_claptrap + branch_init: human/it_economics/ite_claptrap - agent: dummy - branch_init: human/robocode/ite_cliffbot2 + branch_init: human/it_economics/ite_cliffbot2 - agent: dummy - branch_init: human/robocode/ite_ctbot + branch_init: human/it_economics/ite_ctbot - agent: dummy - branch_init: human/robocode/ite_florian2 + branch_init: human/it_economics/ite_florian2 - agent: dummy - branch_init: human/robocode/ite_m9 + branch_init: human/it_economics/ite_m9 - agent: dummy - branch_init: human/robocode/ite_simple + branch_init: human/it_economics/ite_simple - agent: dummy - branch_init: human/robocode/ite_terminator + branch_init: human/it_economics/ite_terminator - agent: dummy - branch_init: human/robocode/jeujdapeu + branch_init: human/joaomcarvalho/jeujdapeu - agent: dummy - branch_init: human/robocode/juggernaut + branch_init: human/dankraemer/juggernaut - agent: dummy - branch_init: human/robocode/kokomo + branch_init: human/u_0x65_e/kokomo - agent: dummy - branch_init: human/robocode/leach + branch_init: human/pez/leach - agent: dummy - branch_init: human/robocode/leachpmc + branch_init: human/pez/leachpmc - agent: dummy - branch_init: human/robocode/littlebrother + branch_init: human/pez/littlebrother - agent: dummy - branch_init: human/robocode/littleevilbrother + branch_init: human/pez/littleevilbrother - agent: dummy - branch_init: human/robocode/mako + branch_init: human/pez/mako - agent: dummy - branch_init: human/robocode/markiv + branch_init: human/tibola/markiv - agent: dummy - branch_init: human/robocode/markrobo + branch_init: human/zcjerry229/markrobo - agent: dummy - branch_init: human/robocode/marshmallow + branch_init: human/pez/marshmallow - agent: dummy - branch_init: human/robocode/maximbot + branch_init: human/mgalushka/maximbot - agent: dummy - branch_init: human/robocode/mb2 + branch_init: human/gjgomez/mb2 - agent: dummy - branch_init: human/robocode/megaborsten + branch_init: human/denssle/megaborsten - agent: dummy - branch_init: human/robocode/meow + branch_init: human/team488/meow - agent: dummy - branch_init: human/robocode/myfirstkiller + branch_init: human/andrekorol/myfirstkiller - agent: dummy - branch_init: human/robocode/myfirstrobot + branch_init: human/robo_code/myfirstrobot - agent: dummy - branch_init: human/robocode/nagisphere + branch_init: human/luke_f_w/nagisphere - agent: dummy - branch_init: human/robocode/npcomplete + branch_init: human/andr3eee1/npcomplete - agent: dummy - branch_init: human/robocode/npcsniper + branch_init: human/iagomonteiro13579/npcsniper - agent: dummy - branch_init: human/robocode/oppswantmedead + branch_init: human/andrekorol/oppswantmedead - agent: dummy - branch_init: human/robocode/paolo + branch_init: human/pez/paolo - agent: dummy - branch_init: human/robocode/pikachu + branch_init: human/kcanida/pikachu - agent: dummy - branch_init: human/robocode/poet + branch_init: human/pez/poet - agent: dummy - branch_init: human/robocode/propiavancat + branch_init: human/pmontp19/propiavancat - agent: dummy - branch_init: human/robocode/pugilist + branch_init: human/pez/pugilist - agent: dummy - branch_init: human/robocode/quadwall + branch_init: human/gabriel_lw/quadwall - agent: dummy - branch_init: human/robocode/ramfire + branch_init: human/robo_code/ramfire - agent: dummy - branch_init: human/robocode/regullarmonk + branch_init: human/robo_code/regullarmonk - agent: dummy - branch_init: human/robocode/reimu + branch_init: human/g_otn/reimu - agent: dummy - branch_init: human/robocode/robrrrat + branch_init: human/sacdalance/robrrrat - agent: dummy - branch_init: human/robocode/roleksii + branch_init: human/miradoconsulting/roleksii - agent: dummy - branch_init: human/robocode/sadbot + branch_init: human/avsthiago/sadbot - agent: dummy - branch_init: human/robocode/shreker + branch_init: human/alexbay218/shreker - agent: dummy - branch_init: human/robocode/sittingduck + branch_init: human/robo_code/sittingduck - agent: dummy - branch_init: human/robocode/smallpoet + branch_init: human/pez/smallpoet - agent: dummy - branch_init: human/robocode/spinbot + branch_init: human/robo_code/spinbot - agent: dummy - branch_init: human/robocode/starterbot + branch_init: human/jonharder/starterbot - agent: dummy - branch_init: human/robocode/supercorners + branch_init: human/mgalushka/supercorners - agent: dummy - branch_init: human/robocode/supercrazy + branch_init: human/mgalushka/supercrazy - agent: dummy - branch_init: human/robocode/supermercutio + branch_init: human/mgalushka/supermercutio - agent: dummy - branch_init: human/robocode/superramfire + branch_init: human/mgalushka/superramfire - agent: dummy - branch_init: human/robocode/superspinbot + branch_init: human/mgalushka/superspinbot - agent: dummy - branch_init: human/robocode/supertracker + branch_init: human/mgalushka/supertracker - agent: dummy - branch_init: human/robocode/superwalls + branch_init: human/mgalushka/superwalls - agent: dummy - branch_init: human/robocode/swiffer + branch_init: human/pez/swiffer - agent: dummy - branch_init: human/robocode/tannerbot1 + branch_init: human/tannerrogalsky/tannerbot1 - agent: dummy - branch_init: human/robocode/tarektank + branch_init: human/alpian/tarektank - agent: dummy - branch_init: human/robocode/tearsofsteel + branch_init: human/technischeinformatica/tearsofsteel - agent: dummy - branch_init: human/robocode/thecarver + branch_init: human/pranav_prakash/thecarver - agent: dummy - branch_init: human/robocode/tirolio + branch_init: human/barriosnahuel/tirolio - agent: dummy - branch_init: human/robocode/tityus + branch_init: human/pez/tityus - agent: dummy - branch_init: human/robocode/tracker + branch_init: human/robo_code/tracker - agent: dummy - branch_init: human/robocode/trackfire + branch_init: human/robo_code/trackfire - agent: dummy - branch_init: human/robocode/trianglehunter + branch_init: human/muzardo/trianglehunter - agent: dummy - branch_init: human/robocode/ultron + branch_init: human/rafaeljdesa/ultron - agent: dummy - branch_init: human/robocode/ur4no + branch_init: human/ur4n0_235/ur4no - agent: dummy - branch_init: human/robocode/velocirobot + branch_init: human/robo_code/velocirobot - agent: dummy - branch_init: human/robocode/vergere + branch_init: human/john_paul_r/vergere - agent: dummy - branch_init: human/robocode/vertileach + branch_init: human/pez/vertileach - agent: dummy - branch_init: human/robocode/vertimicro + branch_init: human/pez/vertimicro - agent: dummy - branch_init: human/robocode/walls + branch_init: human/robo_code/walls - agent: dummy - branch_init: human/robocode/wallspoet + branch_init: human/pez/wallspoet - agent: dummy - branch_init: human/robocode/wallspoetas + branch_init: human/pez/wallspoetas - agent: dummy - branch_init: human/robocode/wallspoethaiku + branch_init: human/pez/wallspoethaiku - agent: dummy - branch_init: human/robocode/wavesurfing + branch_init: human/admiralrasmussen/wavesurfing - agent: dummy - branch_init: human/robocode/wilde + branch_init: human/alexjamesmacpherson/wilde - agent: dummy - branch_init: human/robocode/wrecker + branch_init: human/joaocarpim/wrecker prompts: game_description: RoboCode ladder From 2d9df376b04000d963a32c0af752c76ca9a44329 Mon Sep 17 00:00:00 2001 From: John Yang Date: Tue, 7 Jul 2026 22:26:39 +0000 Subject: [PATCH 5/5] nit --- codeclash/tournaments/ladder.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/codeclash/tournaments/ladder.py b/codeclash/tournaments/ladder.py index c4dd66c8..95ad7c32 100644 --- a/codeclash/tournaments/ladder.py +++ b/codeclash/tournaments/ladder.py @@ -28,14 +28,10 @@ def _player_slug(branch_init: str) -> str: - """Turn a ``human//`` init branch into a bare, filesystem-safe player name: + """ + Turn a ``human//`` init branch into a bare, filesystem-safe player name: strip the ``human/`` prefix and join the rest with ``__`` (e.g. ``human/aleksiy325/snek-two`` -> ``aleksiy325__snek-two``). - - RoboCode also uses this name as a Java *package* (``.MyTank``), which must be a bare - identifier that does not start with the engine's reserved ``robocode`` namespace. The RoboCode - ladder branches are named so the plain slug already satisfies that (``human/robo_code/walls`` - -> ``robo_code__walls``), so no game-specific handling is needed here. """ return branch_init.replace("human/", "").replace("/", "__")