Submitting a bot to Swarm Colosseum

Opens Saturday 2026-10-24, after a review of the sandbox that evening. Until then there is no way to send a file in, but everything below already works on your own machine, so you can get a bot ready. Bots sent in from the 24th join the league on the first night of season two, 2026-10-26. If the review finds something to fix first, the date moves and this page will say so.

Swarm Colosseum is a strategy game played by programs. You write one Python file. Every night it plays every bot on the other side, ten times each, and its results go on a public ladder. It's a game on a blank 2D map with made-up units, and it doesn't model any real equipment or place.

The game in one minute

Your file

One .py file with exactly these at the top level:

"""Idea: one sentence saying what your bot does differently. This line shows up on the
site, so make it good."""

NAME = "slow_pincer"        # 2-40 characters: lowercase letters, digits, _
ROLE = "attacker"           # or "defender"


def reset(seed):            # optional: called once before each match
    pass


def act(obs):               # called every tick; must return an action dict
    return {"launch": [], "steer": {}}

act gets one argument, obs (what your bot can see this tick), and returns a dictionary saying what to do. That's the whole interface.

If you're the attacker

obs contains:

keywhat it is
tickthe current tick, 1 to 600
budgetcredits you have left
asset{"pos": [0, 0], "hp": 30, "radius": 25}
dronesyour drones: {"id", "type", "pos", "vel", "waypoint"} each
interceptorspositions [x, y] of the defender's interceptors in the air
rulesevery number in the game, e.g. obs["rules"]["striker_cost"]

Return:

{"launch": [{"type": "striker", "bearing": 90.0}],   # up to 4 launches per tick
 "steer": {12: [100.0, -40.0]}}                      # optional: drone 12 flies there first

bearing is in degrees: 0 is to the right (+x), 90 is up (+y). A drone flies to its waypoint if it has one, then straight at the asset. Set a waypoint to None to clear it.

If you're the defender

obs contains:

keywhat it is
tickthe current tick
assetas above, with the current hp
tracksradar contacts: {"id", "pos", "vel", "range", "cls"} each. Positions are a bit noisy. cls is "unknown" until the drone is inside 150 u, then "striker" or "decoy".
interceptorsyours in the air: {"id", "target", "pos", "age"}
stockinterceptors left to launch
ammogun rounds left
rulesevery number in the game

Return:

{"intercept": [7, 9],        # launch at tracks 7 and 9 (up to 2 launches per tick)
 "retarget": {3: 11},        # interceptor 3 now chases track 11
 "gun": 7}                   # fire one round at track 7, or None

Rules for act

What you can and can't use

Allowed imports (and nothing else): math, random, itertools, functools, collections, heapq, bisect, statistics, dataclasses, typing, enum, operator.

Not allowed, and the checker will tell you which line it found:

Why so strict? Your bot runs on our machine next to everyone else's. The checks catch honest mistakes with a clear message, and every bot also runs inside a locked box: no internet, no files, 128 MB of memory, half a CPU core.

Limits

file size64 KB
time per act call2 seconds (hard limit)
memory128 MB
CPUhalf a core
network, files, other programsnone

Submitting

Once submissions open, you send a bot as a pull request that adds one file:

bots/submitted/pr/<your login>/<NAME>.py

<your login> is your GitHub login in lowercase, and the file name is your bot's NAME plus .py. One pull request can hold one attacker and one defender, and nothing else: if it changes any other file, the check fails. Send other changes as a separate pull request. To update a bot, change the same file in a new pull request.

An automatic check runs on the pull request. It only reads your file, the same checks as step 1 below, plus the abstract-only rule, and it tells you which line is wrong. It never runs your code. A maintainer then reviews the code and merges it. After that, the league's machine runs the checks again, then the test match in the locked box (steps 2 and 3).

You can run the same checks on your own machine first:

python -m colosseum.submit my_bot.py --side attacker --author yourhandle

Your handle is 2–24 characters: lowercase letters, digits, _ and -.

What happens:

  1. Checks. Your file is read (not run) and checked against the rules above. If something's wrong you get a plain-English reason with a line number, and nothing is kept.
  2. Test match. Your bot plays one match against a built-in bot, inside the locked box. If it crashes on every tick or stops answering, it's rejected, with the reason.
  3. Accepted. It's saved as yourhandle__yourbotname with a record of who sent it, when, and a fingerprint of the file (its SHA-256). Sending the exact same file again does nothing. Sending a changed file under the same bot name replaces the old version.

After that

A complete first bot

An attacker that sends a striker every 6 ticks, rotating around the ring:

"""Idea: a slow, even trickle from all directions, so the defender never gets a crowd to
hit with one interceptor."""

NAME = "even_trickle"
ROLE = "attacker"


def act(obs):
    cost = obs["rules"]["striker_cost"]
    if obs["tick"] % 6 != 0 or obs["budget"] < cost:
        return {"launch": [], "steer": {}}
    bearing = (obs["tick"] * 37) % 360
    return {"launch": [{"type": "striker", "bearing": bearing}], "steer": {}}

Good luck. Watch the replays of the bots that beat you; that's where the ideas are.

Licence. A bot you submit is contributed under the Apache License 2.0, the same terms as the rest of the code, so it can be run, replayed, shown and put in the tactic library.