C and C++ bots
Any language that can call a C function can play: the Python package itself is a wrapper
over ibf_client.dll. C++ has ibf.hpp, a header-only C++20 wrapper with exceptions and
spans. A bot is a program of its own, 64-bit or 32-bit.
The SDK
It comes two ways: inside the Python package (ibf new --lang cpp points at it), and as
ibf-sdk-<version>.zip. Either holds:
include\ |
ibf_client.h (the C API), ibf.hpp (C++20), ibf_plugin.h (plugins), ibf_schema.h (the state's structs) |
lib\x86, lib\x64 |
ibf_client.dll and its import library |
lib/linux-x86_64 |
libibf_client.so: the client for Linux, which speaks TCP only (below) |
cmake\ |
a CMake package: find_package(ibf CONFIG) gives ibf::client |
examples\ (zip) |
cpp_bot and the hello plugin |
You need Visual Studio 2022 or its Build Tools (the C++ workload), and CMake, which comes with Visual Studio.
A bot from the template
ibf new mybot --lang cpp
cmake -S mybot -B mybot\build -DCMAKE_PREFIX_PATH="<the SDK folder ibf new printed>"
cmake --build mybot\build --config Release
ibf match mybot\build\Release\mybot.exe idleibf run and ibf match start a .exe like a .py, and tell it its pad and the game
through the environment (IBF_PAD, IBF_INSTANCE, IBF_BOT_NAME, IBF_FORCE_PAD; over
TCP IBF_URL, IBF_TOKEN and IBF_WAIT), which ibf::RunEnv::read reads. The build copies
ibf_client.dll next to the program.
Over TCP, and on Linux
ibf_connect("tcp://host:47800", ...) reaches a game that listens on TCP ([bridge.tcp] in
the configuration), on this machine or another; the handle works like
one over shared memory, every call the same. ibf_connect_opts.token carries the game's
token, or one pinned to a pad (then the pad is bound already: ibf_pad says which), and
connect_timeout_ms keeps trying a game that is not listening yet, or a token not pinned
yet. Over TCP a client blocks rather than spins, and each call that reads the game's state
(ibf_pad, ibf_get_run_mode, ...) asks the game first, a round trip, so that it is as up
to date as over shared memory.
On Linux the client speaks TCP only: libibf_client.so in the SDK and the Linux wheel, or
built from a checkout with cmake --preset linux-client && cmake --build --preset linux-client (build/linux-x64/bin, with the C++ example bot). A Linux bot is built the
same way with the SDK's CMake package, and finds libibf_client.so next to itself (the
template sets the run path to $ORIGIN). That is how a native bot plays in the arena's bot
containers (the arena): the program named bot, its client beside it.
ibf.hpp
#include "ibf.hpp"
int main() {
const ibf::RunEnv env = ibf::RunEnv::read(0); // what ibf run, ibf match and the arena set
ibf::Client client = ibf::Client::connect({.name = env.name_or("walker"),
.instance = env.instance,
.connect_timeout_ms = env.wait_ms > 0 ? env.wait_ms : 10000,
.url = env.url,
.token = env.token});
if (env.pad >= 0 && client.pad() < 0) client.bind_pad(env.pad, env.force); // IBF_PAD=none: watch only
const int pad = client.pad(); // a pinned token's pad comes bound
const ibf_controls controls = client.controls();
for (;;) {
const std::optional<ibf::Snapshot> s = client.wait(std::chrono::milliseconds(100));
if (!s || pad < 0) continue; // no tick within 100 ms, or no pad
const ibf_ship* me = s->player(pad);
if (!s->in_match() || me == nullptr) {
client.set_pad(ibf::Pad::neutral()); // menus are not the bot's
continue;
}
client.set_pad(ibf::Pad::action(ibf::Dir::right, ibf::Dir::none, controls));
}
}Clientconnects (Client::connect), waits for ticks (wait,latest), holds a pad (bind_pad,set_pad), runs console commands (command, which throwsCommandErrorwith the game's answer), draws (draw()), and declares parameters and stats. It disconnects when destroyed.Snapshotreads the state in place:tick(),phase(),in_match(),ships(),projectiles(),pickups()(asstd::spans of the schema's structs),player(pad),enemies(me),board(),match(),events_since(seen). It is valid until the client's nextwait.Board:tile,to_tile,center,walkable.Pad:Pad::action(move, shoot, controls)maps game actions through the game's controls;Pad::stick,pressfor raw input.- Errors are
ibf::Errorexceptions carrying theibf_status. When the game closes,waitthrows withIBF_HOST_GONE.
examples\cpp_bot\main.cpp is a complete bot (a port of the Python hunter): it draws
its aim, publishes a stat and declares a parameter.
The C API
ibf_client* c = NULL;
ibf_connect_opts opts = {sizeof(opts), "my-bot", 0, IBF_CLIENT_CAPS_TICKS};
/* "tcp://host:47800" for a game over TCP; opts.token for its token */
if (ibf_connect("shm://", &opts, &c) != IBF_OK) { puts(ibf_connect_error()); return 1; }
ibf_bind_pad(c, 0, 0);
for (;;) {
const ibf_snapshot* s = NULL;
ibf_status st = ibf_wait_tick(c, 100, &s);
if (st == IBF_TIMEOUT) continue;
if (st != IBF_OK) break; /* the game closed */
ibf_pad_input in = {0};
in.lx = 32767; /* full right */
ibf_set_pad(c, &in);
}
ibf_disconnect(c);Every call returns an ibf_status; ibf_last_error(c) says why one failed. The
C API reference has every call, and schema.md every field of
ibf_snapshot. Other languages (Rust, C#, Zig, ...) can wrap the same DLL.