The pod was CrashLoopBackOff for 45 days because it was running an outdated image without the LIMIT clause fixes added in June. Updated to the latest image digest which includes: - LIMIT on fetchSeriesGames query ( |
||
|---|---|---|
| .github/workflows | ||
| .wrangler/cache | ||
| bin | ||
| bots | ||
| cmd | ||
| docs | ||
| engine | ||
| manifests | ||
| maps | ||
| metrics | ||
| migrations | ||
| notes | ||
| ratelimit | ||
| scripts | ||
| starters | ||
| wasm | ||
| web | ||
| .build-trigger | ||
| .env.example | ||
| .gitignore | ||
| .needle-predispatch-sha | ||
| .trigger-build | ||
| acb-local-fixed | ||
| acb-local-test | ||
| acb-map-evolver | ||
| acb-maps-loader | ||
| arena.test | ||
| DEPLOYMENT.md | ||
| DEPLOYMENT_STEPS.md | ||
| docker-compose.bots.yml | ||
| docker-compose.workers.yml | ||
| fix-iad-acb-openbao.sh | ||
| fix-iad-acb-r2-credentials.sh | ||
| go.mod | ||
| go.sum | ||
| IAD-ACB-OPENBAO-FIX.md | ||
| IAD-ACB-R2-CREDENTIALS-FIX.md | ||
| Makefile | ||
| MATCH_LIST_TEST_RESULTS.md | ||
| MATCH_LIST_VERIFICATION_SUMMARY.md | ||
| PROGRESS.md | ||
| R2_ACCESS_KEY_SOURCE.md | ||
| README.md | ||
| REPLAY_VIEWER_TEST_RESULTS.md | ||
| REPLAY_VIEWER_VERIFICATION_SUMMARY.md | ||
| test-combat.json | ||
| test-siege-arena.sh | ||
| test-swarm-rusher.json | ||
| test_routes.sh | ||
| TRIGGER.md | ||
| wrangler.toml | ||
AI Code Battle
A competitive bot programming platform where participants write HTTP servers that control units on a grid world. Bots compete in real-time matches, and the winner is the last player with surviving cores.
Overview
- Write a bot in any language — it just needs to run an HTTP server
- The game engine calls your bot every turn with the current game state
- Your bot responds with move orders for each of its units
- Matches run offline; results are published as animated replays with a leaderboard
- Ratings use Glicko-2 (same system as chess.com)
Table of Contents
- Game Rules
- Writing a Bot
- Starter Templates
- Strategy Bots
- Running Locally
- Project Structure
- Architecture
- Testing
Game Rules
The Grid World
Matches are played on a toroidal (wrapping) grid — moving off one edge brings you out the other side. Tiles:
| Tile | Symbol | Effect |
|---|---|---|
| Open | . |
Units can occupy and traverse |
| Wall | # |
Impassable |
| Energy | * |
Collected by units standing on it |
| Core | C |
Win condition — protect yours, destroy theirs |
Win Condition
The last player with at least one surviving Core tile wins. Cores are destroyed when an enemy unit occupies an undefended core tile during the Capture phase.
Turn Phases
Each turn resolves in this order:
- Move — Each unit executes its ordered direction (
N,E,S,W) or stays - Combat (focus-fire) — Units in the same tile deal damage; outnumbered units take extra damage
- Zone — A shrinking storm closes in from the edges, dealing damage to units caught in it
- Capture — Enemy units on undefended Core tiles raze them
- Collect — Units on Energy tiles collect energy for their player
- Spawn — Players spend energy to spawn new units at Core tiles
- Energy Tick — Passive energy regeneration
- Endgame Check — Victory condition evaluated
Key Rules
- Self-collision: If two or more of your own units move to the same tile, they all die
- Energy economy: Spawning units costs energy; energy is collected from
*tiles and regenerated passively - Visibility: Bots only see tiles and units within their units' sight radius (fog of war)
Writing a Bot
A bot is an HTTP server with two endpoints. The game engine calls your server every turn.
Required Endpoints
GET /health
Returns 200 OK when your bot is ready. The engine polls this before starting a match.
GET /health HTTP/1.1
HTTP/1.1 200 OK
POST /turn
The engine sends the current game state; your bot responds with move orders.
Request headers:
| Header | Value |
|---|---|
Content-Type |
application/json |
X-ACB-Match-Id |
Match identifier string |
X-ACB-Turn |
Current turn number (integer as string) |
X-ACB-Signature |
HMAC-SHA256 signature of this request |
Request body (JSON game state):
{
"turn": 42,
"player_id": "player-1",
"bots": [
{
"id": 1,
"x": 5,
"y": 3,
"health": 100,
"owner": "player-1"
}
],
"tiles": [
{ "x": 4, "y": 3, "type": "open" },
{ "x": 5, "y": 3, "type": "energy" }
],
"energy_locations": [{ "x": 5, "y": 3 }],
"core_locations": [
{ "x": 0, "y": 0, "owner": "player-1" },
{ "x": 9, "y": 9, "owner": "player-2" }
],
"scores": {
"player-1": { "energy": 150, "cores": 2 },
"player-2": { "energy": 80, "cores": 1 }
}
}
Only tiles and units visible to your bots are included (fog of war).
Response body (JSON move orders):
{
"moves": [
{ "bot_id": 1, "direction": "N" },
{ "bot_id": 2, "direction": "E" },
{ "bot_id": 3, "direction": "stay" }
]
}
Valid directions: N, E, S, W, stay
Response header:
| Header | Value |
|---|---|
X-ACB-Signature |
HMAC-SHA256 signature of your response body |
HMAC Authentication
Every request and response is signed. The signing key is your SHARED_SECRET environment variable.
Signing string format:
{match_id}.{turn}.{sha256_hex(body)}
Signature:
HMAC-SHA256(key=SHARED_SECRET, message=signing_string) → hex-encoded
To verify an incoming request:
- Read
X-ACB-Match-Id,X-ACB-Turn, and the raw request body - Compute
sha256(body)as hex - Build the signing string:
"{match_id}.{turn}.{body_sha256}" - Compute
HMAC-SHA256(SHARED_SECRET, signing_string)as hex - Compare with
X-ACB-Signature(constant-time comparison)
To sign your response:
- Compute
sha256(response_body)as hex - Build the same signing string with the same
match_idandturn - Compute
HMAC-SHA256(SHARED_SECRET, signing_string)as hex - Set
X-ACB-Signatureresponse header to that value
Environment Variables
| Variable | Description |
|---|---|
SHARED_SECRET |
Shared HMAC key provided by the match coordinator |
PORT |
Port your HTTP server listens on (default: 8080) |
Starter Templates
Ready-to-use templates are in the starters/ directory. Each implements /health and /turn with HMAC auth and a random-move strategy — replace the move logic with your own.
| Language | Directory |
|---|---|
| Python | starters/python/ |
| Go | starters/go/ |
| Rust | starters/rust/ |
| TypeScript | starters/typescript/ |
| JavaScript | starters/javascript/ |
| Java | starters/java/ |
| PHP | starters/php/ |
| C# | starters/csharp/ |
Quick-start with Python
cd starters/python
pip install -r requirements.txt
SHARED_SECRET=test PORT=8080 python main.py
Then in another terminal, test your bot health:
curl http://localhost:8080/health
Strategy Bots
The bots/ directory contains 21 reference bots demonstrating different strategies. These serve as benchmarks and opponents in the automated ladder.
| Bot | Strategy |
|---|---|
random |
Random valid moves — baseline reference |
gatherer |
Energy collection priority, avoids combat |
rusher |
Rushes enemy cores aggressively |
guardian |
Defends cores, cautious expansion |
swarm |
Formation cohesion, advances as a group |
hunter |
Targets isolated enemy units |
assassin |
Prioritizes high-value targets |
coordinator |
Coordinates unit actions across the field |
defender |
Pure defensive posture |
economist |
Maximizes energy efficiency |
farmer |
Long-term energy farming strategy |
kamikaze |
Sacrificial rush tactics |
leader-targeter |
Focuses fire on the strongest opponent |
nomad |
Mobile, avoids prolonged engagements |
opportunist |
Exploits momentary advantages |
pacifist |
Avoids combat entirely |
phalanx |
Tight defensive formation |
raider |
Hit-and-run energy denial |
scout |
Prioritizes map exploration |
siege |
Methodical core destruction |
zone-driver |
Exploits the shrinking zone mechanic |
Bot implementations span Go, Rust, Python, TypeScript, PHP, and Java.
Bot Evolver
The acb-evolver/ component mutates and evolves bot strategies automatically. It runs genetic-algorithm-style evolution over strategy parameters, producing improved variants over time.
Running Locally
Prerequisites
- Go 1.21+ (game engine and CLI tools)
- Node.js 18+ (web app and worker API)
- Docker (for containerized bots and deployment)
Build and Run a Match
# Build CLI tools
go build ./cmd/acb-local
go build ./cmd/acb-mapgen
# Run a match between two built-in bots (outputs replay JSON)
./acb-local -seed 42 -max-turns 100 -output replay.json -verbose
Test Your Bot
# Start the Python starter bot
cd starters/python
pip install -r requirements.txt
SHARED_SECRET=test PORT=8080 python main.py
# In another terminal — run a match against a built-in bot
./acb-local -bot1 http://localhost:8080 -bot2 builtin:rusher \
-secret1 test -seed 42 -output replay.json -verbose
View Replays
# Start the web dev server
cd web && npm install && npm run dev
# Open http://localhost:3000/ → Replay Viewer → load replay.json
The replay viewer shows turn-by-turn animation on a canvas, win probability tracking, an event timeline, and auto-generated commentary.
Project Structure
ai-code-battle/
├── engine/ # Go game simulation library
│ ├── types.go # Core data types
│ ├── grid.go # Toroidal grid implementation
│ ├── game.go # Game state management
│ ├── turn.go # Turn execution phases
│ ├── replay.go # Replay recording
│ └── *_test.go # Test files
├── cmd/
│ ├── acb-local/ # CLI match runner (local testing)
│ ├── acb-mapgen/ # Map generator
│ ├── acb-worker/ # Match execution worker
│ └── acb-indexer/ # Index builder for static files
├── web/ # Cloudflare Pages SPA
│ └── src/
│ ├── replay-viewer.ts # Canvas replay renderer
│ └── app.ts # SPA entry point
├── worker-api/ # Cloudflare Worker API
│ └── src/
│ ├── index.ts # Router + cron dispatcher
│ ├── jobs.ts # Job coordination
│ ├── bots.ts # Bot management
│ └── glicko2.ts # Glicko-2 rating system
├── bots/ # Strategy bot implementations (21 bots)
├── starters/ # Starter templates (8 languages)
│ ├── python/
│ ├── go/
│ ├── rust/
│ ├── typescript/
│ ├── javascript/
│ ├── java/
│ ├── php/
│ └── csharp/
└── acb-evolver/ # Bot evolution engine
Architecture
The platform is split between Cloudflare's edge network and a cloud VM:
- Cloudflare Pages — Static SPA (replay viewer, leaderboard, match history)
- Cloudflare Workers + D1 + R2 — Match coordination API, bot registry, rating updates, replay storage
- Cloud VM — Match workers that pull jobs from the API, run bots as containers, and publish replays
Match flow:
- Worker API schedules match jobs
- Match worker pulls a job and starts both bot containers
- Engine runs the match turn-by-turn, calling each bot's
/turnendpoint - Replay JSON is uploaded to R2
- Worker API updates Glicko-2 ratings and triggers index rebuild
- Web frontend fetches the new replay and displays it
Rating system: Glicko-2 — accounts for rating reliability (RD) and rating volatility, converges faster than Elo, same algorithm used by chess.com.
Testing
# Game engine unit tests
go test ./engine/... -v
# Worker API tests
cd worker-api && npm test
# Index builder tests
cd cmd/acb-indexer && npm test
License
MIT