Each of the six built-in strategy bots is now implemented in a different language (Python, Go, Rust, PHP, TypeScript, Java) to demonstrate that the HTTP protocol is truly language-agnostic. Added per-language container templates, resource specs, and forkable starter kit repos for participants. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1180 lines
46 KiB
Markdown
1180 lines
46 KiB
Markdown
# AI Code Battle — Implementation Plan
|
||
|
||
## 1. Overview
|
||
|
||
AI Code Battle is a competitive bot programming platform where participants write
|
||
HTTP servers that control units on a grid world. The game engine orchestrates
|
||
matches asynchronously, stores replays, and serves a web platform where visitors
|
||
watch rendered game replays and browse leaderboards. Matches are never live —
|
||
they are evaluated offline by match workers and presented as completed replays.
|
||
|
||
The platform ships with several built-in strategy bots, each deployed as its own
|
||
container, serving as both opponents for new participants and reference
|
||
implementations for the HTTP protocol.
|
||
|
||
---
|
||
|
||
## 2. System Architecture
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ Web Platform │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────────────────┐ │
|
||
│ │ Leaderboard │ │ Match History │ │ Replay Viewer (Canvas) │ │
|
||
│ └──────────────┘ └──────────────┘ └───────────────────────────┘ │
|
||
└──────────────────────────────┬──────────────────────────────────────┘
|
||
│ HTTPS
|
||
┌──────────▼──────────┐
|
||
│ API Server │
|
||
│ (user reg, bot reg, │
|
||
│ leaderboard, replay │
|
||
│ metadata, health) │
|
||
└──────────┬──────────┘
|
||
│
|
||
┌────────────────┼────────────────┐
|
||
│ │ │
|
||
┌────────▼───────┐ ┌─────▼──────┐ ┌───────▼────────┐
|
||
│ PostgreSQL │ │ Match │ │ Object Store │
|
||
│ (users, bots, │ │ Queue │ │ (S3-compat) │
|
||
│ matches, │ │ (Redis) │ │ replay JSON │
|
||
│ ratings) │ │ │ │ + map data │
|
||
└────────────────┘ └─────┬──────┘ └────────────────┘
|
||
│
|
||
┌─────────▼─────────┐
|
||
│ Match Workers │ ← Rackspace Spot instances
|
||
│ (stateless, │
|
||
│ interruptible) │
|
||
└─────────┬─────────┘
|
||
│ HTTP (per-turn requests)
|
||
┌───────────────┼───────────────┐
|
||
│ │ │
|
||
┌────────▼──────┐ ┌─────▼─────┐ ┌───────▼──────┐
|
||
│ Participant │ │ Built-in │ │ Participant │
|
||
│ Bot A │ │ Strategy │ │ Bot B │
|
||
│ (external) │ │ Bots │ │ (external) │
|
||
└───────────────┘ │ (containers)│ └──────────────┘
|
||
└────────────┘
|
||
```
|
||
|
||
### Component Summary
|
||
|
||
| Component | Role | Scaling Model |
|
||
|-----------|------|---------------|
|
||
| API Server | REST API for web platform, bot registration, match metadata | Horizontally scaled, always-on |
|
||
| Match Worker | Pulls match jobs from queue, executes full game simulation, uploads replay | Stateless pods on Rackspace Spot |
|
||
| Tournament Scheduler | Creates match jobs based on matchmaking algorithm | Single process, cron-like |
|
||
| Web Frontend | Static SPA — replay viewer, leaderboard, registration | CDN / static hosting |
|
||
| Strategy Bots | Built-in HTTP bots (one container each) | Always-on, lightweight |
|
||
| PostgreSQL | Users, bots, matches, ratings | Single primary + read replica |
|
||
| Redis | Match job queue, rate limiting, caching | Single instance |
|
||
| Object Store | Replay JSON files, map definitions | S3-compatible (Minio or provider) |
|
||
|
||
---
|
||
|
||
## 3. Game Mechanics
|
||
|
||
### 3.1 Map & Grid
|
||
|
||
The game plays on a **toroidal grid** — a rectangular grid that wraps both
|
||
horizontally and vertically (no edges, no corners). This eliminates
|
||
positional advantages from map boundaries.
|
||
|
||
**Tile types:**
|
||
|
||
| Tile | Symbol | Description |
|
||
|------|--------|-------------|
|
||
| Open | `.` | Passable empty tile |
|
||
| Wall | `#` | Impassable barrier |
|
||
| Energy | `*` | Collectible resource (respawns) |
|
||
| Core | `C` | Player spawn point (owned by a player) |
|
||
|
||
**Grid parameters (configurable per match):**
|
||
|
||
| Parameter | Default | Range | Description |
|
||
|-----------|---------|-------|-------------|
|
||
| `rows` | 60 | 30–120 | Grid height |
|
||
| `cols` | 60 | 30–120 | Grid width |
|
||
| `wall_density` | 0.15 | 0.05–0.30 | Fraction of tiles that are walls |
|
||
| `energy_nodes` | 20 | 8–50 | Number of energy spawn locations |
|
||
| `cores_per_player` | 1 | 1–2 | Starting cores per player |
|
||
|
||
### 3.2 Units (Bots)
|
||
|
||
Each player controls **bots** — mobile units on the grid.
|
||
|
||
- Bots move one tile per turn in a cardinal direction: `N`, `E`, `S`, `W`
|
||
- Bots that do not receive a move order hold position
|
||
- Bots are binary — alive or dead, no hit points
|
||
- A bot ordered into a wall tile stays in place (order ignored)
|
||
- Two friendly bots ordered to the same tile: **both die** (self-collision)
|
||
- A bot ordered onto a tile occupied by a stationary enemy: **both die**
|
||
|
||
Each player starts with one bot spawned at each of their cores.
|
||
|
||
### 3.3 Energy & Economy
|
||
|
||
Energy is the sole resource. It is used to spawn new bots.
|
||
|
||
**Energy nodes:**
|
||
- Fixed positions on the map that periodically produce collectible energy
|
||
- Energy appears on a node every `energy_interval` turns (default: 10)
|
||
- When energy is present on a node, it is visible to any player who can see the tile
|
||
|
||
**Collection:**
|
||
- A bot adjacent to (or on) an energy tile collects it if no enemy bot is also
|
||
adjacent to that energy
|
||
- If bots from multiple players are adjacent to the same energy, the energy is
|
||
**destroyed** — nobody gets it (contested resources are denied)
|
||
- Collection happens after combat resolution each turn
|
||
|
||
**Spawning:**
|
||
- Cost: **3 energy** per bot
|
||
- Spawning happens automatically when a player has ≥3 energy and an unoccupied,
|
||
unrazed core
|
||
- One bot spawns per core per turn maximum
|
||
- If a player has multiple cores and enough energy, one bot spawns at each
|
||
eligible core simultaneously
|
||
- Spawn priority: core that has been idle longest
|
||
|
||
### 3.4 Combat
|
||
|
||
Combat uses a **focus fire** algorithm inspired by the aichallenge ants system.
|
||
This rewards formations and positioning over raw unit count.
|
||
|
||
**Attack radius:** squared Euclidean distance ≤ `attack_radius2` (default: **5**,
|
||
meaning ~2.24 tiles — includes cardinal and diagonal neighbors plus one more ring).
|
||
|
||
**Resolution (simultaneous):**
|
||
|
||
```
|
||
for each bot B on the grid:
|
||
enemies_of_B = count of enemy bots within attack_radius2 of B
|
||
for each enemy E within attack_radius2 of B:
|
||
enemies_of_E = count of E's enemies within attack_radius2 of E
|
||
if enemies_of_B >= enemies_of_E:
|
||
mark B as dead
|
||
break (B is already dead, no need to check further)
|
||
```
|
||
|
||
All deaths are resolved **simultaneously** — no cascading within a single turn.
|
||
|
||
**Key properties:**
|
||
- 2v1: the lone bot dies, the pair survives (superior numbers win cleanly)
|
||
- 1v1: both die (mutual destruction)
|
||
- Tight formations are defensive — a cluster facing scattered enemies takes
|
||
fewer losses because each bot in the cluster has a lower enemy count
|
||
- Multi-player battles create emergent alliances and third-party exploitation
|
||
|
||
### 3.5 Fog of War
|
||
|
||
Each player has limited visibility. Only tiles within `vision_radius2`
|
||
(default: **49**, ~7 tiles) of any owned bot are visible.
|
||
|
||
**What players see within their vision:**
|
||
- All tile types (open, wall, energy, core)
|
||
- Enemy bots and their owner IDs
|
||
- Dead bots (for one turn after death)
|
||
|
||
**What players do NOT see:**
|
||
- Anything outside their collective vision radius
|
||
- How much energy opponents have
|
||
- Total number of opponents (discovered through play)
|
||
|
||
**Walls** are sent every turn they are visible (no incremental discovery state —
|
||
keeps the protocol stateless-friendly for HTTP bots).
|
||
|
||
### 3.6 Scoring & Win Conditions
|
||
|
||
**Scoring:**
|
||
- Each player starts with **1 point per core** owned
|
||
- **Capturing a core** (enemy bot moves onto an undefended enemy core): **+2 points** to capturer, **−1 point** to owner; core is razed
|
||
- **Razed cores** stop spawning but the player continues with remaining bots
|
||
- **Energy collected**: tracked as a tiebreaker statistic (not added to score)
|
||
- **Bots eliminated**: tracked as a statistic
|
||
|
||
**Win conditions (checked in order):**
|
||
|
||
| Condition | Trigger | Resolution |
|
||
|-----------|---------|------------|
|
||
| **Sole Survivor** | Only one player has living bots | That player wins; bonus +2 per surviving enemy core |
|
||
| **Annihilation** | All players eliminated simultaneously | Draw |
|
||
| **Dominance** | One player controls ≥80% of all bots for 100 consecutive turns | That player wins |
|
||
| **Turn Limit** | Turn count reaches `max_turns` (default: 500) | Highest score wins; ties broken by energy collected, then bots alive |
|
||
|
||
### 3.7 Turn Structure
|
||
|
||
Each turn executes in a strict, deterministic sequence:
|
||
|
||
```
|
||
1. Send game state to all players (HTTP POST, filtered by fog of war)
|
||
2. Await responses (up to 3-second timeout per player, in parallel)
|
||
3. Validate all responses against schema
|
||
4. Phase: MOVE — execute valid movement orders
|
||
5. Phase: COMBAT — resolve focus-fire algorithm, remove dead bots
|
||
6. Phase: CAPTURE — enemy bots on undefended cores raze them
|
||
7. Phase: COLLECT — uncontested energy adjacent to bots is collected
|
||
8. Phase: SPAWN — players with ≥3 energy spawn bots at eligible cores
|
||
9. Phase: ENERGY_TICK — energy nodes on their interval produce new energy
|
||
10. Phase: ENDGAME — check win conditions
|
||
11. Record turn state for replay
|
||
```
|
||
|
||
All player requests in step 1 are sent **concurrently**. Responses are collected
|
||
with the 3-second deadline. The engine does not proceed to step 3 until all
|
||
responses are in or timed out.
|
||
|
||
### 3.8 Map Generation
|
||
|
||
Maps are generated offline and stored in the map library. They are not generated
|
||
on-the-fly during matches.
|
||
|
||
**Symmetry requirements:**
|
||
- 2-player maps: 180° rotational symmetry (point symmetry through center)
|
||
- 3-player maps: 120° rotational symmetry
|
||
- 4-player maps: 90° rotational symmetry
|
||
- 6-player maps: 60° rotational symmetry
|
||
|
||
**Generation algorithm:**
|
||
1. Generate one **sector** (1/N of the map for N players)
|
||
2. Place walls using cellular automata (random seed → smooth with neighbor rules)
|
||
3. Place cores and energy nodes within the sector
|
||
4. Validate connectivity: BFS from core must reach all energy nodes and the
|
||
sector boundary
|
||
5. Mirror/rotate the sector to fill the full map
|
||
6. Validate full-map connectivity: all cores must be reachable from each other
|
||
7. Store the map with metadata (player count, dimensions, wall density)
|
||
|
||
**Map library:**
|
||
- Pre-generated pool of 50+ maps per player count (2, 3, 4, 6)
|
||
- Maps are curated — auto-generated then play-tested with strategy bots
|
||
- Matchmaking selects the least-recently-used map for each match
|
||
|
||
---
|
||
|
||
## 4. Communication Protocol
|
||
|
||
### 4.1 HTTP Interface
|
||
|
||
The game engine communicates with bots via HTTP POST requests. Each bot exposes
|
||
a single endpoint.
|
||
|
||
**Bot endpoint:** `POST {bot_base_url}/turn`
|
||
|
||
The engine sends the game state as a JSON body. The bot responds with its moves
|
||
as a JSON body. No other endpoints are required from the bot (though `/health` is
|
||
recommended for registration validation).
|
||
|
||
**Request flow per turn:**
|
||
```
|
||
Engine Bot
|
||
│ │
|
||
│ POST /turn │
|
||
│ Headers: auth + metadata │
|
||
│ Body: game state JSON │
|
||
│─────────────────────────────►│
|
||
│ │ (bot computes moves)
|
||
│ 200 OK │
|
||
│ Body: moves JSON │
|
||
│◄─────────────────────────────│
|
||
│ │
|
||
```
|
||
|
||
### 4.2 Game State Schema (Engine → Bot)
|
||
|
||
```json
|
||
{
|
||
"match_id": "m_7f3a9b2c",
|
||
"turn": 42,
|
||
"config": {
|
||
"rows": 60,
|
||
"cols": 60,
|
||
"max_turns": 500,
|
||
"vision_radius2": 49,
|
||
"attack_radius2": 5,
|
||
"spawn_cost": 3,
|
||
"energy_interval": 10
|
||
},
|
||
"you": {
|
||
"id": 0,
|
||
"energy": 7,
|
||
"score": 3
|
||
},
|
||
"bots": [
|
||
{ "row": 10, "col": 15, "owner": 0 },
|
||
{ "row": 12, "col": 15, "owner": 0 },
|
||
{ "row": 30, "col": 40, "owner": 1 }
|
||
],
|
||
"energy": [
|
||
{ "row": 20, "col": 25 }
|
||
],
|
||
"cores": [
|
||
{ "row": 5, "col": 5, "owner": 0, "active": true },
|
||
{ "row": 55, "col": 55, "owner": 1, "active": true }
|
||
],
|
||
"walls": [
|
||
{ "row": 10, "col": 10 },
|
||
{ "row": 10, "col": 11 }
|
||
],
|
||
"dead": [
|
||
{ "row": 15, "col": 20, "owner": 1 }
|
||
]
|
||
}
|
||
```
|
||
|
||
**Schema rules:**
|
||
- `bots`, `energy`, `cores`, `walls`, `dead` — only includes tiles within the
|
||
player's collective vision
|
||
- `owner` IDs are consistent within a match but randomized per match (player 0
|
||
is always "you")
|
||
- `config` is identical for all players and does not change between turns
|
||
- `walls` are sent every turn they are visible (stateless — bot does not need to
|
||
track previously seen walls, though smart bots will)
|
||
- `dead` contains bots that died on the previous turn (visible for one turn)
|
||
|
||
### 4.3 Move Schema (Bot → Engine)
|
||
|
||
```json
|
||
{
|
||
"moves": [
|
||
{ "row": 10, "col": 15, "direction": "N" },
|
||
{ "row": 12, "col": 15, "direction": "E" }
|
||
]
|
||
}
|
||
```
|
||
|
||
**Validation rules:**
|
||
- `moves` must be an array (may be empty — all bots hold position)
|
||
- Each move must reference a `(row, col)` where the player owns a bot
|
||
- `direction` must be one of: `"N"`, `"E"`, `"S"`, `"W"`
|
||
- Duplicate `(row, col)` entries: first valid entry wins, rest ignored
|
||
- Moves referencing tiles with no owned bot: ignored
|
||
- Moves into walls: ignored (bot stays)
|
||
- Any response that fails top-level schema validation: entire response
|
||
discarded, all bots hold
|
||
- **The engine never parses, evaluates, or interprets any field beyond
|
||
`moves[].row`, `moves[].col`, `moves[].direction`**
|
||
|
||
### 4.4 Authentication (HMAC Shared Secret)
|
||
|
||
Each registered bot has a **shared secret** generated at registration time. The
|
||
secret is known only to the bot owner and the game engine. It authenticates both
|
||
directions — the bot can verify requests came from the real game engine, and the
|
||
engine can verify responses came from the real bot.
|
||
|
||
**Engine → Bot (request signing):**
|
||
|
||
Headers sent with every request:
|
||
```
|
||
X-ACB-Match-Id: m_7f3a9b2c
|
||
X-ACB-Turn: 42
|
||
X-ACB-Timestamp: 1711200000
|
||
X-ACB-Bot-Id: b_4e8c1d2f
|
||
X-ACB-Signature: <hex-encoded HMAC-SHA256>
|
||
```
|
||
|
||
Signature computation:
|
||
```
|
||
signing_string = "{match_id}.{turn}.{timestamp}.{sha256(request_body)}"
|
||
signature = HMAC-SHA256(shared_secret, signing_string)
|
||
```
|
||
|
||
The bot verifies:
|
||
1. Compute the expected signature from the headers and request body
|
||
2. Compare with `X-ACB-Signature` (constant-time comparison)
|
||
3. Verify `X-ACB-Timestamp` is within ±30 seconds of current time (prevents
|
||
replay attacks)
|
||
4. If verification fails: bot should return 401 and ignore the request
|
||
|
||
**Bot → Engine (response signing):**
|
||
|
||
Response headers:
|
||
```
|
||
X-ACB-Signature: <hex-encoded HMAC-SHA256>
|
||
```
|
||
|
||
Signature computation:
|
||
```
|
||
signing_string = "{match_id}.{turn}.{sha256(response_body)}"
|
||
signature = HMAC-SHA256(shared_secret, signing_string)
|
||
```
|
||
|
||
The engine verifies the response signature. If invalid, the response is
|
||
discarded (bots hold position). This prevents man-in-the-middle from
|
||
injecting moves.
|
||
|
||
**Why HMAC over OAuth/JWT/mTLS:**
|
||
- Minimal complexity — no token refresh, no certificate management
|
||
- Bot developers add a single header computation, not an auth library
|
||
- Symmetric: both sides can verify the other with the same secret
|
||
- Sufficient for the threat model (prevent impersonation and tampering)
|
||
|
||
**Secret management:**
|
||
- Secrets are generated as 256-bit random values, hex-encoded (64 characters)
|
||
- Displayed once at registration time; bot owner must save it
|
||
- Can be rotated via the web platform (old secret invalidated immediately)
|
||
- Stored hashed (bcrypt) in the database — the engine uses the hash to verify,
|
||
so the raw secret is never stored. **Correction**: HMAC requires the raw
|
||
secret, so it is stored encrypted (AES-256-GCM) with a master key, not
|
||
hashed. The master key is held in an environment variable, never in the database.
|
||
|
||
### 4.5 Timeout & Error Handling
|
||
|
||
| Scenario | Behavior |
|
||
|----------|----------|
|
||
| Bot responds within 3s | Moves validated and applied normally |
|
||
| Bot responds after 3s | Response discarded; bots hold position for that turn |
|
||
| Bot returns non-200 status | Treated as timeout; bots hold position |
|
||
| Bot returns invalid JSON | Treated as timeout; bots hold position |
|
||
| Bot returns valid JSON failing schema | Entire response discarded; bots hold position |
|
||
| Bot connection refused | Bots hold position; engine retries next turn |
|
||
| Bot connection timeout (TCP) | Engine uses 2s connect timeout within the 3s budget |
|
||
| 10 consecutive failures | Bot marked as **crashed** for this match; bots become inert for remaining turns |
|
||
|
||
The bot is **never killed or disconnected**. Even after being marked crashed, the
|
||
match continues — the crashed bot's units simply hold position every turn until
|
||
they are destroyed or the match ends.
|
||
|
||
---
|
||
|
||
## 5. Strategy Bots
|
||
|
||
Six built-in strategy bots serve as reference implementations and permanent
|
||
ladder opponents. Each is implemented in a **different programming language**
|
||
to demonstrate that the HTTP protocol is truly language-agnostic and to
|
||
provide starter code for participants across the most popular ecosystems.
|
||
|
||
Each bot is deployed as its own container running a lightweight HTTP server.
|
||
|
||
| Bot | Language | Complexity | Expected Rank |
|
||
|-----|----------|------------|---------------|
|
||
| RandomBot | Python | Trivial | 6th (floor) |
|
||
| GathererBot | Go | Low | 4th–5th |
|
||
| RusherBot | Rust | Low | 4th–5th |
|
||
| GuardianBot | PHP | Medium | 3rd–4th |
|
||
| SwarmBot | TypeScript | Medium | 1st–2nd |
|
||
| HunterBot | Java | High | 1st–2nd |
|
||
|
||
### 5.1 RandomBot — Python
|
||
|
||
**Language rationale:** Python is the most accessible language for newcomers.
|
||
The random bot doubles as the simplest possible starter template — a
|
||
participant can fork it and have a working bot in minutes.
|
||
|
||
**Strategy:** Makes uniformly random valid moves each turn.
|
||
|
||
**Behavior:**
|
||
- For each owned bot, pick a random direction (N/E/S/W) or hold (20% chance)
|
||
- No pathfinding, no memory, no awareness of enemies
|
||
- Serves as the absolute baseline — any reasonable bot should beat this
|
||
|
||
**Value:** Ensures new participants have an easy opponent to test against.
|
||
Rating floor anchor.
|
||
|
||
**Implementation:** Flask or bare `http.server`. ~50 lines of strategy code.
|
||
HMAC verification via `hmac` stdlib module.
|
||
|
||
### 5.2 GathererBot — Go
|
||
|
||
**Language rationale:** Go is the same language as the game engine and
|
||
platform services, making this the canonical "how to build a bot" reference.
|
||
Demonstrates idiomatic Go HTTP server patterns.
|
||
|
||
**Strategy:** Maximize energy collection, avoid combat entirely.
|
||
|
||
**Behavior:**
|
||
- BFS from each owned bot to the nearest visible energy
|
||
- Assign each bot to the closest uncontested energy (greedy matching)
|
||
- If an enemy bot is within vision, move away from it
|
||
- Never voluntarily enters attack range of an enemy
|
||
- Spawns bots as fast as energy allows
|
||
|
||
**Value:** Tests whether aggressive bots can actually close games or whether
|
||
passive resource hoarding is dominant (it shouldn't be).
|
||
|
||
**Implementation:** `net/http` stdlib server. Shared `game/` package with
|
||
grid utilities, BFS, and distance calculations that participants can reuse.
|
||
|
||
### 5.3 RusherBot — Rust
|
||
|
||
**Language rationale:** Rust participants get maximum compute within the
|
||
3-second timeout. This bot demonstrates that Rust's performance advantage
|
||
matters less than strategy — a dumb fast bot still loses to a smart slow one.
|
||
|
||
**Strategy:** Identify and rush the nearest enemy core as fast as possible.
|
||
|
||
**Behavior:**
|
||
- BFS from each owned bot toward the nearest known enemy core
|
||
- If no enemy core is known, spread out to explore (random walk with
|
||
bias toward unexplored territory)
|
||
- Ignores energy except incidentally (walks over it)
|
||
- Ignores enemy bots unless they block the path
|
||
- Spawns bots immediately and sends all toward the target
|
||
|
||
**Value:** Punishes bots that neglect defense. Tests whether the combat
|
||
system allows pure aggression to dominate (it shouldn't — rusher bots will
|
||
walk into defensive formations and die).
|
||
|
||
**Implementation:** `axum` or `actix-web`. Serde for JSON. HMAC via `hmac`
|
||
and `sha2` crates. Demonstrates Rust's zero-copy deserialization.
|
||
|
||
### 5.4 GuardianBot — PHP
|
||
|
||
**Language rationale:** PHP is often overlooked in competitive programming
|
||
but is widely known and trivially deployable. This demonstrates that even
|
||
PHP — without async, without frameworks — can compete on equal footing
|
||
when the interface is HTTP. Lowers the barrier for the large PHP developer
|
||
community.
|
||
|
||
**Strategy:** Defend own core, gather nearby energy, cautious expansion.
|
||
|
||
**Behavior:**
|
||
- Maintain a perimeter of bots within 5 tiles of each owned core
|
||
- Assign excess bots (beyond perimeter needs) to gather energy within
|
||
10 tiles of a core
|
||
- If enemy bots are spotted approaching, consolidate defenders between
|
||
the enemy and the core
|
||
- Only sends scouts (lone bots) to explore beyond the safe zone
|
||
- Very conservative spawning — maintains energy reserve of 6
|
||
|
||
**Value:** Tests whether turtling is viable. Should beat rushers but lose to
|
||
gatherers/swarms in the long game (inferior economy due to limited territory).
|
||
|
||
**Implementation:** PHP built-in server (`php -S`) with a single router
|
||
script. `hash_hmac()` for HMAC. JSON via `json_decode`/`json_encode`.
|
||
BFS implemented with `SplQueue`.
|
||
|
||
### 5.5 SwarmBot — TypeScript
|
||
|
||
**Language rationale:** TypeScript (Node.js) is the most popular language
|
||
for web developers entering the platform. This bot demonstrates maintaining
|
||
complex state across turns — the swarm's formation tracking, rally points,
|
||
and center-of-mass calculation benefit from TypeScript's type system.
|
||
|
||
**Strategy:** Keep units in tight formations, advance as a group toward enemies.
|
||
|
||
**Behavior:**
|
||
- All bots maintain cohesion — no bot moves if it would be >3 tiles from the
|
||
nearest friendly bot
|
||
- The swarm moves as a unit toward the nearest enemy presence
|
||
- BFS-based center-of-mass steering: average position of all owned bots
|
||
is the swarm center; steer toward enemy center of mass
|
||
- Energy collection is incidental (pass over it during advance)
|
||
- New spawns rally to the swarm before advancing
|
||
|
||
**Value:** Exploits the focus combat system — a tight group defeats scattered
|
||
enemies. But slow expansion means inferior economy. Should dominate combat
|
||
but can be outscored by gatherers on large maps.
|
||
|
||
**Implementation:** Express.js or Fastify. State persisted in-process across
|
||
turns (the HTTP server stays alive between requests). HMAC via Node.js
|
||
`crypto` module. Typed interfaces for game state and moves.
|
||
|
||
### 5.6 HunterBot — Java
|
||
|
||
**Language rationale:** Java is dominant in competitive programming (Battlecode
|
||
is Java-only). This is the most sophisticated strategy bot, demonstrating
|
||
that Java's verbosity is offset by mature data structures (`PriorityQueue`,
|
||
`HashMap`) and predictable GC behavior within the timeout window.
|
||
|
||
**Strategy:** Target isolated enemy bots for efficient kills.
|
||
|
||
**Behavior:**
|
||
- Identify enemy bots that are ≥4 tiles from their nearest friendly bot
|
||
(isolated targets)
|
||
- Send pairs of bots to intercept isolated enemies (2v1 wins cleanly)
|
||
- If no isolated targets, default to gatherer behavior
|
||
- Maintain a map of known enemy positions across turns, predict movement
|
||
based on last-seen direction and speed
|
||
- Avoid engaging formations of 3+ enemy bots
|
||
- Opportunistic energy collection when not actively hunting
|
||
|
||
**Value:** Sophisticated target selection and prediction. Represents an
|
||
intermediate-to-advanced-skill bot. Should beat random/gatherer/rusher but
|
||
struggle against swarm formations.
|
||
|
||
**Implementation:** Javalin or `com.sun.net.httpserver`. `javax.crypto.Mac`
|
||
for HMAC. Maintains a `HashMap<Position, EnemyTracker>` across turns for
|
||
movement prediction. Hungarian algorithm for optimal bot-to-target assignment.
|
||
|
||
### 5.7 Container Templates
|
||
|
||
Each language has its own container structure. All share the same contract:
|
||
listen on port 8080, serve `POST /turn` and `GET /health`.
|
||
|
||
**Go (GathererBot):**
|
||
```
|
||
strategy-gatherer/
|
||
├── Dockerfile
|
||
├── main.go # HTTP server, HMAC verification
|
||
├── strategy.go # Gatherer-specific logic
|
||
├── game/
|
||
│ ├── state.go # Game state types
|
||
│ ├── grid.go # Grid utilities (BFS, distance, wrapping)
|
||
│ └── moves.go # Move response types
|
||
└── go.mod
|
||
```
|
||
|
||
**Python (RandomBot):**
|
||
```
|
||
strategy-random/
|
||
├── Dockerfile
|
||
├── main.py # HTTP server, HMAC verification, strategy
|
||
├── game.py # Game state types and grid utilities
|
||
└── requirements.txt # (minimal — stdlib only for random bot)
|
||
```
|
||
|
||
**Rust (RusherBot):**
|
||
```
|
||
strategy-rusher/
|
||
├── Dockerfile
|
||
├── Cargo.toml
|
||
└── src/
|
||
├── main.rs # HTTP server, HMAC verification
|
||
├── strategy.rs # Rusher-specific logic
|
||
└── game.rs # Game state types, grid utilities
|
||
```
|
||
|
||
**PHP (GuardianBot):**
|
||
```
|
||
strategy-guardian/
|
||
├── Dockerfile
|
||
├── index.php # Router + HMAC verification
|
||
├── strategy.php # Guardian-specific logic
|
||
├── game.php # Game state types, BFS, grid utilities
|
||
└── composer.json # (optional — no external deps needed)
|
||
```
|
||
|
||
**TypeScript (SwarmBot):**
|
||
```
|
||
strategy-swarm/
|
||
├── Dockerfile
|
||
├── package.json
|
||
├── tsconfig.json
|
||
└── src/
|
||
├── index.ts # HTTP server, HMAC verification
|
||
├── strategy.ts # Swarm-specific logic
|
||
└── game.ts # Game state types, grid utilities
|
||
```
|
||
|
||
**Java (HunterBot):**
|
||
```
|
||
strategy-hunter/
|
||
├── Dockerfile
|
||
├── pom.xml
|
||
└── src/main/java/com/acb/hunter/
|
||
├── App.java # HTTP server, HMAC verification
|
||
├── Strategy.java # Hunter-specific logic
|
||
├── GameState.java # Game state deserialization
|
||
└── Grid.java # Grid utilities, BFS, distance
|
||
```
|
||
|
||
**Shared contract (all languages):**
|
||
- Listen on port 8080
|
||
- `POST /turn` — receives game state, runs strategy, returns moves
|
||
- `GET /health` — returns 200 (used for registration health check)
|
||
- HMAC signature verification on incoming requests
|
||
- HMAC signature on outgoing responses
|
||
- Request logging (turn number, compute time, move count)
|
||
|
||
**Container specs:**
|
||
|
||
| Bot | Build Image | Runtime Image | Memory Limit | CPU Limit |
|
||
|-----|-------------|---------------|-------------|-----------|
|
||
| RandomBot | `python:3.13-slim` | `python:3.13-slim` | 64MB | 0.1 cores |
|
||
| GathererBot | `golang:1.24-alpine` | `alpine:3.21` | 128MB | 0.25 cores |
|
||
| RusherBot | `rust:1.85-alpine` | `alpine:3.21` | 128MB | 0.25 cores |
|
||
| GuardianBot | `php:8.4-cli-alpine` | `php:8.4-cli-alpine` | 128MB | 0.25 cores |
|
||
| SwarmBot | `node:22-alpine` | `node:22-alpine` | 128MB | 0.25 cores |
|
||
| HunterBot | `eclipse-temurin:21-alpine` | `eclipse-temurin:21-jre-alpine` | 256MB | 0.5 cores |
|
||
|
||
Java gets a higher resource allocation due to JVM overhead. All others are
|
||
intentionally constrained — strategy bots should be lightweight.
|
||
|
||
### 5.8 Starter Kit & SDK Libraries
|
||
|
||
To lower the barrier for participants writing their own bots, the platform
|
||
provides **starter kits** for each supported language. Each starter kit is a
|
||
minimal, forkable repository containing:
|
||
|
||
- A working HTTP server with HMAC verification already implemented
|
||
- Type definitions for the game state and move schemas
|
||
- Grid utility functions (toroidal distance, BFS, neighbor enumeration)
|
||
- A stub strategy function that holds all bots in place (participant fills in)
|
||
- A Dockerfile that builds and runs the bot
|
||
- A README with quickstart instructions
|
||
|
||
**Starter kit languages (matching strategy bots):**
|
||
|
||
| Kit | Repository | Notes |
|
||
|-----|-----------|-------|
|
||
| `acb-starter-python` | Template repo | Flask-based, ~100 lines total |
|
||
| `acb-starter-go` | Template repo | Shares `game/` package with GathererBot |
|
||
| `acb-starter-rust` | Template repo | `axum` + `serde`, strongly typed |
|
||
| `acb-starter-php` | Template repo | Zero dependencies, built-in server |
|
||
| `acb-starter-typescript` | Template repo | Fastify, full type definitions |
|
||
| `acb-starter-java` | Template repo | Javalin, Maven-based |
|
||
|
||
Participants are not limited to these languages. Any language that can serve
|
||
HTTP and compute HMAC-SHA256 can compete. The starter kits simply eliminate
|
||
boilerplate for the most common choices.
|
||
|
||
---
|
||
|
||
## 6. Tournament System
|
||
|
||
### 6.1 Matchmaking
|
||
|
||
Matches are created continuously by the **tournament scheduler**, a
|
||
process that runs on a fixed interval (default: every 10 seconds).
|
||
|
||
**Algorithm:**
|
||
|
||
1. **Select seed bot**: the registered bot with the most time since its last
|
||
match (tiebreak: lowest bot ID)
|
||
2. **Determine match size**: based on the seed bot's least-played format
|
||
(2-player, 3-player, 4-player, or 6-player)
|
||
3. **Select opponents**: from the eligible pool, preferring:
|
||
a. Closest skill rating to seed (Pareto distribution: 80% within 16 ranks)
|
||
b. Least recently paired with the seed
|
||
c. Fewest games played in the last 24 hours (keeps game counts even)
|
||
4. **Select map**: least recently used map for the chosen player count
|
||
5. **Assign player slots**: random
|
||
6. **Create match job**: push to Redis queue with match config + bot endpoints
|
||
|
||
**Eligibility:**
|
||
- Bot must be registered and active (passed health check within last hour)
|
||
- Bot must not be in a match currently (one match at a time per bot)
|
||
- Bot must not have been marked crashed in its last 3 consecutive matches
|
||
(cooldown: 30 minutes)
|
||
|
||
### 6.2 Rating System
|
||
|
||
**Algorithm: Glicko-2**
|
||
|
||
Glicko-2 is preferred over TrueSkill for this platform because:
|
||
- No licensing concerns (TrueSkill is patented by Microsoft)
|
||
- Includes a volatility parameter (σ) that adapts to inconsistent performance
|
||
- Well-suited to multi-player games via pairwise decomposition
|
||
- Established in competitive gaming (chess, Go, online games)
|
||
|
||
**Parameters per bot:**
|
||
- `mu` (μ): rating estimate (default: 1500)
|
||
- `phi` (φ): rating deviation / uncertainty (default: 350)
|
||
- `sigma` (σ): rating volatility (default: 0.06)
|
||
|
||
**Display rating:** `mu - 2*phi` (conservative estimate shown on leaderboard)
|
||
|
||
**Update frequency:** after every match. Ratings converge quickly — a new bot
|
||
reaches a stable rating within ~30 matches.
|
||
|
||
**Multi-player adaptation:**
|
||
- A 4-player match produces 6 pairwise results (every pair of players)
|
||
- Each pairwise result is: win/loss based on relative score, or draw if equal
|
||
- Glicko-2 update is applied once per match using all pairwise outcomes
|
||
|
||
### 6.3 Continuous Tournament
|
||
|
||
The tournament runs indefinitely with no seasons or resets (initially).
|
||
|
||
**Match throughput target:** enough matches that every active bot plays at
|
||
least 10 matches per day. With N active bots and M match workers:
|
||
- 2-player matches: each match involves 2 bots, takes ~3 minutes (500 turns × 3s max + overhead)
|
||
- One worker produces ~20 matches/hour
|
||
- 3 workers: ~60 matches/hour, ~1440/day — supports ~288 active bots at 10 games/day
|
||
|
||
**Scaling:** add more spot workers to increase throughput.
|
||
|
||
---
|
||
|
||
## 7. Replay System
|
||
|
||
### 7.1 Replay Data Format
|
||
|
||
Replays are JSON files optimized for compact storage while supporting full
|
||
client-side reconstruction of every game turn.
|
||
|
||
```json
|
||
{
|
||
"version": 1,
|
||
"match_id": "m_7f3a9b2c",
|
||
"date": "2026-03-23T14:30:00Z",
|
||
"players": [
|
||
{ "bot_id": "b_4e8c1d2f", "name": "SwarmBot", "owner": "alice" },
|
||
{ "bot_id": "b_9a1b3c4d", "name": "HunterBot", "owner": "bob" }
|
||
],
|
||
"result": {
|
||
"winner": 0,
|
||
"condition": "turn_limit",
|
||
"final_scores": [7, 3],
|
||
"final_energy": [12, 4],
|
||
"final_bots": [18, 6]
|
||
},
|
||
"config": {
|
||
"rows": 60,
|
||
"cols": 60,
|
||
"max_turns": 500,
|
||
"vision_radius2": 49,
|
||
"attack_radius2": 5,
|
||
"spawn_cost": 3,
|
||
"energy_interval": 10
|
||
},
|
||
"map": {
|
||
"walls": [[10,10], [10,11], [10,12]],
|
||
"energy_nodes": [[20,25], [40,35]],
|
||
"cores": [
|
||
{ "pos": [5,5], "owner": 0 },
|
||
{ "pos": [55,55], "owner": 1 }
|
||
]
|
||
},
|
||
"turns": [
|
||
{
|
||
"moves": {
|
||
"0": [{"from":[10,15],"dir":"N"},{"from":[12,15],"dir":"E"}],
|
||
"1": [{"from":[50,45],"dir":"S"}]
|
||
},
|
||
"spawns": [[5,5,0]],
|
||
"deaths": [[30,40,1]],
|
||
"captures": [],
|
||
"energy_collected": {"0": [[20,25]]},
|
||
"energy_spawned": [[35,15]],
|
||
"scores": [3, 1]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Size estimate:** a 500-turn, 4-player match with ~50 bots total produces
|
||
a replay of ~200–500 KB uncompressed, ~30–80 KB gzipped.
|
||
|
||
**Optimization:** for very long matches, the `turns` array can use delta
|
||
encoding — only recording events that changed from the previous turn.
|
||
|
||
### 7.2 Storage
|
||
|
||
- Replays are stored in S3-compatible object storage (Minio self-hosted or
|
||
provider-managed)
|
||
- Path: `replays/{year}/{month}/{match_id}.json.gz`
|
||
- Retention: indefinite for top-100 matches per month; older matches pruned
|
||
after 90 days
|
||
- Map definitions stored separately: `maps/{map_id}.json`
|
||
- The API server returns signed URLs for replay access (no public bucket)
|
||
|
||
### 7.3 Browser Replay Viewer
|
||
|
||
The replay viewer is a client-side TypeScript application rendered on
|
||
HTML5 Canvas.
|
||
|
||
**Rendering pipeline:**
|
||
1. Fetch replay JSON from object storage (via signed URL from API)
|
||
2. Parse and index: build per-turn game state by replaying events from turn 0
|
||
3. Render the current turn to canvas
|
||
4. User controls advance/rewind the turn index
|
||
|
||
**Visual design:**
|
||
|
||
| Element | Rendering |
|
||
|---------|-----------|
|
||
| Grid | Subtle grid lines on dark background |
|
||
| Walls | Dark gray filled squares |
|
||
| Open tiles | Transparent (background shows through) |
|
||
| Energy nodes | Small yellow diamond; pulse animation when energy is present |
|
||
| Cores | Large player-colored circle with ring; X overlay when razed |
|
||
| Bots | Player-colored filled circles; brief trail showing last move direction |
|
||
| Dead bots | Fading red X for one turn |
|
||
| Fog of war | Dark semi-transparent overlay on tiles outside selected player's vision |
|
||
| Combat | Flash effect on tiles where kills occurred |
|
||
|
||
**Controls:**
|
||
|
||
| Control | Function |
|
||
|---------|----------|
|
||
| Play / Pause | Toggle automatic playback |
|
||
| Speed slider | 1x, 2x, 4x, 8x, 16x (turns per second: 2, 4, 8, 16, 32) |
|
||
| Turn scrubber | Drag to any turn; displays turn number |
|
||
| Perspective dropdown | "All" (omniscient) or per-player fog of war view |
|
||
| Zoom | Scroll to zoom; drag to pan |
|
||
| Score overlay | Per-player score, energy, bot count — updates each turn |
|
||
| Minimap | Small overview of full grid in corner (for large maps) |
|
||
|
||
**Shareable URLs:** `https://aicodebattle.com/replay/{match_id}` — the
|
||
replay viewer is the landing page for any match. No login required to watch.
|
||
|
||
---
|
||
|
||
## 8. Web Platform
|
||
|
||
### 8.1 User Registration
|
||
|
||
- Email + password or OAuth (GitHub recommended — target audience is developers)
|
||
- Email verification required before bot registration
|
||
- Profile: username (unique), display name, avatar (from OAuth provider)
|
||
|
||
### 8.2 Bot Registration
|
||
|
||
**Registration flow:**
|
||
|
||
1. User navigates to "Register Bot" in their dashboard
|
||
2. Provides:
|
||
- **Bot name** (unique, alphanumeric + hyphens, 3–32 chars)
|
||
- **Endpoint URL** (HTTPS required for competitive play; HTTP allowed for
|
||
development with a flag)
|
||
- **Description** (optional, shown on leaderboard)
|
||
3. Platform generates:
|
||
- `bot_id`: unique identifier (`b_` prefix + 8 hex chars)
|
||
- `shared_secret`: 256-bit random, hex-encoded (64 chars)
|
||
4. Platform displays the shared secret **once** — user must copy it
|
||
5. Platform performs a **health check**: `GET {endpoint_url}/health`
|
||
- Must return 200 within 5 seconds
|
||
- If health check fails, registration is saved but bot is marked **inactive**
|
||
6. Platform performs a **protocol test**: sends a mock turn-0 game state to
|
||
`POST {endpoint_url}/turn` with valid HMAC
|
||
- Bot must return a valid (possibly empty) moves response within 3 seconds
|
||
- If protocol test fails, bot is marked **inactive** with an error message
|
||
|
||
**Bot status lifecycle:**
|
||
```
|
||
PENDING → ACTIVE → INACTIVE (health check failed)
|
||
→ SUSPENDED (manual by admin)
|
||
→ RETIRED (by owner)
|
||
```
|
||
|
||
Only `ACTIVE` bots participate in matchmaking.
|
||
|
||
**Ongoing health checks:** the platform pings each active bot's `/health`
|
||
endpoint every 15 minutes. Three consecutive failures → marked `INACTIVE`.
|
||
Bots automatically return to `ACTIVE` when health checks resume passing.
|
||
|
||
### 8.3 Leaderboard
|
||
|
||
- Default sort: Glicko-2 display rating (mu - 2*phi) descending
|
||
- Columns: rank, bot name, owner, rating, games played, win rate, last active
|
||
- Filterable by: player count tier (2p, 3p, 4p, 6p), time range
|
||
- Updates in near-real-time (WebSocket push or 30-second polling)
|
||
- Public — no login required to view
|
||
|
||
### 8.4 Match History & Profiles
|
||
|
||
**Bot profile page** (`/bot/{bot_name}`):
|
||
- Current rating + rating history chart
|
||
- Recent matches (last 50) with links to replay viewer
|
||
- Win/loss/draw breakdown
|
||
- Performance vs. each opponent
|
||
- Bot description, owner, registration date
|
||
|
||
**User profile page** (`/user/{username}`):
|
||
- List of owned bots
|
||
- Aggregate statistics across all bots
|
||
|
||
**Match page** (`/match/{match_id}`):
|
||
- Participants, map, final scores
|
||
- Embedded replay viewer (auto-plays)
|
||
- Turn-by-turn event log (collapsible)
|
||
|
||
---
|
||
|
||
## 9. Deployment & Infrastructure
|
||
|
||
### 9.1 Container Architecture
|
||
|
||
| Image | Base | Purpose | Replicas |
|
||
|-------|------|---------|----------|
|
||
| `acb-api` | Go binary on Alpine | REST API server | 2 (always-on) |
|
||
| `acb-worker` | Go binary on Alpine | Match execution worker | 3–10 (spot) |
|
||
| `acb-scheduler` | Go binary on Alpine | Tournament matchmaking | 1 (always-on) |
|
||
| `acb-web` | Nginx + static files | Frontend SPA | 1 (or CDN) |
|
||
| `acb-strategy-random` | Python 3.13 slim | RandomBot | 1 |
|
||
| `acb-strategy-gatherer` | Go on Alpine | GathererBot | 1 |
|
||
| `acb-strategy-rusher` | Rust on Alpine | RusherBot | 1 |
|
||
| `acb-strategy-guardian` | PHP 8.4 CLI Alpine | GuardianBot | 1 |
|
||
| `acb-strategy-swarm` | Node 22 Alpine | SwarmBot (TypeScript) | 1 |
|
||
| `acb-strategy-hunter` | Temurin 21 JRE Alpine | HunterBot (Java) | 1 |
|
||
|
||
### 9.2 Rackspace Spot Deployment
|
||
|
||
Match workers are the primary consumers of compute and are **perfectly suited
|
||
for spot instances**:
|
||
|
||
- **Stateless**: workers pull jobs from a queue, execute, and push results.
|
||
No persistent local state.
|
||
- **Interruptible**: if a spot instance is reclaimed mid-match, the match job
|
||
is re-queued after a staleness timeout (10 minutes with no progress update).
|
||
The match is replayed from scratch on another worker.
|
||
- **Bursty**: match throughput can flex with spot availability. More instances
|
||
= faster ladder convergence, but no hard deadline.
|
||
|
||
**Instance sizing:**
|
||
- Match workers: 2 vCPU, 4 GB RAM per instance (each runs one match at a time)
|
||
- Strategy bots: can share a single small instance (all 6 use <1GB total;
|
||
Java's JVM is the biggest consumer at ~256MB)
|
||
- API server + scheduler: 2 vCPU, 4 GB RAM, always-on (not spot)
|
||
|
||
**Deployment layout:**
|
||
|
||
```
|
||
Always-on tier (standard instances):
|
||
├── acb-api (×2, behind load balancer)
|
||
├── acb-scheduler (×1)
|
||
├── acb-web (×1 or CDN)
|
||
├── acb-strategy-* (×1 each, shared instance)
|
||
├── PostgreSQL (managed or self-hosted)
|
||
├── Redis (managed or self-hosted)
|
||
└── Minio / S3-compatible store
|
||
|
||
Spot tier (preemptible instances):
|
||
├── acb-worker (×3 minimum, scale up as available)
|
||
└── (each worker is a standalone container, no coordination needed)
|
||
```
|
||
|
||
**Spot reclaim handling:**
|
||
1. Worker registers a shutdown hook that catches SIGTERM
|
||
2. On SIGTERM, worker sets the current match status to `interrupted` in Redis
|
||
3. Worker exits gracefully (within the 30-second SIGTERM grace period)
|
||
4. Scheduler's stale-match reaper detects `interrupted` or stale `in_progress`
|
||
matches and re-queues them
|
||
5. Another worker picks up the job
|
||
|
||
### 9.3 Data Stores
|
||
|
||
**PostgreSQL:**
|
||
- Tables: `users`, `bots`, `matches`, `match_participants`, `maps`, `ratings`
|
||
- Single primary instance; read replica for leaderboard queries
|
||
- Connection pooling via PgBouncer
|
||
- Backup: daily automated dumps to object storage
|
||
|
||
**Redis:**
|
||
- Match job queue (Redis Streams or List-based queue)
|
||
- Rate limiting (per-bot, per-endpoint)
|
||
- Session cache
|
||
- Leaderboard cache (sorted sets)
|
||
- No persistence required — queue jobs are recoverable from PostgreSQL match
|
||
records with `queued` status
|
||
|
||
**Object Storage (S3-compatible):**
|
||
- Replay files (gzipped JSON)
|
||
- Map definition files
|
||
- Bot submission metadata / logs
|
||
- Signed URL generation for replay access (1-hour expiry)
|
||
|
||
### 9.4 Networking & Security
|
||
|
||
**External traffic:**
|
||
- Web platform: HTTPS only, behind reverse proxy (Caddy or nginx)
|
||
- Bot endpoints: engine connects outbound to registered URLs
|
||
|
||
**Internal traffic:**
|
||
- API ↔ PostgreSQL: private network
|
||
- API ↔ Redis: private network
|
||
- Workers ↔ Redis: private network (workers may be in different regions — use
|
||
Redis over TLS if cross-region)
|
||
- Workers → bot endpoints: public internet (HTTPS required for competitive bots)
|
||
- Workers → strategy bots: private network (same infrastructure)
|
||
|
||
**Security boundaries:**
|
||
- The game engine (workers) never executes bot code — HTTP only
|
||
- All bot responses are schema-validated before processing
|
||
- HMAC authentication prevents request/response forgery
|
||
- Rate limiting on API endpoints (registration, health checks)
|
||
- Bot endpoint URLs validated at registration (no internal IPs, no localhost)
|
||
- Workers run with no inbound ports — they only make outbound HTTP calls
|
||
|
||
### 9.5 Monitoring
|
||
|
||
| Signal | Tool | Alert Threshold |
|
||
|--------|------|-----------------|
|
||
| Match throughput | Prometheus counter | <10 matches/hour for >30 minutes |
|
||
| Worker count | Prometheus gauge | <2 live workers for >15 minutes |
|
||
| Bot health check failures | Prometheus counter | >50% of active bots failing |
|
||
| API latency (p99) | Prometheus histogram | >500ms |
|
||
| Match queue depth | Redis metric | >100 pending matches |
|
||
| Replay storage usage | S3 metric | >80% of quota |
|
||
| Error rate (5xx) | Access logs | >1% of requests |
|
||
|
||
---
|
||
|
||
## 10. Implementation Phases
|
||
|
||
### Phase 1: Core Engine (foundation)
|
||
|
||
Build the game simulation as a standalone Go library with a CLI runner.
|
||
|
||
**Deliverables:**
|
||
- `engine/` package: grid, bots, energy, combat, fog of war, turn execution
|
||
- `cmd/acb-local/` CLI: run a match between two local bot processes
|
||
(stdin/stdout for dev convenience) and output a replay JSON file
|
||
- Replay JSON writer
|
||
- Comprehensive unit tests for combat resolution, fog of war, wrapping,
|
||
collision, scoring, endgame conditions
|
||
- Map generation tool: `cmd/acb-mapgen/`
|
||
|
||
**Exit criteria:** can run a complete 500-turn match between two bots locally
|
||
and produce a valid replay file.
|
||
|
||
### Phase 2: HTTP Protocol & Strategy Bots
|
||
|
||
**Deliverables:**
|
||
- HTTP bot interface in the engine (replaces stdin/stdout for production)
|
||
- HMAC signing and verification library (Go, reusable by GathererBot)
|
||
- GathererBot (Go) and RandomBot (Python) — validate the protocol works
|
||
across languages before building the remaining four
|
||
- RusherBot (Rust), GuardianBot (PHP), SwarmBot (TypeScript), HunterBot (Java)
|
||
- All 6 bots containerized with language-appropriate Dockerfiles
|
||
- Starter kit template repos for each language (fork-ready)
|
||
- Integration test: engine runs a full match between bots in different
|
||
languages over HTTP
|
||
|
||
**Exit criteria:** can run a complete match between any two strategy bot
|
||
containers (in different languages) over HTTP, with HMAC authentication,
|
||
producing a valid replay.
|
||
|
||
### Phase 3: Replay Viewer
|
||
|
||
**Deliverables:**
|
||
- TypeScript Canvas-based replay viewer
|
||
- Play/pause, scrub, speed control
|
||
- Fog of war perspective toggle
|
||
- Score overlay
|
||
- Loads replay JSON from local file or URL
|
||
|
||
**Exit criteria:** can open a replay file in a browser and watch a complete
|
||
match with all visual elements rendering correctly.
|
||
|
||
### Phase 4: Match Orchestration
|
||
|
||
**Deliverables:**
|
||
- Match worker service (`acb-worker`): pulls from Redis queue, runs matches,
|
||
uploads replays, records results
|
||
- Tournament scheduler (`acb-scheduler`): matchmaking algorithm, creates jobs
|
||
- PostgreSQL schema and migrations
|
||
- Stale match reaper (handles interrupted spot instances)
|
||
- Match result → Glicko-2 rating update pipeline
|
||
|
||
**Exit criteria:** scheduler creates matches, workers execute them
|
||
autonomously, ratings update, replays are stored. System recovers from
|
||
worker interruption.
|
||
|
||
### Phase 5: Web Platform
|
||
|
||
**Deliverables:**
|
||
- API server (`acb-api`): user registration, bot registration, leaderboard,
|
||
match history, replay URLs
|
||
- Web frontend (`acb-web`): registration, bot management dashboard,
|
||
leaderboard, match history, embedded replay viewer
|
||
- Bot health check system (periodic + on-registration)
|
||
- Shared secret generation, display, rotation
|
||
|
||
**Exit criteria:** a user can register, add a bot, see it appear on the
|
||
leaderboard after matches are played, and watch replays of its games.
|
||
|
||
### Phase 6: Deployment & Production
|
||
|
||
**Deliverables:**
|
||
- Container images pushed to registry
|
||
- Rackspace Spot deployment for workers
|
||
- Always-on deployment for API, scheduler, strategy bots, datastores
|
||
- TLS termination, DNS, CDN for static assets
|
||
- Monitoring dashboards and alerts
|
||
- Backup automation for PostgreSQL and replay storage
|
||
|
||
**Exit criteria:** platform is publicly accessible, matches run continuously,
|
||
strategy bots compete on the ladder, external participants can register and
|
||
play.
|