Python API reference

The ibf package, version 0.9.0: every public name, from its docstring. The guide (docs/guide/writing-bots.md) explains how they fit together.

Inversus Bot Framework: the Python SDK (design section 10).

A bot is a class with on_tick(state) -> Action; run connects it to the running game (or ibf_fakehost.exe), binds its pad and plays it:

from ibf import Action, Bot, Dir, run

class Walker(Bot):
    name = "walker"

    def on_tick(self, state):
        return Action(move=Dir.RIGHT)

if __name__ == "__main__":
    run(Walker, pad=0)

Lower down, ibf.connect gives a Client: each tick's state, the host's log, pads and console commands. The ibf command runs bots (ibf run), plays matches between them (ibf match), and reads the game (ibf state, ibf logs, ibf cmd). A bot draws over the board (self.draw, ibf.Draw), declares parameters the overlay shows as controls (ibf.Param) and publishes stats (self.stats). The package loads ibf_client.dll (see ibf._native for where it looks).

AbiMismatchError (exception)

The host speaks another bridge ABI; the message names both versions.

Action(move: Dir | Vec2 | None = None, shoot: Dir | None = None, pause: bool = False, pad: Pad | None = None) -> None (class)

One tick's input: where to move, where to shoot (held), and whether to press pause.

  • idle() -> Action: Nothing pressed.
  • raw(pad: Pad) -> Action: This exact pad state.
  • to_pad(self, controls: Controls) -> Pad: The pad state this action is under the host's controls.

Board(raw: _schema.Board, wrap: bool = False, wrap_shift: tuple[int, int] = (0, 0)) -> None (class)

The board's tiles. Tiles are (x, y) pairs, row 0 at the bottom.

On a wrapping map (wrap) a ship or a shot crossing an edge comes back on the other side, moved along that side by wrap_shift; step, neighbors, raycast, clear_lane, next_step and delta follow it.

  • tile(self, x: int, y: int) -> Tile: The tile at (x, y); NONE off the board.
  • to_tile(self, pos: Vec2) -> tuple[int, int]: The tile a world position lies in (the origin is the board's centre).
  • center(self, tile: tuple[int, int]) -> Vec2: The world position of a tile's centre.
  • tile_at(self, pos: Vec2) -> Tile: The tile under a world position.
  • walkable(self, tile: tuple[int, int], for_color: Color) -> bool: Whether a ship of for_color may stand on the tile: ships walk on the other colour.
  • step(self, tile: tuple[int, int], direction: Dir) -> tuple[int, int]: The tile one step from tile in a direction. Across an edge of a wrapping board it is the tile on the other side; otherwise it may be off the board.
  • neighbors(self, tile: tuple[int, int], for_color: Color) -> list[tuple[int, int]]: The 4-connected neighbours a ship of for_color may step onto.
  • raycast(self, tile: tuple[int, int], direction: Dir) -> list[tuple[int, int]]: The tiles from tile (not included) in a direction, up to a hole or the edge of the board. On a wrapping board the ray crosses the edges, for at most SHOT_RANGE tiles and never past a tile it already met.
  • clear_lane(self, a: tuple[int, int], b: tuple[int, int], for_color: Color | None = None) -> Dir | None: The direction of a straight line from tile a to tile b with no tile between them off the board (or, given for_color, unwalkable for that colour); else None. A shot from a along it reaches b. On a wrapping board the line may cross an edge; the shortest wins.
  • delta(self, a: Vec2, b: Vec2) -> Vec2: The shortest vector from a to b: across the edges on a wrapping board (as the game measures it), else b - a.
  • next_step(self, a: tuple[int, int], b: tuple[int, int], for_color: Color) -> Dir: The first step (up, down, left or right) of a shortest walk from tile a to tile b over tiles a ship of for_color may stand on; Dir.NONE when b cannot be reached or a is b.
  • array (property): The tiles as a NumPy uint8[height, width] (row 0 at the bottom); None without NumPy.
  • rows(self) -> list[str]: The board as text rows, bottom row first: '0' white, '1' black, '-' none.

Bot() -> None (class)

A bot: override on_tick and whichever callbacks it needs.

While run plays it, these are set: client (the connection), log (a logging.Logger to the terminal and the game's console), input (the input sequencer), game (commands and the Navigator), controls, pad (the pad it plays, or None), state (the newest snapshot), draw (this tick's drawing) and stats (numbers and texts for the Bots panel).

  • on_connect(self, info: HostInfo) -> None: Connected (and bound to the pad).
  • on_match_start(self, state: State) -> None: A match started.
  • on_round_start(self, state: State, number: int) -> None: A round started; number counts from 1.
  • on_event(self, event: Event) -> None: Each new event, in order, before that tick's on_tick.
  • on_tick(self, state: State) -> Action | None: The action for this tick; None keeps the last one.
  • on_round_end(self, state: State, winner: int | None) -> None: A round ended; winner is the player index, or None when unknown.
  • on_match_end(self, state: State, winner: int | None) -> None: A match ended; winner is the player index, or None (a tie, or a quit).
  • on_param_changed(self, name: str, value: object) -> None: A parameter was changed in the overlay or by param.set; the attribute already reads the new value.
  • on_disconnect(self, reason: IbfError) -> None: The host went away, or dropped this bot.
  • submit(self, action: Action) -> None: The action for the next tick, from any thread (it wins over on_tick's).

BridgeHandler(client: Client, level: int = 0) -> None (class)

Writes each record to the client's log queue; the host shows it as bot:<name>.

  • emit(self, record: logging.LogRecord) -> None

Buttons (enum)

XInput's button bits.

Members: UP, DOWN, LEFT, RIGHT, START, BACK, LS, RS, LB, RB, A, B, X, Y.

Client(name: str = python, *, instance: int | None = None, tool: bool = False, ticks: bool = True, spin_us: int | None = None, connect_timeout: float = 0.0, player: int | None = None, url: str | None = None, token: str | None = None) -> None (class)

A connection to a host: its snapshots, its log, and a slot in its client table.

Args: name: shown in-game and in the host's log (bot:<name>); up to 31 bytes of UTF-8. instance: the host's process ID; None connects to the newest running host. tool: a tool (the CLI) rather than a bot; the status bar counts only bots. ticks: wake this client after each publish. Off for clients that only read logs. spin_us: the window around each expected tick that the client spins through instead of sleeping: the lowest latency, and no wake-up for the game thread to pay, for about 6% of a core at 1000 us. None: a bot spins 1000 us, a tool sleeps. 0 sleeps. connect_timeout: seconds to wait for a host to appear. player: the player index this client plays, for State.me. url: shm:// for a host on this machine, tcp://host:port for one over TCP (design 6.6). None: IBF_URL, then shm://. token: the TCP server's token, or one pinned to a pad. None: IBF_TOKEN.

  • close(self) -> None: Says goodbye; the host frees the slot at once. Safe to call twice, and while another thread logs: it waits for that line.
  • closed (property)
  • heartbeat(self) -> None: Tells the host this client is alive; waiting does it too. Raises when the connection ended.
  • wait(self, timeout: float | None = 0.1) -> State | None: The next snapshot newer than the last one, or None after timeout seconds.

The newest wins: snapshots published while the caller was busy are skipped (State.skipped counts them). None waits as long as it takes. Raises HostGoneError or DisconnectedError when the connection ends.

  • latest(self) -> State | None: The newest snapshot now, without waiting; None before the first, and when every try overlapped the host writing one (it was preempted mid-write): try again.
  • ticks(self, timeout: float = 0.1) -> Iterator[State]: Every new snapshot, until the host goes away or drops this client.

disconnect_reason says which. timeout is how long each wait may take; the iterator keeps waiting through timeouts.

  • log(self, level: int | str, message: str) -> bool: Logs into the host's log as bot:<name>; False when the queue was full (dropped). Any thread may log, close() included meanwhile: a closed client logs nothing (False).
  • logs(self, *, from_oldest: bool = True) -> Iterator[LogRecord]: The host's log records not read yet; the first call starts at the oldest still held (or at the next one written). Returns when caught up: call again to follow. Raises RefusedError for a bot whose TCP token is pinned to a pad (design 6.6).
  • logs_lost (property): Log records overwritten before this client read them.
  • bind_pad(self, pad: int, *, force: bool = False) -> None: Takes bot pad pad (0-3); with force, from the client that has it. A client owns one pad: binding another gives the first back. Sets player to the pad, since bot pads are players 1-4 in pad order. Raises NotBotPadError or PadTakenError.
  • release_pad(self) -> None: Gives the pad back: it reads neutral until another client binds it. Raises RefusedError for a bot whose TCP token is pinned to its pad (design 6.6).
  • pad (property): The pad this client owns now (the console's pad.bind can change it), or None.
  • set_pad(self, pad: Pad, *, for_tick: int | None = None) -> None: The pad's state from now on. Write it every tick: a state unchanged for the host's input.timeout_ms (250 ms) reads neutral. for_tick is the tick of the snapshot this answers (default: the last one waited for). Raises NotBoundError.
  • pads(self) -> list[PadSlot]: The four pads: which are bot pads, and who owns each.
  • run_mode (property): "realtime" or "lockstep" (design 4.5); None from a host before ABI 0.7.
  • sim_paused (property): The host's simulation is paused (sim.pause).
  • controls (property): The pad input each game action maps to (the host's [controls], design 7.7). Raises
  • command(self, line: str, timeout: float = 30.0) -> str: Runs one console command on the host and returns its result. Raises CommandError when it fails (the result says why), CommandRefusedError, or IbfError on a timeout.
  • draw_submit(self, cmds: bytearray, count: int, payload: bytearray, payload_len: int, ttl_ms: int = 0, dropped: int = 0) -> None: Publishes a draw list: count packed draw_cmd structs and their payload (ibf.Draw builds them). Raises IbfError when the host has no draw lists (before ABI 0.5).
  • define_params(self, defs: Sequence[_schema.ParamDef]) -> None: Declares this client's parameters (ibf.Param builds them).
  • param_values(self, since: int) -> tuple[int, list[tuple[float, int]]] | None: The parameters' values (value, changes), in the order declared, when the host changed any since change count since, with the new count; else None.
  • publish_stats(self, entries: ctypes.Array[_schema.StatEntry]) -> None: Publishes the stats (ibf.Stats builds them).
  • info (property): The host: its process, version, ABI and this client's row. Raises once the host
  • clients(self) -> list[ClientInfo]: Every connected client, this one included.

ClientInfo(index: int, pid: int, name: str, tool: bool, pad: int | None, connected_ns: int, heartbeat_ns: int) -> None (class)

One connected client, as the host's client table shows it.

Color (enum)

Ship or tile colour

Members: WHITE, BLACK, UNKNOWN.

CommandError (exception)

The command ran and failed, or there is no such command; text is the host's answer.

CommandRefusedError (exception)

The host refused the request: it takes no commands, or the line was malformed.

connect(name: str = python, *, instance: int | None = None, tool: bool = False, ticks: bool = True, spin_us: int | None = None, connect_timeout: float = 0.0, player: int | None = None, url: str | None = None, token: str | None = None) -> Client

Connects to a host; the arguments are :class:Client's.

Controls(move: Stick = <Stick.LEFT_STICK: 0>, min_press_ticks: int = 2, shoot_up: Buttons = <Buttons.Y: 32768>, shoot_down: Buttons = <Buttons.A: 4096>, shoot_left: Buttons = <Buttons.X: 16384>, shoot_right: Buttons = <Buttons.B: 8192>, pause: Buttons = <Buttons.START: 16>, confirm: Buttons = <Buttons.A: 4096>, back: Buttons = <Buttons.B: 8192>) -> None (class)

The pad input for each game action: the host's [controls] (design 7.7).

  • from_raw(raw: _schema.Controls) -> Controls
  • stick(self, x: float, y: float) -> Pad: Movement toward (x, y), each -1.0..1.0, as this mapping moves: a stick, or the D-pad's nearest of eight directions.

Dir (enum)

A direction: the four shot directions, plus the diagonals for movement.

Members: NONE, UP, DOWN, LEFT, RIGHT, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT.

DisconnectedError (exception)

The host freed this client's slot (no heartbeat for over a second).

Draw(ttl_ms: int = 0) -> None (class)

One tick's drawing: world space here, screen (back-buffer pixels) and norm (0..1 across the back buffer) for the rest. Tiles count from the board's lower-left corner, like Board.tile.

  • clear(self) -> None
  • tile(self, x: int, y: int, color: ColorLike = (1, 1, 1, 0.3), *, fill: bool = True, w: int = 1, h: int = 1, width: float = 1.0, above: bool = False, fade: bool = False) -> None: Board tile (x, y), or a w x h block from it.
  • tiles(self, tiles: Iterable[tuple[int, int]], color: ColorLike = (1, 1, 1, 0.3), *, fill: bool = True, above: bool = False) -> None: Each of the tiles.
  • path(self, tiles: Iterable[tuple[int, int]], color: ColorLike = lime, width: float = 2.0, *, above: bool = False) -> None: A line through the centres of the tiles, in order (a path from next_step, say): at most 1,024.
  • heatmap(self, values: Any, palette: str | int = inferno, alpha: float = 0.35, *, origin: tuple[int, int] = (0, 0), lo: float | None = None, hi: float | None = None, zero_clear: bool = False, tint: ColorLike = white, above: bool = False) -> None: A grid of values drawn as translucent tiles (danger maps, path costs), from tile origin (its lower-left). values is rows of numbers, row 0 at the bottom, like Board.array: a list of lists or a 2-D NumPy array. They are scaled from lo..hi (default: 0 or the smallest, to the largest, of the finite values) to the palette's 0..255; a value that is not finite (inf, NaN) is drawn as the lowest.

A heatmap is at most 256 cells a side, and its cells fit one list's payload: about 16,000 in all (a 127 x 127 grid, or a board-sized one many times over).

  • publish(self, client: Client) -> None: Hands the list to the overlay; it replaces the last one. The list stays as it is.

EnemyKind (enum)

What an arcade enemy does (ABI 0.9)

Members: UNKNOWN, SMASH, SHOOT, SMASH_SHOOT.

Event(tick: int, kind: EventKind | int, a: int, b: int, player: int | None) -> None (class)

Something that happened, derived from consecutive ticks (design 5.5).

EventKind (enum)

Events derived by diffing consecutive ticks (design section 5.5)

Members: NONE, MENU_CHANGED, MATCH_STARTED, ROUND_STARTED, ROUND_ENDED, MATCH_ENDED, SHIP_SPAWNED, SHIP_DIED, SHOT_FIRED, PICKUP_SPAWNED, PICKUP_TAKEN, SCORE_CHANGED.

Game(client: Client) -> None (class)

Console commands on the host, and the Navigator's goals (design 9.3, 7.8).

  • command(self, name: str, *args: object, timeout: float = 30.0, **kwargs: object) -> str: Runs name args key=value ... on the host and returns its result. Each argument is one word: one with a space or a quote is quoted for the host.
  • status(self) -> str: What the Navigator is doing, or how its last goal ended.
  • wait_navigator(self, timeout: float = 90.0) -> str: Waits for the Navigator's goal to end; returns how, or raises NavigatorError.

While the game shows its how-to-play (a save's first versus match), which the Navigator plays through, the timeout waits: the host has a limit of its own for it.

  • wait_ready(self, timeout: float = 90.0) -> None: Waits until the game's state reads (its profile in effect, its menus known): a game just launched takes a few seconds, longer on a busy machine. Raises NavigatorError.
  • start_match(self, mode: str = versus, players: int = 1, map: int = 0, *, wait: bool = True, timeout: float = 90.0) -> str: Sets up a match through the menus (match.start); with wait, until it starts. It first waits for the game to be ready (:meth:wait_ready).
  • restart(self, *, wait: bool = True, timeout: float = 90.0) -> str: Plays the match again (match.restart).
  • quit(self, *, wait: bool = True, timeout: float = 60.0) -> str: Back to the main menu (match.quit).
  • pause(self) -> str: Pauses the game's simulation (sim.pause, experimental): no ticks come until :meth:step or :meth:resume; the game still draws.
  • resume(self) -> str: Lets the simulation run again (sim.resume).
  • step(self, n: int = 1) -> str: Lets n steps of the simulation run, each with its tick, then stays paused (sim.step); pauses first when running.
  • set_mode(self, mode: str, *, timeout_ms: int | None = None, miss: str | None = None) -> str: "lockstep": after each tick the game waits until every bot pad answers it, up to timeout_ms; "realtime": it never waits (mode). miss is what a bot that misses a tick plays: "neutral" until it catches up, or "hold", its last input for that tick only (with a timeout of a tick, the bots play in real time).
  • record(self, file: str | None = None, *, note: str | None = None) -> str: Starts recording every tick to an .ibfrec file on the host's machine (record.start; default ibf\recordings\rec-
  • stop_recording(self) -> str: Finishes the recording (record.stop); the result says where it went.
  • lock(self) -> str: Takes the match lock: other clients' commands that change the game, the pads or the run mode are refused until :meth:unlock or this client leaves (design 10.8).
  • unlock(self) -> str: Gives the match lock back.

GameType (enum)

Which game is running (ABI 0.6)

Members: NONE, VERSUS, ARCADE, UNKNOWN.

HostGoneError (exception)

The host exited or is exiting.

HostInfo(pid: int, version: str, abi: tuple[int, int], generation: int, client_index: int, state: str, published: int, clock_mono_ns: int, clock_unix_ns: int) -> None (class)

The host this client is connected to.

  • unix_ns(self, mono_ns: int) -> int: A host clock reading (a snapshot's or a log record's time) as Unix nanoseconds.

IbfError (exception)

A bridge call failed; status is the C library's ibf_status.

InputSequencer(controls: Controls | None = None) -> None (class)

Timed presses on top of each tick's action: a tap, a charged shot, a held button.

Presses run one after another, each followed by a release, so two taps of the same button are two presses to the game.

  • tap(self, what: Dir | Buttons, ticks: int | None = None) -> None: A short press: a direction taps its shoot button. Held for ticks (at least the host's min_press_ticks), then released.
  • charge(self, direction: Dir, ticks: int = 40) -> None: Holds the shot in a direction for ticks, then lets go: a charged shot.
  • hold(self, buttons: Buttons, ticks: int) -> None: Holds buttons for ticks.
  • clear(self) -> None
  • busy (property): A press is still running or waiting.
  • apply(self, pad: Pad) -> Pad: This tick's pad: pad with the running press's buttons added. Advances one tick.

Instance(pid: int, version: str, abi: tuple[int, int], process_time: int) -> None (class)

A running host from the directory.

instances() -> list[Instance]

The hosts running in this logon session, newest first.

InvalidReason (enum)

Why the latest tick produced no valid snapshot

Members: NONE, NO_PROFILE, ROOT_UNREADABLE, PATH_FAULT, LIST_INVALID, BOARD_OUT_OF_RANGE, COUNT_OUT_OF_RANGE, NON_FINITE_VALUE, READ_FAULT, TORN_READ, PROFILE_ERROR, STATE_DISABLED.

load_bot(path: str | Path, class_name: str | None = None) -> type[Bot]

The Bot subclass a file defines, whatever its __main__ block does. With several, class_name picks one.

LogRecord(seq: int, time_ns: int, tick: int, thread: int, level: str, source: str, text: str) -> None (class)

One record of the host's log.

Mark(tick: int, kind: EventKind, a: int) -> None (class)

A match or round boundary, from the snapshots' events.

Match(raw: _schema.Match) -> None (class)

The game being played and its map: the mode, the map, its wrapping, the rounds.

  • game_type (property): VERSUS or ARCADE; NONE at start-up. The menus after a game keep its type (with no map).
  • map_set (property): 0 tutorial, 1 arcade one player, 2 arcade two players, 3 versus 1v1, 4 versus 2v2; None in the menus.
  • map_index (property): The map's index in its set (the map select's item); None in the menus.
  • map_scale (property): The map's zoom (1.0 for most maps).
  • wrap (property): Whether ships and shots crossing an edge of the board come back on the other side.
  • wrap_shift (property): (columns, rows) a crossing moves by: crossing the right edge moves wrap_shift[1] rows
  • teams (property)
  • rounds_to_win (property): Versus: the round wins that take the match.
  • round_state (property): NONE before the round starts, PLAYING, then DECIDED once a team has won it.
  • round_winner (property): The team that won the round, once DECIDED; None before.
  • focus_hash (property): The FNV-1a hash of the focused menu button's name (ResumeButton, RetryButton,

A match.* goal failed, or did not finish in time; the message says where it stopped.

NoHostError (exception)

No host is running, or not the one asked for.

NoSlotError (exception)

All eight client slots are taken.

NotBotPadError (exception)

The slot is not a bot pad: the host's input.bot_pads lists the ones that are.

NotBoundError (exception)

This client owns no pad: it never bound one, released it, or another client took it.

now_ns() -> int

The host's clock: QueryPerformanceCounter in nanoseconds, as snapshots and log records use it.

Pad(lx: int = 0, ly: int = 0, rx: int = 0, ry: int = 0, lt: int = 0, rt: int = 0, buttons: Buttons = <Buttons.NONE: 0>) -> None (class)

One pad state: sticks as -32768..32767 (+Y up), triggers 0..255, buttons as bits.

Pad.of(lx=0.5, buttons=Buttons.A) takes fractions of full scale too.

  • of(lx: float = 0, ly: float = 0, rx: float = 0, ry: float = 0, lt: float = 0, rt: float = 0, buttons: int = 0) -> Pad: A pad from fractions (-1.0..1.0 sticks, 0.0..1.0 triggers) or raw numbers.
  • neutral() -> Pad
  • with_buttons(self, extra: int) -> Pad: This state with more buttons held.
  • to_raw(self, for_tick: int = 0) -> _schema.PadInput

PadSlot(pad: int, bot: bool, owner: int | None) -> None (class)

One of the four pads as the bridge sees it.

PadTakenError (exception)

Another client owns the pad (bind_pad(..., force=True) takes it).

Param(kind: Kind, default: float, minimum: float = 0.0, maximum: float = 0.0, step: float = 0.0, help: str = , options: Sequence[str] = ()) -> None (class)

One parameter of a Bot subclass: a class attribute made with Param.float(...) and friends. On the bot it reads as its current value.

  • bool(default: bool = False, *, help: str = ) -> Param: A checkbox.
  • int(default: int = 0, minimum: int = 0, maximum: int = 0, *, step: int = 0, help: str = ) -> Param: A whole number; a slider from minimum to maximum, unbounded when maximum is not above minimum.
  • float(default: float = 0.0, minimum: float = 0.0, maximum: float = 0.0, *, step: float = 0.0, help: str = ) -> Param: A number; a slider from minimum to maximum, unbounded when maximum is not above minimum.
  • enum(options: Sequence[str], default: str | int = 0, *, help: str = ) -> Param: One of the options (a drop-down); reads as the chosen option's name.
  • button(*, help: str = ) -> Param: A button; reads as how many times it was pressed (watch on_param_changed).
  • color(default: ColorLike = white, *, help: str = ) -> Param: A colour picker; reads as packed RGBA (any drawing call takes it).
  • convert(self, value: float) -> Any: The host's number as this parameter's Python value.
  • to_def(self) -> _schema.ParamDef: The definition the host is sent.

Phase (enum)

Game phase, mapped from the menu ID by the profile

Members: UNKNOWN, BOOT, MENU, LOBBY, GET_READY, GO, PLAYING, DEAD, ROUND_OVER, GAME_OVER.

Pickup(raw: _schema.Pickup) -> None (class)

A power-up on the board.

  • pos (property)
  • kind (property)
  • amount (property)
  • age (property): Seconds since it appeared.

PickupKind (enum)

Members: FAST_SHOTS, BLOCK_EAT, UNKNOWN, SHIELD.

PickupSpawn(raw: _schema.PickupSpawn) -> None (class)

A pickup spawn point of the map, and when its next pickup appears.

  • pos (property): The spawn cell's centre.
  • state (property): DUE (spawns on the next step), OUT (its pickup is on the board) or WAITING.
  • time_left (property): Seconds until it spawns while WAITING; 0 otherwise.

Projectile(raw: _schema.Projectile, ext: _schema.ProjectileExt | None = None) -> None (class)

A shot in flight.

  • pos (property)
  • dir (property)
  • speed (property)
  • kind (property)
  • kind_raw (property): The game's own type value.
  • owner (property): The player who fired it, or None when unknown (an arcade enemy's shot).
  • team (property): The shooter's team: ships of the other team die in its tile. None from a host before ABI 0.6.
  • distance (property): Tiles flown since it was fired (it dies at SHOT_RANGE); None from a host before ABI 0.6.
  • age (property): Seconds since it was fired.

ProjectileKind (enum)

Members: NORMAL, FAST, UNKNOWN.

RecordedPad(for_tick: int, lx: int, ly: int, rx: int, ry: int, buttons: Buttons, lt: int, rt: int, source: str, owner: int | None) -> None (class)

One pad as the game last polled it before a tick.

  • to_pad(self) -> Pad

RecordedTick(tick: int, frame: int, host_time_ns: int, pads: list[RecordedPad], data: bytes) -> None (class)

RecordedTick(tick: 'int', frame: 'int', host_time_ns: 'int', pads: 'list[RecordedPad]', data: 'bytes')

  • state (property): The snapshot as a :class:State.

Recording(path: str | Path) -> None (class)

An .ibfrec file: its header, its chunk index and marks, and its ticks.

  • first_tick (property)
  • last_tick (property)
  • ticks(self, from_tick: int = 0, to_tick: int | None = None) -> Iterator[RecordedTick]: Every recorded tick with from_tick <= tick <= to_tick, in order.
  • answers(self, pad: int) -> dict[int, RecordedPad]: The recorded answer to each tick on pad: a bot's record names the tick it answered; a real controller's answer to a tick is the next tick's poll. The host's own presses (pad.*, the Navigator) answer nothing.

RecordingError (exception)

The file is no recording, or a damaged one.

RefusedError (exception)

The host's TCP server refused the connection or the request: a wrong or spent token, or something a bot with a pinned token may not do (design 6.6).

rgba(color: ColorLike, alpha: float | None = None) -> int

A colour as the host's packed RGBA (red in the low byte); alpha (0..1) overrides its own.

RoundState (enum)

Where the round is (ABI 0.6)

Members: NONE, PLAYING, DECIDED, UNKNOWN.

run(bot: type[Bot] | Bot, pad: int | None = 0, *, name: str | None = None, instance: int | None = None, force: bool = False, spin_us: int | None = None, connect_timeout: float = 10.0, log_level: str = info, stop: threading.Event | None = None, max_ticks: int | None = None, url: str | None = None, token: str | None = None) -> int

Plays a bot until the host goes away (or stop is set, or max_ticks passed).

pad is the bot pad to play (None: watch only). The environment overrides the arguments, so ibf run and ibf match can place a bot whatever its own run(...) call says: IBF_PAD (a number, or none), IBF_INSTANCE, IBF_BOT_NAME, IBF_FORCE_PAD=1, IBF_WAIT (the seconds to keep trying to connect); and for a host over TCP (design 6.6), IBF_URL and IBF_TOKEN. A token pinned to a pad brings its pad: the bot plays that one. Returns 0 when the host went away or the bot was stopped, 1 when it could not connect (or the connection ended before it played), 2 when it could not take its pad or the environment is wrong.

Ship(raw: _schema.Ship, index: int, board: Board, ext: _schema.ShipExt | None = None) -> None (class)

One ship: its position and motion, ammunition, score and flags.

  • player_id (property): The player index 0-3, or None for a ship no player owns (an arcade enemy).
  • color (property): The ship's colour (it walks on tiles of the other colour).
  • alive (property)
  • pos (property)
  • vel (property): The direction of movement: a unit vector while moving, zero when still.
  • velocity (property): The speed and direction in tiles per second (at most 10, or 15 under a speed boost);
  • team (property): The team, 0 or 1: team 0 is black and walks on white. None from a host before ABI 0.6.
  • tile (property): The tile under the ship's centre.
  • last_shot_pos (property)
  • ammo (property)
  • max_ammo (property)
  • fast_ammo (property): Red (fast) shots held.
  • reload_progress (property)
  • reload_time (property)
  • time_alive (property)
  • time_since_shot (property): The charge timer: from 0 when a charge starts and when a shot fires, so while a
  • score (property)
  • multiplier (property)
  • charging (property)
  • charge_dir (property)
  • shield (property)
  • block_eat (property): Whether it breaks the cells it runs into (arcade enemies always do).
  • block_eat_time (property): Seconds of block-eat left (0 from a host before ABI 0.6).
  • speed_boost_time (property): Seconds of x1.5 speed left (0 from a host before ABI 0.6).
  • shield_time (property): Seconds of shield left: a hit breaks the shield instead (0 from a host before ABI 0.6).
  • parry_dir (property): The direction of a charge started in the last 0.3 s: a shot coming the other way is
  • dying (property): Hit, and exploding.
  • points (property): Points for killing it (arcade enemies).
  • moving (property)
  • known (property): The fields the profile confirms and this tick read (the snapshot's valid bits).

SpawnMarker(raw: _schema.SpawnMarker) -> None (class)

Arcade: a cell marked on the board where an enemy is about to appear.

  • pos (property): The marked cell's centre.
  • time_left (property): Seconds until the enemy appears (a marker shows for 1.5 s in Inversus 1.2.0).
  • kind (property): SMASH (breaks cells), SHOOT, SMASH_SHOOT, or UNKNOWN.

SpawnState (enum)

A pickup spawn point's state (ABI 0.6)

Members: DUE, OUT, WAITING.

Stat(value: Any, plot: bool = False) -> None (class)

A stat's value with options: Stat(ms, plot=True) keeps a sparkline in the panel.

State(buffer: bytearray, *, tick_info: Any = None, player: int | None = None, events_from: int | None = None) -> None (class)

One tick of game state, read in place from its own copy of the snapshot.

  • tick (property): Simulation steps since the game started.
  • frame (property)
  • time (property): The round's time in seconds.
  • host_time_ns (property)
  • valid (property): Whether this tick's read succeeded; when not, invalid_reason says why.
  • stale (property): The body is an earlier tick's (this one failed): only the header is new.
  • in_match (property)
  • lockstep (property): The host runs in lockstep: it waits for every bot pad's answer to this tick (design 4.5).
  • sim_paused (property): The simulation is paused (sim.pause): no ticks come until it steps or resumes.
  • invalid_reason (property)
  • tick_source (property)
  • phase (property)
  • menu (property): The game's menu ID.
  • menu_item (property): The highlighted menu item; the top one is 0.
  • paused (property)
  • arcade_score (property): The arcade score (what the screen counts up to); 0 in versus.
  • lives (property): Arcade lives left, the ship in play included.
  • board (property): The board, wrapping when the map does.
  • match (property): The game being played and its map; None from a host before ABI 0.6.
  • pickup_spawns (property): The map's pickup spawn points and their timers (none from a host before ABI 0.6).
  • spawn_markers (property): Arcade: the cells where enemies are about to appear (none from a host before ABI 0.9).
  • ships (property)
  • players (property): Each player's ship, by player index.
  • me (property): This client's ship, when it plays (its player is set); otherwise None.
  • enemies (property): The other living ships: of the other colour when this client's colour is known.
  • nearest_enemy(self) -> Ship | None: The closest enemy to this client's ship, or None.
  • projectiles (property)
  • pickups (property)
  • all_events(self) -> list[Event]: Every event the snapshot's ring holds, oldest first.
  • events (property): The events new since the last State this client got (the first gets this tick's).
  • extras (property): The custom probes' values (design 5.6), by name; raw ones as 4-byte bytes each.
  • to_json(self) -> str: The snapshot as JSON: the same text as the host's state.dump.

Stats() -> None (class)

A bot's stats: names to numbers or short texts (or Stat). Published after each tick in which they changed; up to 64, names up to 23 bytes, texts up to 23.

  • entries(self) -> ctypes.Array[_schema.StatEntry]: The stats as the host's entries.

Stick (enum)

Where movement goes (the host's [controls] move).

Members: LEFT_STICK, RIGHT_STICK, DPAD.

TickSource (enum)

Which hook built the snapshot (design section 4.5)

Members: PRESENT, SIM_STEP.

Tile (enum)

Board tile state

Members: WHITE, BLACK, NONE.

Vec2(x: float, y: float) -> None (class)

An immutable 2-D vector in world units (tiles).

  • length (property): The vector's length.
  • distance(self, other: Vec2) -> float: The distance to another point.
  • dot(self, other: Vec2) -> float: The dot product.
  • normalized(self) -> Vec2: The same direction at length 1; the zero vector stays zero.
  • angle (property): The direction in radians, counter-clockwise from +X.
  • to_tile(self, board: Board) -> tuple[int, int]: The board tile this point lies in, as (x, y) (it may be off the board).