376 lines
11 KiB
Markdown
376 lines
11 KiB
Markdown
# 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
|
|
|
|
1. [Game Rules](#game-rules)
|
|
2. [Writing a Bot](#writing-a-bot)
|
|
3. [Starter Templates](#starter-templates)
|
|
4. [Strategy Bots](#strategy-bots)
|
|
5. [Running Locally](#running-locally)
|
|
6. [Project Structure](#project-structure)
|
|
7. [Architecture](#architecture)
|
|
8. [Testing](#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:
|
|
|
|
1. **Move** — Each unit executes its ordered direction (`N`, `E`, `S`, `W`) or stays
|
|
2. **Combat (focus-fire)** — Units in the same tile deal damage; outnumbered units take extra damage
|
|
3. **Zone** — A shrinking storm closes in from the edges, dealing damage to units caught in it
|
|
4. **Capture** — Enemy units on undefended Core tiles raze them
|
|
5. **Collect** — Units on Energy tiles collect energy for their player
|
|
6. **Spawn** — Players spend energy to spawn new units at Core tiles
|
|
7. **Energy Tick** — Passive energy regeneration
|
|
8. **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):
|
|
|
|
```json
|
|
{
|
|
"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):
|
|
|
|
```json
|
|
{
|
|
"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:**
|
|
|
|
1. Read `X-ACB-Match-Id`, `X-ACB-Turn`, and the raw request body
|
|
2. Compute `sha256(body)` as hex
|
|
3. Build the signing string: `"{match_id}.{turn}.{body_sha256}"`
|
|
4. Compute `HMAC-SHA256(SHARED_SECRET, signing_string)` as hex
|
|
5. Compare with `X-ACB-Signature` (constant-time comparison)
|
|
|
|
**To sign your response:**
|
|
|
|
1. Compute `sha256(response_body)` as hex
|
|
2. Build the same signing string with the same `match_id` and `turn`
|
|
3. Compute `HMAC-SHA256(SHARED_SECRET, signing_string)` as hex
|
|
4. Set `X-ACB-Signature` response 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
|
|
|
|
```bash
|
|
cd starters/python
|
|
pip install -r requirements.txt
|
|
SHARED_SECRET=test PORT=8080 python main.py
|
|
```
|
|
|
|
Then in another terminal, test your bot health:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
1. Worker API schedules match jobs
|
|
2. Match worker pulls a job and starts both bot containers
|
|
3. Engine runs the match turn-by-turn, calling each bot's `/turn` endpoint
|
|
4. Replay JSON is uploaded to R2
|
|
5. Worker API updates Glicko-2 ratings and triggers index rebuild
|
|
6. 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
|
|
|
|
```bash
|
|
# 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
|