Hack112 Arena
Team sign-in
Rulebook

How the arena works

The game: Paper.io, 4 players

You start with a 3×3 square of territory in one corner of a 40×40 board. Every tick, all four bots move one cell at the same time.

  • Trails. Outside your territory you leave a trail behind you. Step back onto your own territory and the trail, plus everything it enclosed, becomes yours.
  • Cutting. If anyone steps onto a trail, the trail's owner dies. That includes your own trail: crossing it kills you.
  • Collisions. Two heads on the same cell: both die. Driving off the board: you die.
  • Death. A dead player's territory and trail vanish immediately.
  • Reversing (e.g. LEFT while moving RIGHT) is ignored; you keep going straight. Before your first move you face the centre of the board.
  • End. After 300 ticks or when one player is left. Survivors rank by territory, then the eliminated rank by how long they lasted.
  • Timeouts. get_move() has 1 second of wall-clock time per tick. Go over once and your bot is frozen for the rest of that game: it keeps driving straight. Crashes in get_move() also count as "keep going straight" (the error shows in your console).

Rounds, points and the finals

Each team starts with 100 points. Roughly every 20 minutes the admin opens a round: a menu of games appears, each seating four bots. You pick one game per round, keep editing until the round locks, and then every game is played with the code you had saved at lock time. Empty seats are filled by house bots, which never earn points.

Points are the currency and the ranking: you spend them to enter higher-stakes tables and you earn them by placing well. When regular rounds end, points decide the seeds for the bracketed finals (seed 1 is the leader). Finals matches are four-player; the top two from each match advance until one bot remains.

GameEntryPayout
Scrimmage
Free to enter, nothing to win. Use it to test your bot against real teams instead of house bots.
free 1st +0 · 2nd +0 · 3rd +0 · 4th +0
Casual
Costs nothing. Everyone walks away with something; the winner gets the most.
free 1st +12 · 2nd +6 · 3rd +3 · 4th +1
Ranked
Entry fee of 10 points. First place earns 40, second 15, third 5. Fourth loses the fee.
10 1st +40 · 2nd +15 · 3rd +5 · 4th +0
High Stakes
Entry fee of 30 points. First place earns 120, second 40, everyone else loses their fee.
30 1st +120 · 2nd +40 · 3rd +0 · 4th +0
Double or Nothing
Entry fee of 25 points. The winner gets 50 back; nobody else gets anything.
25 returns 2× / 0× / 0× / 0× of your stake
Jeopardy
You choose the wager (at least 5 points, up to everything you have). First place triples it, second place keeps it, third and fourth lose it.
wager ≥ 5 returns 3× / 1× / 0× / 0× of your stake
Winner Takes All
Entry fee of 15 points from every team. The top finishing team takes 100% of the pot. House bots never win the pot.
15 pot split 100% / 0% / 0% / 0% (teams only)

Entry fees are deducted when you join and refunded if you leave before the round locks. "Returns ×2" means the winner receives twice the stake back (net +1×).

Writing your bot

Define a class Bot with a get_move(self, state) method that returns "UP", "DOWN", "LEFT" or "RIGHT". __init__ runs once per game, so store memory on self. print() goes to your console. Plain Python 3 only (no pip packages); random, math, collections etc. are fine.

class Bot:
    name = "Lawnmower"

    def __init__(self):
        self.turns = 0

    def get_move(self, state):
        me = state.me
        for direction in state.legal_moves():
            r, c = state.next_cell(me.row, me.col, direction)
            if state.is_safe(r, c):
                return direction
        return "UP"

GameState reference

MemberMeaning
state.meYour Player
state.opponentsLiving opponents (Player list)
state.playersAll four players, including dead ones
state.rows, state.colsBoard size (40 × 40)
state.tick, state.max_ticks, state.ticks_leftClock
state.grid[r][c]Owner id of a cell, 0 if unclaimed
state.trails[r][c]Id of the player whose trail is on the cell, 0 if none
state.owner(r, c)Same as grid, but returns None off the board
state.trail_owner(r, c)Same as trails, None off the board
state.is_mine(r, c)True if you own the cell
state.in_my_territory()True if your head is on your territory
state.is_safe(r, c)On the board and no trail on it
state.in_bounds(r, c)Cell is on the board
state.legal_moves()Directions that stay on the board and are not a reversal
state.next_cell(r, c, direction)The cell one step away
state.neighbors(r, c)[(direction, r, c), ...] for in-bounds neighbours
state.distance(r1, c1, r2, c2)Manhattan distance
state.cells_owned_by(id) / state.my_cells()List of (r, c)
state.nearest_my_cell(r=None, c=None)Closest cell you own (to your head by default)
state.direction_toward(r, c)A legal direction that gets you closer

Player

id, nameId 1–4 (matches grid values) and display name
row, col, positionHead position; position is (row, col)
directionLast direction moved, None before the first tick
alive, death_tickStatus
territoryCells owned
trail, trail_lengthCurrent trail cells, oldest first
killsOpponents this player has cut

Globals available without importing: DIRECTIONS, DELTAS (direction → (dr, dc)), OPPOSITE. Coordinates are (row, col); row 0 is the top, col 0 is the left. UP is row − 1.