From 282a3aed94612a16358e87a11eef1e6746123148 Mon Sep 17 00:00:00 2001 From: jedarden Date: Mon, 23 Mar 2026 22:56:54 -0400 Subject: [PATCH] Add platform depth features: debug telemetry, territory heatmaps, embeds, playlists, predictions, map evolution, series, event timeline, seasons, bot cards New section 13 (10 features) + Phase 9: - Bot debug telemetry: optional structured debug field in move response, rendered as targets/heatmaps/reasoning in the replay viewer - Three replay view modes: dots, Voronoi territory, influence gradient - Embeddable replay widget (~50KB iframe, OG tags, query params) - Auto-curated playlists: Closest Finishes, Upsets, Comebacks, etc. - Prediction system for non-coders (within CF free tier: ~864 writes/day) - Map evolution with breeding, engagement scoring, positional fairness monitoring, and user voting (upvote/downvote maps) - Multi-game series (bo3/bo5/bo7) across different maps with spoiler toggle - Match event timeline with clickable icon ribbon - Seasonal rotations with backward-compatible rule versioning (additive changes only: new optional fields, param tuning, new tile types that old bots can ignore), championship brackets, season archives - Bot profile cards as shareable PNGs with OG tags Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/plan/plan.md | 851 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 851 insertions(+) diff --git a/docs/plan/plan.md b/docs/plan/plan.md index 753c537..72a72ba 100644 --- a/docs/plan/plan.md +++ b/docs/plan/plan.md @@ -1946,6 +1946,35 @@ replays with commentary and win probability, export clips for social sharing, view rivalries, and submit tactical feedback that influences the evolution pipeline. +### Phase 9: Platform Depth + +**Deliverables:** +- Bot debug telemetry: optional `debug` field in move response schema, + stored in replay, rendered in viewer side panel + grid overlays +- Replay view modes: dots (default), Voronoi territory, influence gradient + — all computed client-side, toggled via viewer toolbar +- Embeddable replay widget: `/embed/{match_id}` route on Pages, minimal + Chrome, auto-play, ~50KB, Open Graph tags +- Replay playlists: auto-curated collections rebuilt by index cron, stored + in R2, browsable on the static site +- Prediction system: D1 `predictions` table, Worker endpoints for submit + + resolve, prediction leaderboard JSON in R2 +- Map evolution pipeline: engagement scoring, breeding/mutation, symmetry + validation, positional fairness monitoring, user map voting +- Multi-game series: D1 `series` table, series scheduler, unified replay + presentation, spoiler toggle +- Match event timeline: client-side event extraction, icon ribbon in + replay viewer, click-to-jump +- Seasonal system: D1 `seasons` table, ladder reset logic, season archive + pages, versioned game rules with backward compatibility +- Bot profile cards: Canvas-rendered PNG, shareable URL with OG tags + +**Exit criteria:** the platform supports seasonal competition with map +evolution, multi-game series, predictions for non-coders, embeddable +replays, curated playlists, three replay view modes, bot debug telemetry, +event timelines, and shareable bot profile cards. All within Cloudflare +free tier. + --- ## 12. Enhanced Features @@ -2388,3 +2417,825 @@ system guarantees you lose the 3v1." contribute strategic insight, the AI translates it into code, the platform evaluates it, and successful feedback is credited. This gives non-coders a way to participate meaningfully in the competition. + +--- + +## 13. Platform Depth Features + +### 13.1 Bot Debug Telemetry + Reasoning Visualization + +Bots can optionally include a `debug` field in their move response. The +engine stores it in the replay without interpreting it. The replay viewer +renders it. + +**Extended move response schema:** + +```json +{ + "moves": [ + { "row": 10, "col": 15, "direction": "N" } + ], + "debug": { + "reasoning": "3 energy within 5 tiles east; enemy cluster north — avoiding", + "targets": [ + { "row": 20, "col": 25, "label": "energy", "priority": 0.9 }, + { "row": 8, "col": 30, "label": "threat", "priority": 0.7 } + ], + "values": { + "energy_reserves": 7, + "threat_level": "medium", + "mode": "gathering" + }, + "heatmap": { + "name": "threat", + "data": [[0, 0, 0.2, 0.8], [0, 0.1, 0.5, 0.9]] + } + } +} +``` + +**Schema rules for `debug`:** +- Entirely optional — bots that omit it behave identically +- Max size: 10 KB per turn (prevents replay bloat; excess is truncated) +- The engine never reads or acts on debug data — it's pass-through to replay +- No fields inside `debug` are validated beyond size — bots can put anything +- Only the bot's owner sees debug data by default; owners can toggle public + visibility per-bot in their bot profile + +**Replay viewer rendering:** + +| Debug field | Rendering | +|-------------|-----------| +| `reasoning` | Text in a collapsible side panel, one entry per turn | +| `targets` | Colored markers on the grid (green = high priority, red = low) with labels | +| `values` | Key-value table in the side panel, updates each turn | +| `heatmap` | Semi-transparent color overlay on the grid (blue→red gradient) | + +All debug rendering is toggled via a "Debug" button in the viewer toolbar. +When off, no debug data is shown (default for spectators). When on, the +viewer shows the selected player's debug output. + +**Replay size impact:** + +A bot sending 5 KB of debug data per turn across 500 turns adds 2.5 MB +to the replay. With gzip compression (~90% on structured JSON), that's +~250 KB. Acceptable alongside the ~50 KB base replay. + +**Why it matters:** This is a visual debugger for distributed bot code. +Instead of reading logs, developers watch their bot's thought process +alongside its actions. For spectators who opt in, seeing "the bot is +scared of the northern cluster" while watching it move south creates +narrative that no commentary system can match. + +### 13.2 Territory Control Heatmap Overlay + +The replay viewer supports three visualization modes, toggled via a toolbar +dropdown. All computed client-side from bot positions — no server cost. + +**Mode 1: Dots (default)** + +The current view — bots as colored circles on the grid. Minimal, clean, +fast. + +**Mode 2: Voronoi Territory** + +Each tile on the grid is colored by which player's nearest bot is closest. +Creates clean territorial borders that shift each turn. + +``` +Computation per turn: + for each visible tile (row, col): + min_dist = infinity + owner = none + for each bot on the grid: + d = toroidal_distance_squared(tile, bot) + if d < min_dist: + min_dist = d + owner = bot.owner + tile_color = player_colors[owner] at 30% opacity +``` + +For a 60×60 grid with 50 bots, that's 3,600 × 50 = 180,000 distance +calculations per turn — trivial for modern JS (~1ms). The result is a +per-tile color array rendered as a single full-grid Canvas `fillRect` pass +underneath the bot sprites. + +**Mode 3: Influence Gradient** + +Force projection based on bot count and distance. Each player's influence +at a tile is the sum of `1 / (1 + distance)` across all their bots. +Rendered as a smooth gradient: + +``` +for each visible tile: + influence = [0, 0, ..., 0] // per player + for each bot: + d = toroidal_distance(tile, bot) + influence[bot.owner] += 1.0 / (1.0 + d) + dominant = argmax(influence) + strength = influence[dominant] / sum(influence) + tile_color = player_colors[dominant] at (strength × 50%) opacity +``` + +The gradient creates a softer, more organic visualization than Voronoi — +you can see where influence is strong (dense, saturated) vs weak (faint, +contested). Frontlines appear as narrow bands where no player dominates. + +**Performance:** both modes compute in <5ms per turn on a 60×60 grid. +The replay viewer caches the overlay bitmap per turn and only recomputes +on turn change. At 32 turns/second (16× speed), this stays within frame +budget. + +**Toolbar UI:** + +``` +View: [Dots ▼] [Dots | Territory | Influence] +``` + +Switching modes is instant — the underlying replay data doesn't change, +only the rendering pipeline. + +### 13.3 Embeddable Replay Widget + +A lightweight, standalone replay player that works in an iframe anywhere. + +**URL format:** +``` +https://aicodebattle.com/embed/{match_id} +https://aicodebattle.com/embed/{match_id}?start=87&speed=4&mode=territory +``` + +**Query parameters:** + +| Param | Default | Description | +|-------|---------|-------------| +| `start` | 0 | Starting turn | +| `speed` | 2 | Playback speed (1, 2, 4, 8, 16) | +| `mode` | dots | Visualization mode (dots, territory, influence) | +| `autoplay` | true | Start playing on load | +| `controls` | true | Show play/pause and speed controls | + +**Widget design:** + +Stripped-down replay viewer: canvas + minimal controls bar. No scrubber, +no side panel, no fog-of-war toggle. Just the match playing. + +``` +┌──────────────────────────────┐ +│ │ +│ [Canvas] │ +│ │ +├──────────────────────────────┤ +│ ▶ 2x SwarmBot 3 — 1 Hunter │ +│ Watch full ↗ │ +└──────────────────────────────┘ +``` + +"Watch full" links to the main replay page on aicodebattle.com. + +**Implementation:** + +- Separate route on Cloudflare Pages: `/embed/{match_id}` +- Loads the same replay JSON from R2 +- Renders with the same Canvas engine, minus chrome +- Total bundle: ~50 KB (JS + CSS) +- Open Graph tags for rich previews when pasting the URL: + ```html + + + + ``` +- Thumbnail: auto-generated PNG of the final turn state, created by the + index rebuilder using OffscreenCanvas in a Worker (or pre-rendered by + the match worker) + +**Cloudflare free tier impact:** embed loads are Pages requests (unlimited). +The replay JSON fetch is an R2 Class B read — already accounted for in the +existing budget. + +### 13.4 Replay Playlists + Auto-Curation + +Automatically curated collections of replays, browsable from the static +site's landing page. + +**Playlist definitions:** + +| Playlist | Query Criteria | Rebuild Frequency | +|----------|---------------|-------------------| +| "Closest Finishes" | `final_score_diff <= 1` sorted by `win_prob_crossings DESC` | Every 2 min (index cron) | +| "Biggest Upsets" | `winner_rating - loser_rating <= -150` | Every 2 min | +| "Best Comebacks" | `min(win_prob) < 0.2 AND winner = underdog` | Every 2 min | +| "Evolution Breakthroughs" | Evolved bot's first win against a top-10 bot | Every 2 min | +| "Rivalry Classics" | Matches between detected rivals, sorted by closeness | Every 2 min | +| "This Week's Highlights" | Top 10 by community upvote count (from §12.6) | Every 2 min | +| "New Bot Debuts" | First match of each newly registered bot | Every 2 min | +| "Season Highlights" | Top 20 matches of the current season by engagement | Every 2 min | + +**R2 storage:** `data/playlists/{slug}.json` + +```json +{ + "name": "Closest Finishes", + "description": "Matches decided by a single point or less", + "updated_at": "2026-03-23T14:35:00Z", + "matches": [ + { + "match_id": "m_7f3a9b2c", + "players": ["SwarmBot", "HunterBot"], + "scores": [3, 2], + "date": "2026-03-23T14:30:00Z", + "thumbnail_url": "https://data.aicodebattle.com/thumbnails/m_7f3a9b2c.png", + "enriched": true + } + ] +} +``` + +**Static site UI:** landing page shows playlists as horizontal scrollable +rows (Netflix-style). Each card shows a thumbnail, player names, and score. +Click opens the replay. + +**Cloudflare free tier impact:** playlist JSONs are tiny (<50 KB each). +They're rebuilt by the existing index rebuilder cron — just additional D1 +queries and R2 writes within existing budget. + +### 13.5 Prediction System + +Visitors predict outcomes of upcoming notable matches. Correct predictions +earn reputation. A prediction leaderboard tracks the best analysts. + +**Which matches get predictions:** + +The matchmaker flags a match as "predictable" when: +- Both bots are in the top 20 +- It's a rivalry match +- It's a series match (§13.7) +- An evolved bot faces a top-10 human-written bot + +At ~60 matches/hour, roughly 5–10% are flagged — about 3–6 per hour. + +**Flow:** + +1. Scheduler creates a match job with `predictable: true` +2. Worker API writes the match to a `predictions_open` state in D1 +3. Static site shows "Upcoming Matches" with a predict button +4. Visitor clicks a player to predict (stored via `POST /api/predict`) +5. Prediction window: open from job creation until the match starts + executing (typically 1–5 minutes) +6. Match executes normally +7. On result submission, Worker resolves predictions in D1 +8. Index rebuilder updates the prediction leaderboard JSON in R2 + +**D1 schema:** + +```sql +CREATE TABLE predictions ( + prediction_id TEXT PRIMARY KEY, + match_id TEXT NOT NULL, + predictor_id TEXT NOT NULL, -- localStorage-generated UUID + predictor_name TEXT, -- optional display name + predicted_winner INTEGER NOT NULL, + correct INTEGER, -- null until resolved + created_at TEXT NOT NULL +); + +CREATE TABLE predictor_stats ( + predictor_id TEXT PRIMARY KEY, + predictor_name TEXT, + correct INTEGER NOT NULL DEFAULT 0, + incorrect INTEGER NOT NULL DEFAULT 0, + streak INTEGER NOT NULL DEFAULT 0, + best_streak INTEGER NOT NULL DEFAULT 0, + rating REAL NOT NULL DEFAULT 1000.0 +); +``` + +Predictor rating uses a simplified Elo: correct prediction on a balanced +match (close ratings) = small gain; correct prediction on a heavy underdog += large gain. + +**Cloudflare free tier check:** + +| Metric | Usage | Limit | +|--------|-------|-------| +| D1 writes | ~6 predictions/match × 6 matches/hour × 24h = ~864/day | 100K/day | +| D1 reads | ~50 leaderboard reads/day | 5M/day | +| Worker requests | `POST /api/predict` ~864/day | 100K/day | + +Comfortably within limits. Even at 10× the assumed prediction volume +(8,640/day), still under 9% of the write limit. + +**Static site UI:** + +- "Predictions" page showing upcoming predictable matches with bot profiles + and head-to-head records +- One-click predict button (no login required — UUID from localStorage) +- After match: result shown with "You were right/wrong" + points earned +- Prediction leaderboard: top 50 analysts ranked by prediction rating + +### 13.6 Map Evolution + +Maps evolve alongside bots. High-engagement maps breed to produce new maps. +Low-engagement maps retire. User feedback and positional fairness monitoring +ensure quality. + +**Engagement scoring:** + +After each match, the map receives an engagement score: + +``` +engagement = ( + win_prob_crossings × 3.0 + + critical_moments × 2.0 + + map_coverage_pct × 1.0 + + closeness × 2.0 + + avg_turn_count / max_turns × 1.0 +) + +where: + closeness = 1.0 - (abs(score_diff) / max(total_score, 1)) + map_coverage_pct = tiles_visited_by_any_bot / total_open_tiles +``` + +The map's engagement score is the rolling average across its last 20 matches. + +**Positional fairness monitoring:** + +A map is **positionally fair** if no starting position has a systematic +advantage. Monitored by tracking win rate per player slot: + +```sql +SELECT + map_id, + player_slot, + COUNT(*) AS games, + AVG(CASE WHEN winner = player_slot THEN 1.0 ELSE 0.0 END) AS win_rate +FROM match_participants mp +JOIN matches m ON m.match_id = mp.match_id +GROUP BY map_id, player_slot +HAVING COUNT(*) >= 20 +``` + +If any player slot's win rate deviates from the expected rate (1/N for +N-player maps) by more than **10 percentage points** across 20+ matches, +the map is flagged as **unfair** and removed from the competitive pool. + +Example: on a 2-player map, if player slot 0 wins 62% of the time after +20 matches, the map is flagged (62% - 50% = 12% > 10% threshold). + +**User map voting:** + +After watching a replay, visitors can upvote or downvote the map (not the +match — the map). Stored in D1: + +```sql +CREATE TABLE map_votes ( + vote_id TEXT PRIMARY KEY, + map_id TEXT NOT NULL, + voter_id TEXT NOT NULL, -- localStorage UUID + vote INTEGER NOT NULL, -- +1 or -1 + created_at TEXT NOT NULL, + UNIQUE(map_id, voter_id) +); +``` + +Map voting influences the evolution system: +- Maps with net negative votes get a 0.5× engagement multiplier (less likely + to breed) +- Maps with >10 net positive votes get a 1.5× multiplier +- Maps with >20 net negative votes are force-retired regardless of engagement + +The replay viewer shows a simple 👍/👎 widget for the map (not the bots) +alongside map metadata (name, dimensions, wall density, energy count). + +**Breeding algorithm:** + +Runs weekly on the evolver (Rackspace Spot). Produces ~5 new maps per +player-count tier. + +``` +1. Select parents: + - Top 5 maps by engagement × vote_multiplier for this player count + - Weighted random: higher engagement = more likely to be selected + +2. Crossover: + - Divide parent maps into quadrants (or thirds for 3/6-player) + - Randomly select quadrants from each parent + - Compose into a new map + +3. Apply symmetry: + - Generate one sector from the composed quadrants + - Mirror/rotate to fill the full map for the target player count + - This guarantees positional fairness by construction + +4. Mutate: + - Randomly flip 5-10% of tiles (wall ↔ open) + - Shift 1-3 energy node positions by 1-3 tiles + - Apply cellular automata smoothing (2 iterations) to avoid + jagged walls + +5. Validate: + - BFS from every core must reach every other core + - BFS from every core must reach ≥3 energy nodes + - Open area per player must be between 900 and 5000 tiles + - Wall density must be between 5% and 30% + +6. Smoke-test: + - Run 3 matches with built-in bots on the candidate map + - Engagement score must exceed 50th percentile of current pool + - If failed: discard and retry (max 3 attempts per candidate) + +7. Add to pool: + - Store map JSON in R2 + - Insert into D1 maps table with `status: 'active'` + - Available for matchmaking in the next scheduler cycle +``` + +**Lifecycle:** + +| Status | Meaning | +|--------|---------| +| `active` | In the matchmaking pool, eligible for competitive play | +| `probation` | Fairness flag triggered — under review, still playable | +| `retired` | Removed from pool (low engagement, unfair, or force-retired) | +| `classic` | Top 5 all-time maps, immune from retirement | + +- Active pool: 50 maps per player count (2, 3, 4, 6) +- New maps: ~5 per week per player count +- Retirement: bottom 10% by engagement score pruned monthly +- Classic promotion: maps that sustain top-5 engagement for 3+ months + +### 13.7 Multi-Game Series + +Best-of-N matches between two bots across different maps. Series produce +more meaningful ratings than single matches and create narrative arcs. + +**Series types:** + +| Type | Games | Trigger | +|------|-------|---------| +| Best-of-3 | 3 | Auto-scheduled for top-20 bots, 1 per day per bot | +| Best-of-5 | 5 | Weekly featured series between top rivalries | +| Best-of-7 | 7 | Season championship bracket (§13.9) | + +**Map selection for series:** + +Each game in a series uses a different map, selected to test different +strategic dimensions: + +``` +Game 1: Map with highest engagement score (the "classic") +Game 2: Map with highest wall density in pool (corridors/chokepoints) +Game 3: Map with lowest wall density in pool (open field) +Game 4: Most recently evolved map (untested terrain) +Game 5+: Random from remaining pool +``` + +This ensures series test bot adaptability, not just performance on one +map type. + +**D1 schema:** + +```sql +CREATE TABLE series ( + series_id TEXT PRIMARY KEY, + bot_a_id TEXT NOT NULL, + bot_b_id TEXT NOT NULL, + format INTEGER NOT NULL, -- 3, 5, or 7 + status TEXT NOT NULL DEFAULT 'pending', + a_wins INTEGER NOT NULL DEFAULT 0, + b_wins INTEGER NOT NULL DEFAULT 0, + season_id TEXT, + created_at TEXT NOT NULL, + completed_at TEXT +); + +CREATE TABLE series_games ( + series_id TEXT NOT NULL, + game_number INTEGER NOT NULL, + match_id TEXT, -- null until played + map_id TEXT NOT NULL, + winner INTEGER, + PRIMARY KEY (series_id, game_number) +); +``` + +**Execution:** + +The scheduler creates all games in a series as pending jobs with sequential +ordering. Workers execute them in order (game 2 doesn't start until game 1 +completes). If either bot reaches the winning threshold (2 for bo3, 3 for +bo5, 4 for bo7), remaining games are skipped. + +**Rating impact:** + +Series results contribute to Glicko-2 ratings as follows: +- Each individual game in the series contributes to the pairwise rating + update (same as a single match) +- The series winner gets a bonus rating adjustment of +10 mu (small but + meaningful — rewards series consistency) + +**Replay presentation:** + +The series page (`/series/{series_id}`) shows all games as a unified +experience: + +``` +SwarmBot vs HunterBot — Best of 5 (Season 4 Semifinals) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Game 1 ✓ SwarmBot 3-1 Map: The Labyrinth [Watch] +Game 2 ✓ HunterBot 2-4 Map: Open Expanse [Watch] +Game 3 ✓ HunterBot 1-3 Map: Coral Reef [Watch] +Game 4 ??? [Reveal] +Game 5 ??? [Reveal] + +Series: HunterBot leads 2-1 +``` + +**Spoiler toggle:** by default, future games are hidden ("???"). Viewers +click "Reveal" to show the result — or "Watch All" to experience the +series sequentially with auto-advancing between games. + +### 13.8 Match Event Timeline + +A horizontal event ribbon below the replay canvas showing significant +events as colored, clickable icons. + +**Event types:** + +| Icon | Event | Trigger | +|------|-------|---------| +| ⚔️ | Combat | 2+ bots died this turn | +| 🏰 | Core captured | A core was razed | +| 💎 | Energy milestone | Player collected 3+ energy in one turn | +| 💀 | Mass death | 5+ bots died this turn | +| 📈 | Momentum shift | Win probability crossed 50% | +| 🌟 | Critical moment | Win probability shifted >15% | +| 🐣 | Spawn wave | 3+ bots spawned this turn | + +**Implementation:** + +Events are extracted client-side from the replay data on load. For each +turn, check the events array (deaths, captures, spawns, energy_collected) +against the trigger thresholds. Win probability events come from the +`win_prob` and `critical_moments` arrays already in the replay. + +**Rendering:** + +``` +┌──────────────────────────────────────────────────┐ +│ [Canvas] │ +├──────────────────────────────────────────────────┤ +│ Win Prob: ~~~~~~~~~/\~~~~~/\~~~~/\~~~~~~ │ ← sparkline +├──────────────────────────────────────────────────┤ +│ Events: ·💎·····⚔️··💎···🏰⚔️···💎···⚔️💀··🏰🌟│ ← timeline +├──────────────────────────────────────────────────┤ +│ ◄ ▶ ⏸ Turn 203/500 Speed: 4x View: [Dots]│ ← controls +└──────────────────────────────────────────────────┘ +``` + +- Icons are positioned proportionally along the timeline by turn number +- Hovering an icon shows a tooltip: "Turn 87: 3 bots killed in eastern + corridor" +- Clicking an icon scrubs the replay to that turn +- Dense clusters of icons indicate "hot zones" of activity — visually + obvious even at a glance +- The timeline is rendered as an HTML element overlaid on the viewer + (not Canvas) for accessibility and hover interactions + +The event timeline and win probability graph work together: the graph +shows the *trend*, the timeline shows the *moments*. A viewer can scan +the timeline for icon clusters, then check the win probability graph to +see if those moments mattered. + +### 13.9 Seasonal Rotations + +The platform runs in **seasons** — 4-week competitive periods with a fresh +map pool, a new ladder, and a theme. Seasons provide urgency, freshness, +and a reason to come back. + +**Season structure:** + +| Week | Phase | Description | +|------|-------|-------------| +| 1 | Discovery | New map pool + theme released. All bots start at default rating. Exploration matches. | +| 2–3 | Competition | Main ladder. Matchmaking intensifies. Mid-season stats published. | +| 4 | Championship | Top 8 bots by rating enter a best-of-7 bracket. Season champion crowned. | +| Between | Break (3 days) | New maps bred via map evolution. Season archive published. | + +**What resets each season:** +- Glicko-2 ratings (mu/phi/sigma reset to defaults) +- Map pool (evolved maps from previous season + new generated maps) +- Prediction standings +- Playlist contents + +**What persists:** +- Bot registrations and endpoints (bots don't re-register) +- All-time records and historical season archives (browsable) +- Evolution population (continues across seasons, adapts to new maps) +- Community feedback and replay annotations + +**D1 schema:** + +```sql +CREATE TABLE seasons ( + season_id TEXT PRIMARY KEY, + name TEXT NOT NULL, + theme TEXT NOT NULL, + rules_version INTEGER NOT NULL, + started_at TEXT NOT NULL, + ended_at TEXT, + champion_id TEXT, + status TEXT NOT NULL DEFAULT 'active' +); +``` + +**Season themes and game rule versioning:** + +Each season can introduce **minor rule variations** that keep the meta +fresh. The critical constraint: **existing bots must continue to work +without modification.** This is achieved through additive, optional +changes only. + +**Backward compatibility rules:** + +``` +ALLOWED per-season changes (additive, non-breaking): + ✓ New tile types that bots can ignore (treated as open by old bots) + ✓ New optional fields in the game state JSON (old bots ignore them) + ✓ Adjusted numeric parameters within the existing schema: + - vision_radius2, attack_radius2, spawn_cost, energy_interval + - These are sent in the config object each match — bots that read + config adapt automatically; bots that hardcode values still work + but may be suboptimal + ✓ New scoring bonuses (additive to existing scoring) + ✓ Map pool changes (different maps, not different map format) + +FORBIDDEN (would break existing bots): + ✗ Removing or renaming existing fields in game state / move schema + ✗ Changing the meaning of existing fields + ✗ New required fields in the move response + ✗ Changing the coordinate system or grid topology + ✗ Removing movement directions (N/E/S/W) + ✗ Changing the turn structure (phases must remain in the same order) +``` + +**Example seasonal themes:** + +| Season | Theme | Rule Variation | +|--------|-------|---------------| +| 1 | "The Labyrinth" | High wall density maps, `vision_radius2: 36` (reduced from 49) | +| 2 | "Energy Rush" | `energy_interval: 5` (doubled production), `spawn_cost: 2` (cheaper bots) | +| 3 | "Fog of War" | `vision_radius2: 25` (heavily reduced), new optional `sonar` field in game state showing approximate enemy count per quadrant | +| 4 | "The Colosseum" | `attack_radius2: 8` (extended range), open maps, aggressive meta | +| 5 | "Shifting Sands" | New tile type `quicksand` in game state (bots that don't handle it treat it as open — they can enter but movement costs 2 turns) | + +For season 5's `quicksand` example: the game state sends +`{ "row": 15, "col": 20, "type": "quicksand" }` in a new `terrain` array. +Old bots that don't read `terrain` still function — they walk through +quicksand unknowingly (and get slowed). New bots that parse `terrain` can +avoid quicksand tiles, gaining a strategic edge. This creates an incentive +to update bots each season without *forcing* anyone to. + +**Season config in the match protocol:** + +The game state's `config` object already includes all tunable parameters. +Seasonal changes are just different values: + +```json +{ + "config": { + "season_id": "s4", + "season_name": "The Colosseum", + "rules_version": 4, + "rows": 60, + "cols": 60, + "max_turns": 500, + "vision_radius2": 49, + "attack_radius2": 8, + "spawn_cost": 3, + "energy_interval": 10, + "special_tiles": ["quicksand"] + } +} +``` + +Bots that read `config.attack_radius2` adapt automatically. Bots that +hardcode `attack_radius2 = 5` still work but use stale assumptions. +`special_tiles` is a new array listing any non-standard tile types in +play — old bots that don't read it are unaffected. + +**Season archive:** + +Each completed season gets an archive page (`/season/{season_id}`): +- Champion + top 10 + bracket results +- Most improved bot (biggest rating gain) +- Best newcomer (highest-rated bot registered this season) +- Most watched match (by replay view count) +- Evolution highlights (best evolved bot, most creative strategy) +- Map of the season (highest engagement score) +- All replays preserved and browsable + +**Season championship bracket:** + +In week 4, the top 8 bots enter a single-elimination bracket of best-of-7 +series (§13.7). The bracket is published on the season page with live +updates as series complete. + +``` +Quarterfinals: + #1 SwarmBot vs #8 NewBot → SwarmBot (4-1) + #4 GathererBot vs #5 RusherBot → RusherBot (4-3) + #3 HunterBot vs #6 evo-go-g12 → HunterBot (4-2) + #2 GuardianBot vs #7 evo-py-g8 → GuardianBot (4-0) + +Semifinals: + SwarmBot vs RusherBot → SwarmBot (4-2) + HunterBot vs GuardianBot → HunterBot (4-3) + +Finals: + SwarmBot vs HunterBot → ??? +``` + +### 13.10 Bot Profile Cards + +Auto-generated visual cards summarizing a bot's identity, stats, and +character in a single shareable image. + +**Card generation:** + +The card is rendered as a PNG via OffscreenCanvas (in the browser on +demand, or pre-rendered by the index rebuilder for top-50 bots). + +**Card content:** + +``` +┌─────────────────────────────────┐ +│ │ +│ SwarmBot #3 │ +│ by alice Rating: 1820 │ +│ │ +│ ┌─────────────────────────┐ │ +│ │ Archetype: │ │ +│ │ FORMATION SWARM │ │ +│ │ │ │ +│ │ Season 4 · 142 games │ │ +│ └─────────────────────────┘ │ +│ │ +│ Win Rate 69% ████████░░ │ +│ vs Rushers 82% █████████░ │ +│ vs Turtles 45% ████░░░░░░ │ +│ │ +│ Signature: Eastern corridor │ +│ push on 4-player maps │ +│ │ +│ Rival: HunterBot (11-11-1) │ +│ │ +│ ⚔️ 847 kills 💎 2.1k energy │ +│ 🏰 23 captures 📈 +320 Elo │ +│ │ +│ aicodebattle.com │ +└─────────────────────────────────┘ +``` + +**Data sources (all from existing bot profile JSON):** + +| Field | Source | +|-------|--------| +| Rating, rank | Leaderboard | +| Archetype | Strategy classifier from behavioral features (§12 evolution meta) | +| Win rate breakdown | D1 query: wins vs each archetype cluster | +| Signature | Most statistically distinctive behavior vs population average | +| Rival | From rival detection (§12.5) | +| Kill/energy/capture stats | Aggregate from match_participants | + +**"Signature" computation:** + +For each bot, compare its behavioral features (aggression, economy, +exploration, formation) to the population mean. The dimension where the +bot deviates most is its signature. Combined with map-type analysis: + +``` +if bot.aggression is 2σ above mean AND best_map_type == "4-player": + signature = "Aggressive multi-front warfare on 4-player maps" +if bot.economy is 1.5σ above mean AND bot.exploration > 80%: + signature = "Full-map economic dominance" +``` + +Template-generated from ~20 signature patterns. + +**Sharing:** + +- "Share Card" button on the bot profile page generates a PNG download +- Direct URL: `https://aicodebattle.com/card/{bot_id}.png` + - Served as a static PNG from R2 (pre-rendered for top-50 bots) + - Or rendered on-demand via a Worker that reads the bot profile JSON, + draws to Canvas (using `@cloudflare/workers-types` Canvas API or + a pre-built image template), and returns the PNG +- Open Graph tags on the URL so pasting it into Twitter/Discord/Slack + shows the card as a rich preview: + ```html + + + ``` +- The card image includes the platform URL as a watermark, driving traffic