---
title: 'Arena Fighter: a self-contained game brief for a fresh mind'
summary: 'Complete, method-neutral description of the Victris Arena Fighter game (rules v0.22.1): grid, maps, fighters, weapons, tick order, fog, zone, protocol limits, ladder and matchmaking, what real matches look like, and the resources available to a bot author. Written to be pasted into a fresh context to get its intuitive approach.'
updated: '2026-10-25'
canonical: https://unaen.org/llmwiki/topics/saintcon/arena-game-brief
markdown: https://unaen.org/llmwiki/topics/saintcon/arena-game-brief.md
---

# Arena Fighter: game brief

This page describes a game completely and neutrally, so it can be pasted into a fresh context. It says nothing about
how anyone currently plays it. The question for the reader is at the end.

## 1. What it is

**A real-time ladder of 1v1 bot duels.** Each bot is your program, connected over a WebSocket.
- Every tick the server sends each bot an observation. The bot replies with one turn.
- The game is simultaneous: both turns resolve together.
- Each fighter sees only part of the map (fog of war).

**Match length.** A match is at most **600 ticks** (60 s at 10 ticks/s, for playback only; bots think in ticks).
- The last fighter standing wins.
- If the match reaches tick 600: higher remaining HP **percentage** wins. On a tie, more total damage dealt wins. Otherwise it's a draw.

**No luck.** The engine is deterministic: integer math, one seeded PRNG used only for setup such as spawn assignment.
Once both turns are known, nothing is random.

## 2. The grid and maps

- **Grid:** 21×21 tiles. Positions are `[x, y]`, with origin top-left, x east and y south.
- **Distance:** Chebyshev, `max(|dx|,|dy|)`. A diagonal step costs the same as an orthogonal one.
- **Directions:** 8, `N NE E SE S SW W NW`. A diagonal step may not cut past a wall corner.
- **Tiles:** floor `.` or wall `#`. Walls block movement, shots and sight.
- **Spawns:** `[3,10]` and `[17,10]` on every map, assigned by seed.
- **Pickup pads:** 4 per map, all on the centre column.
  - **health** at `[10,2]` and `[10,18]`.
  - **power** at `[10,8]` and `[10,12]`.

Each match uses one of three maps, chosen evenly (about a third each):

```
arena-21 (empty)        columns-21              pillars-21
.....................   .....................   .....................
.....................   .......#.....#.......   .....................
.....................   .....................   .....................
.....................   .....................   .....................
.....................   ....#..#..#..#..#....   ..........#..........
.....................   .....................   ..........#..........
.....................   .....................   ......##.....##......
.....................   .#..#..#..#..#..#..#.   ......#.......#......
.....................   .....................   .....................
.....................   .....................   .....................
.....................   .......#..#..#.......   ..........#..........
.....................   .....................   .....................
.....................   .....................   .....................
.....................   .#..#..#..#..#..#..#.   ......#.......#......
.....................   .....................   ......##.....##......
.....................   .....................   ..........#..........
.....................   ....#..#..#..#..#....   ..........#..........
.....................   .....................   .....................
.....................   .......#.....#.......   .....................
.....................   .....................   .....................
.....................   .....................   .....................
```

## 3. Fighters

- **Health:** 130 HP, no regeneration, armor 0. Damage is `max(1, base − armor)`, scaled by the attacker's power buff.
- **Facing and resources:** no facing, no stamina, no ammo. Every limit is a cooldown. A cooldown of *c* means the ability is usable again *c* ticks after use.
- **States:**

  | State | Meaning |
  |---|---|
  | `idle` | can act |
  | `recovering` | 1 tick after a dash; can't act |
  | `stunned` | 3 ticks after a wall slam; can't act |
  | `dead` | out of the match |

## 4. Actions (one turn per tick)

A turn has **a movement part** (a step **or** a dash, or neither), plus optionally **one attack**, plus an optional **shout**. Movement resolves first, and the attack fires from where you end up. Each part is validated independently: a refused attack doesn't cancel the move, and the other way round.

**Movement**

| Action | Effect | Cooldown | Notes |
|---|---|---|---|
| step | 1 tile in 8 directions | 2 ticks, so 5 tiles/s | Losing a move conflict still starts the cooldown. |
| dash | exactly 3 tiles in a direction | 12 ticks | Stops early at a wall, edge or fighter. **Invulnerable on the dash tick.** 1 tick of `recovering` after. Only the landing tile collects a pickup. Usable while your step is on cooldown. |

**Attacks.** Each has its own cooldown, and all fire **the tick they are sent**: no windup, no recovery, no telegraph.

| Attack | Damage | Cooldown | What it does |
|---|---|---|---|
| `ranged` | 12 | 6 | A projectile along the exact line from your tile centre through a target tile centre, at **any angle**. Flies **2 tiles/tick** (Chebyshev along the line's major axis), no range limit. **Bounces once** off a flat wall; the map edge counts as a wall, and a corner or second wall stops it. Hits the first fighter or wall whose tile the line passes through (grazing a corner doesn't count). Within 2 tiles it lands the same tick, with zero warning. |
| `frostburst` | 14 | 14 | Cast instantly on a **visible** non-wall tile within **7**. Creates a 3×3 area, with walls shielding tiles behind them. After a **6-tick fuse** it explodes on every opponent still inside, whichever of the 9 tiles they're on. Until then, each step *starting* inside the area costs +2 move cooldown (slow). Dash and attacks aren't slowed. |
| `repulsor` | 5 | 12 | No aim. Hits every opponent on the 8 tiles around you and shoves each **3 tiles straight away**, tile by tile. Into a wall: **wall slam**, +10 damage and **stunned 3 ticks**. Into another fighter: 5 damage to both. Lands the same tick. |

**Power buff.** Taking a power pad gives weapon damage ×1.5, rounded down, for 50 ticks: ranged 18, frostburst 21, repulsor 7. It also boosts the damage your repulsor causes by shoving: wall slam 10 → 15, collision 5 → 7 to each fighter (the `collision` event still reports 5). It doesn't boost zone damage. It applies when damage lands, not when the shot is fired.

**Health pad:** +32 HP, capped at 130.

**Pads.** Every pad is stocked on tick 20. Health respawns 120 ticks after it's taken, power 200. You take one by **ending** your move or dash on it; walking or dashing across doesn't count.

**Shout** (≤40 characters, 10-tick cooldown): text every fighter receives next tick, in sight or not. It reveals who spoke, not where. It has no game effect.

## 5. Tick resolution order (simultaneous)

1. Validate both turns. A missing or late turn becomes `idle`.
2. **Dashes** move, and dashers are invulnerable this tick.
3. **Steps** resolve together. If both target the same tile, or the two would swap tiles, neither moves. Stepping into a tile the other is leaving is allowed.
4. **Pickups** are taken by a fighter standing on a stocked pad. Power applies immediately, so it boosts this tick's attack.
5. **Attacks:**
   1. Repulsors resolve first, from post-move positions.
   2. Frostburst areas are created.
   3. New shots spawn on the shooter's tile, and **every projectile advances 2 tiles**. Each tick's segment includes the tile it started in, so stepping *into* a shot's path is a hit.
   4. Frostburst areas whose fuse ends now explode.
6. **Zone** damage applies, and the zone shrinks if due.
7. **Deaths:** both dead the same tick is a draw.
8. Cooldowns tick down, invulnerability ends, pads restock, and observations are sent.

Both fighters can hit each other on the same tick, and both hits land. Two projectiles pass through each other.

## 6. Shrinking zone

- **Shape:** a Chebyshev square around the centre `[10,10]`. Radius 10, which is the whole map, until tick 259.
- **Shrinks:** to 9 at tick **260**, 7 at **320**, 5 at **380**, 3 at **440**, 1 at **500**.
- **Warning:** each shrink is announced 20 ticks ahead (`zone.shrinks_in`, `zone.next_radius`).
- **Damage outside:** 5 per tick, plus 5 for every 20 consecutive ticks outside. The counter resets on a tick that ends inside.
- **No escape:** by tick 500 the safe area is the 3×3 around the centre.

## 7. Fog of war and information

**Sight.** You see tiles within Chebyshev **8** that have clear line of sight, using the same line test as projectiles. Sight is symmetric: if you see them, they see you. Fighters, projectiles, frostburst areas and pads outside sight are hidden. The static map is always known.

**What each observation contains:**
- **Your own state:** position, HP, state, slow, buffs, every cooldown, and the remaining time budget.
- **Visible enemies:** position, **exact HP**, **dash cooldown**, state, slow and buffs. Their **attack cooldowns are hidden**, but every attack you see produces an `attack` event naming the weapon, so you can count from it.
- **Visible projectiles:** exact position in 1/1000 tile, origin, target, bounces left.
- **Visible frostburst areas:** tiles and ticks left.
- **Visible pads:** stock state and time to restock.
- **Zone.**
- **`moves` / `dashes`:** your legal steps and dashes this tick, with landing tiles.
- **`danger`:** every tile a *visible* opponent projectile or area will hit, and on which tick, computed by the engine's own code. Attacks sent this tick aren't listed yet, and point-blank shots and repulsors never appear because they land instantly.
- **`events`:** attacks, hits (with a ricochet flag), pickups, shouts and rejections, for what happened to or near you last tick.

**Time is the hidden state.** Facts about it:
- **Every limit is an integer tick count:**

  | Action or effect | Ticks |
  |---|---|
  | step | 2 |
  | dash | 12, plus 1 recovering |
  | ranged | 6 |
  | frostburst | 14, with a 6-tick fuse |
  | repulsor | 12 |
  | stun | 3 |
  | power buff | 50 |
  | health / power pad respawn | 120 / 200 |
  | zone shrinks | every 60, warned 20 ahead |

- **Your own timers are exact:** every cooldown, `state_ticks_left`, slow and buffs.
- **Visible about the opponent:** HP, **dash** cooldown, state with `state_ticks_left`, slow, and buffs.
- **Not visible:** the opponent's **step cooldown** and **attack cooldowns**.
- **The opponent's step cadence must be inferred.** A fighter can step at most every other tick, and a lost move conflict still starts its cooldown, so whether the opponent can step *this* tick depends on when it last stepped. Steps aren't reported; they show only as position changes while the opponent is visible.
- **Attack cooldowns can only be counted from sightings.** Opponent attacks appear as `attack` events only when seen, so a timer can only count from the last sighting.
- **Observations carry no history.** An observation is a snapshot of the current tick plus the previous tick's events. Anything about earlier ticks exists only if the bot remembers it.

**First contact:** on these maps the fighters usually see each other almost immediately. First sight is tick 5 in the median and tick 7 at the 75th percentile, measured over 9.6k live matches.

## 8. Protocol and time limits

- **Turn deadline:** 1,000 ms after each observation. Late or missing turns are played as `idle`.
- **Match budget:** **300,000 ms** of total response time. That's an average of 500 ms/tick if all 600 ticks are played. When it runs out, every remaining turn is `idle`.
- **Startup and messages:** 10 s startup. Messages are capped at 4 KB. More than 5 messages in one tick closes the connection.
- **Disqualification:** 10 consecutive protocol failures (timeout, malformed message, wrong tick).
- **Rules delivery:** the `hello` message carries the full rules table, the map and pads, and the opponent's **name** and bot id. The opponent's **version** is revealed only at the end.

## 9. Ladder, rating and matchmaking

**Ratings and pairing**
- **Rating:** Bradley–Terry ratings per bot version. The leaderboard shows rank, a score and win–loss records.
- **Scheduler:** every 2 s it pairs queued bots. It favours close expected scores (4p(1−p)), waiting time, fresh pairings, and less-tested pairs.
- **Discovery:** about 1 in 7 matches are discovery matches, which ignore the rating gap.
- **Maps** rotate so each version pair plays every map about equally.
- Same-owner pairs and house-vs-house pairs are never matched.

**Who's on the ladder**
- A public pool of dozens of active bots, plus official **house bots**: names like `house-champion`, `house-brawler`, `house-kiter`, `house-ts-starter`, `big-brother01..04` and `little-brother`.
- Bots change versions often, and a new version starts with no pair history.
- On 10-25 the top bot (`house-champion`, about 3,262 W / 933 L) was ~150 points above #2. The next ~20 bots sat within ~100 points of each other.

**Bot behaviour.** A bot knows its opponent's name from `hello`. Bots may legally tailor play per opponent and may change between versions. In a test of 36 opponents' openings, none measurably changed its opening by opponent name.

## 10. What real matches look like (9,598 live ladder matches, 10-22 to 10-25)

**How matches end**

| Reason | Matches |
|---|---|
| Knockout (`last_standing`) | 9,518 (99.2%) |
| Both died the same tick | 38 |
| Timeout, decided on HP | 20 |
| Disqualification | 17 |
| Forfeit | 3 |
| Timeout, decided on damage | 1 |
| Timeout draw | 1 |

**When matches end:** median **tick 248**. 10% end by tick 108, 25% by tick 150, 75% by tick 363, 90% by tick 442. **54% end before the zone first moves (tick 260).**

**Implications**
- **Two fighters each start with 130 HP.** Ranged does 12, so it takes about 11 unanswered shots to kill. Point-blank shots and repulsor slams are unblockable if you're standing still or pinned.
- **The zone forces a close fight in the second half.**
- **Timeouts are rare,** so the HP-percentage tiebreak almost never matters. In live play, standing still mostly just gets you killed.

## 11. Resources available to a bot author

- **The server's own simulator.** It ships as a JS module and a Python package with Gymnasium and PettingZoo environments. It's the exact match code, has no clock, and runs house bots in-process.
- **A complete archive of every ladder match:** about 130k+ replays, all bots. Each holds the header with rules, map and both bot names and versions, plus every turn of both fighters with reply times, and the result. Replays are full information, with no fog. Any fighter's fogged observations can be rebuilt from them.
- **Compute:**
  - Several home machines: CPUs, plus small GPUs with about 8 GB, 1070-Ti class.
  - A 48-thread CPU box.
  - Rentable GPUs on demand, for example RTX 5090s at about $0.60 an hour.
- **The live bot can run a search or a neural net per tick,** within the 1 s/tick and 300 s/match limits.

## 12. The question

Given only the above, how would you build the strongest bot for this ladder? Cover:
- **Approach:** search, learned policy, hand-written rules, a mix, and why.
- **What to learn from data versus self-play.**
- **How to handle fog and hidden opponent cooldowns.**
- **What to evaluate against,** and how to tell real progress from self-play overfitting.
- **Where the leverage is:** which mechanics or quirks decide most matches.

Give your intuitive plan first, then the main risks.

