Skip to main content

Arena Hero Python

The official typed Python SDK for Arena Hero.

You own the game loop. The SDK connects to the HTTP and WebSocket APIs, parses authoritative state, exposes control methods for every Unit type and Core, and submits one complete plan when you call submit().

Documentation

  • Quickstart: installation, synchronous and asynchronous loops, state access, Unit control, and local development.
  • API reference: every client option, Turn field, control method, model, event, enum, and exception.
  • Game rules and wire API: authoritative gameplay, HTTP, and WebSocket behavior.

Install

Python 3.11 or newer is required.

Install the published release:

pip install arena-hero

Synchronous game loop

from getpass import getpass

from arena_hero import ArenaHeroClient, Direction


api_key = getpass("Arena Hero API key: ")

with ArenaHeroClient(api_key=api_key) as game:
    for turn in game.turns():
        for worker in turn.workers:
            if worker.position in turn.resource_cells:
                worker.harvest()
            else:
                worker.move(Direction.RIGHT)

        turn.submit()

worker.harvest() and worker.move() only queue actions on the current Turn. They do not make network requests. turn.submit() sends the complete queued plan in one HTTP request.

Asynchronous game loop

import asyncio
from getpass import getpass

from arena_hero import AsyncArenaHeroClient, Direction


async def play(api_key: str) -> None:
    async with AsyncArenaHeroClient(api_key=api_key) as game:
        async for turn in game.turns():
            for vanguard in turn.vanguards:
                vanguard.sweep(Direction.LEFT)

            await turn.submit()


asyncio.run(play(getpass("Arena Hero API key: ")))

The synchronous and asynchronous clients use the same models and control methods:

  • ArenaHeroClient with for and turn.submit()
  • AsyncArenaHeroClient with async for and await turn.submit()

Control interfaces

Every controlled object exposes its authoritative state through .view. Calling another method for the same object replaces its earlier queued action on that Turn.

Object Available methods
Worker move, harvest, deposit, pickup_beacon, drop_beacon, self_destruct, wait, clear_action
Vanguard move, sweep, pickup_beacon, drop_beacon, self_destruct, wait, clear_action
Ranger move, shoot, pickup_beacon, drop_beacon, self_destruct, wait, clear_action
Core spawn, repair_shield, start_move, cancel_move, pickup_beacon, drop_beacon, wait, clear_action

Useful Turn data:

turn.tick
turn.state
turn.resources
turn.core
turn.units
turn.workers
turn.vanguards
turn.rangers
turn.visible_enemies
turn.resource_cells
turn.obstacle_cells
turn.beacon
turn.events
turn.plan

turn.resource_cells includes visible natural points and Worker cargo piles. Pile amounts are not exposed; a partially recovered pile remains in the set. Use event.resource_amount and event.harvest_source on turn.events to read private cargo-drop and recovery results without unpacking values yourself.

Worker

worker = turn.workers[0]

worker.move(Direction.UP)
worker.harvest()
worker.deposit()
worker.pickup_beacon()
worker.drop_beacon()
worker.self_destruct()
worker.wait()
worker.clear_action()

If a Worker dies, including through self_destruct(), its complete cargo amount becomes a recoverable resource pile on its final cell.

Vanguard

vanguard = turn.vanguards[0]

vanguard.move(Direction.DOWN)
vanguard.sweep(Direction.RIGHT)
vanguard.pickup_beacon()
vanguard.drop_beacon()
vanguard.self_destruct()
vanguard.wait()

Ranger

Pass a visible Unit or Core to derive both the target UUID and expected cell:

ranger = turn.rangers[0]
enemy = turn.visible_enemies[0]

ranger.shoot(enemy)

When you only have a UUID, provide the expected cell:

from uuid import UUID

target_id = UUID("8d60b600-78d4-4aba-83fd-4e5e27b88c9d")
ranger.shoot(target_id, expected_cell=(120, 85))

Core

The Core may be absent while the player is respawning.

if turn.core is not None:
    turn.core.spawn(UnitType.WORKER)
    turn.core.repair_shield()
    turn.core.start_move(Direction.RIGHT)
    turn.core.cancel_move()
    turn.core.pickup_beacon()
    turn.core.drop_beacon()
    turn.core.wait()

Complete event stream

game.turns() yields each actionable Tick once. Use game.events() when the program also needs tick notices and canonical plans submitted by this or another client:

from arena_hero import Received, Tick, Turn


with ArenaHeroClient(api_key=api_key) as game:
    for event in game.events():
        if isinstance(event, Tick):
            current_tick = event.tick
        elif isinstance(event, Turn):
            # Queue actions, then submit one complete plan.
            event.submit()
        elif isinstance(event, Received):
            latest_received = event

The latest current-Tick AGENT and MANUAL plans are also available through game.latest_receipts. A new Received value replaces the earlier value for that source.

Use either events() or turns() on a client, not both at the same time.

Direct plan submission

Advanced callers can build and submit the exact public protocol model:

from uuid import UUID

from arena_hero import CommandPlan, Direction, MoveAction


plan = CommandPlan(
    tick=turn.tick,
    unit_actions={
        UUID("9d3e4941-2816-4a39-a220-df8cd95e877d"): MoveAction(direction=Direction.UP)
    },
)

receipt = game.submit(plan)

For asynchronous code, use await game.submit(plan).

Connection behavior

The SDK:

  • sends the API key only in the Authorization header;
  • never reads credentials or endpoints from environment variables;
  • disables WebSocket message compression to match the server contract;
  • uses protocol Ping/Pong automatically;
  • reconnects with jittered exponential backoff from 250 ms to 5 seconds;
  • retries an uncertain HTTP submission with the same idempotency key and exact request bytes;
  • stops on WebSocket close code 1008;
  • treats each state as a complete replacement;
  • preserves unknown resolution event names and reason codes as strings.

The default backend is https://api.arenahero.io. Pass test endpoints explicitly:

client = ArenaHeroClient(
    api_key=api_key,
    base_url="http://localhost:8080",
    websocket_url="ws://localhost:8080/api/v1/game/ws",
)

Errors

All SDK exceptions inherit from ArenaHeroError.

Exception Meaning
ConfigurationError Invalid constructor option or idempotency key
AuthenticationError WebSocket authentication was rejected
PolicyViolationError WebSocket closed with code 1008
ProtocolError The server returned an invalid public-protocol message
APIError The command API returned a structured rejection
TransportError A network operation failed after safe retries
TurnClosedError Code tried to change a Turn after a newer Tick arrived
InvalidActionError A local action target or owned Unit was invalid

Dynamic gameplay failures are not exceptions. They arrive in the next Turn.events as normal resolution results.

Development

This project uses uv, a src/ layout, and a locked development environment.

uv sync --locked --all-groups
uv run ruff format --check .
uv run ruff check .
uv run ty check
uv run bandit -r src -c pyproject.toml
uv run pytest
uv build

The public game protocol is documented at doc.arenahero.io.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

arena_hero-0.2.0.tar.gz (77.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

arena_hero-0.2.0-py3-none-any.whl (22.7 kB view details)

Uploaded Python 3

File details

Details for the file arena_hero-0.2.0.tar.gz.

File metadata

  • Download URL: arena_hero-0.2.0.tar.gz
  • Upload date:
  • Size: 77.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for arena_hero-0.2.0.tar.gz
Algorithm Hash digest
SHA256 e2d10a4f70bab075931b8a2b2d6d33f04bf8a7bd341585e887085e7fc28af48f
MD5 2bfd8c38939c90ea43fe249bcdf0b96d
BLAKE2b-256 440ccd97875ca1cd33d07ec20be65680157a4bc8b8d51c4f8da25d4075a9ccf2

See more details on using hashes here.

Provenance

The following attestation bundles were made for arena_hero-0.2.0.tar.gz:

Publisher: publish.yml on arena-hero/arena-hero-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file arena_hero-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: arena_hero-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 22.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for arena_hero-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 327e0abe357f685f1fbe5fcca38644f6a2c1be82d4f30ca73963ff3294d608cf
MD5 0df71500c99a7ecc50a8dbae9c341222
BLAKE2b-256 f72a1d6a2233b1cf4c7b0df043664efeffaa1cb255b508b731291f0fa01d7fc6

See more details on using hashes here.

Provenance

The following attestation bundles were made for arena_hero-0.2.0-py3-none-any.whl:

Publisher: publish.yml on arena-hero/arena-hero-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page