Scubywasm API
C ABI for WebAssembly agents and engine
Loading...
Searching...
No Matches
scubywasm_engine.h File Reference

More...

#include <stdint.h>
Include dependency graph for scubywasm_engine.h:

Go to the source code of this file.

Data Structures

struct  Config
 Engine configuration. More...
 
struct  Pose
 Pose on the unit torus. More...
 

Enumerations

enum  ActionFlags : unsigned int {
  ACTION_NONE = 0U , ACTION_THRUST = 1U , ACTION_TURN_LEFT = 2U , ACTION_TURN_RIGHT = 4U ,
  ACTION_FIRE = 8U
}
 Action bitmask. More...
 

Functions

struct Configget_config_buffer (void)
 Return a pointer to a module-owned Config singleton buffer.
 
void set_default_config (struct Config *cfg)
 Initialize a Config to engine defaults.
 
struct Context * create_context (const struct Config *cfg)
 Create a new engine context (start a new round).
 
void free_context (struct Context *ctx)
 Destroy a context created by create_context().
 
struct Poseget_pose_buffer (void)
 Return a pointer to a module-owned Pose singleton buffer.
 
uint32_t add_agent (struct Context *ctx, const struct Pose *pose)
 Add a new ship/agent to the round.
 
int32_t set_action (struct Context *ctx, uint32_t agent_id, enum ActionFlags flags)
 Process an agent action for the next tick.
 
uint32_t tick (struct Context *ctx, uint32_t n_times)
 Advance the simulation by one or more ticks.
 
void get_ship_pose (const struct Context *ctx, uint32_t agent_id, struct Pose *pose)
 Get the current pose of a ship.
 
int32_t get_shot_pose (const struct Context *ctx, uint32_t agent_id, struct Pose *pose)
 Get the current pose of the active shot of a ship.
 
int32_t is_alive (const struct Context *ctx, uint32_t agent_id)
 Check whether a ship is alive.
 
int32_t get_score (const struct Context *ctx, uint32_t agent_id)
 Get the current score of a ship.
 

Detailed Description

Scubywasm engine ABI.

This header defines the C ABI of the Scubywasm engine (the simulation core). It is designed to be:

  • callable from native code (hosted builds), and
  • exportable as a WebAssembly (WASM) module in freestanding builds.

Typical call pattern

The engine is driven in three phases:

1) Configuration

2) Round execution

3) Shutdown

Hosted vs. freestanding builds

The compile-time macro FREESTANDING controls whether the engine provides helper APIs intended for WASM / freestanding use.

  • If FREESTANDING is 1:
    • The engine avoids any dependencies, including glibc, libm, etc.
    • Allocation is intended to be static/fixed-size (no requirement for malloc/free in the module).
    • The engine exposes singleton argument buffers such as get_pose_buffer() and get_config_buffer().
  • If FREESTANDING is 0:
    • The engine is intended to be used from a hosted/native context.
    • Callers can naturally allocate Config and Pose in their own address space (stack/heap) and pass pointers directly.
    • Therefore the singleton buffer helpers are hidden.

Why singleton buffers exist (WASM use-case)

In WASM, the engine module owns its linear memory. External callers often cannot simply take the address of a host-allocated Pose/Config and pass it across the ABI boundary. The singleton buffer helpers solve this by letting the module provide stable storage:

This avoids requiring a general-purpose allocator inside the module and keeps argument passing simple and deterministic.

Warning
The singleton buffers are not re-entrant. If you call into the engine from multiple threads or overlap calls that reuse the same buffer, you must provide external synchronization.

Coordinate conventions

The implicit coordinate conventions are:

  • x and y live on a unit torus with values in [0, 1).
  • heading is in degrees in [0, 360) with:
    • 0 deg = up,
    • 90 deg = right,
    • 180 deg = down,
    • 270 deg = left.

Data Structure Documentation

◆ Config

struct Config

Engine configuration.

This structure defines the relevant game dynamics and constraints that callers (and agents, indirectly) should use for planning (movement, turning, shooting, collision avoidance, etc.).

The configuration is provided exactly once during initialization via create_context() and is never changed thereafter for a given Context.

Typical usage:

struct Config cfg;
cfg.ship_max_velocity = .02F;
struct Context *ctx = create_context(&cfg);
struct Context * create_context(const struct Config *cfg)
Create a new engine context (start a new round).
void set_default_config(struct Config *cfg)
Initialize a Config to engine defaults.
Engine configuration.
Definition scubywasm_engine.h:173

In freestanding/WASM builds, callers typically use get_config_buffer() instead of stack/heap allocation.

Data Fields
float ship_hit_radius Ship hit radius (in torus-units).

Ships are considered colliding/touching when their distance satisfies the engine's collision criterion derived from this radius.

float ship_max_turn_rate Ship turn rate per tick (in degrees per tick).

If ACTION_TURN_LEFT or ACTION_TURN_RIGHT is set, the ship's heading is changed by this value (left/right) for this tick.

float ship_max_velocity Ship speed when thrust is enabled (in torus-units per tick).

Ship speed is binary: either zero or this configured maximum. If ACTION_THRUST is set, the ship's velocity is set to this value. If ACTION_THRUST is not set, the ship's velocity is zero.

int32_t shot_lifetime Shot lifetime / end-of-life (in ticks).

A shot is removed when its lifetime reaches zero.

float shot_velocity Shot velocity (in torus-units per tick).

Determines how far a shot advances per tick after ACTION_FIRE succeeds.

◆ Pose

struct Pose

Pose on the unit torus.

A pose describes position and orientation in the engine's 2D world:

  • x and y are coordinates on the unit torus in the interval [0, 1).
  • heading is an orientation angle in degrees in [0, 360) with:
    • 0 deg = up,
    • 90 deg = right,
    • 180 deg = down,
    • 270 deg = left.

In freestanding/WASM builds, callers typically populate a Pose via get_pose_buffer() (module-owned singleton storage) rather than passing a pointer to host-allocated memory.

Data Fields
float heading Heading in degrees in [0, 360).
float x x-position in [0, 1).
float y y-position in [0, 1).

Enumeration Type Documentation

◆ ActionFlags

enum ActionFlags : unsigned int

Action bitmask.

The engine interprets the argument of set_action() as a bitwise OR of these flags. Unless explicitly stated otherwise, flags are combinable.

Dynamics (turn rate, max velocity, shot velocity, lifetimes, etc.) are defined by the current configuration as provided via create_context() using Config.

ACTION_TURN_LEFT and ACTION_TURN_RIGHT are logically mutually exclusive. If a caller sets both, the engine may ignore both, pick one deterministically, or apply an engine-defined tie-breaker.

Enumerator
ACTION_NONE 

Do nothing this tick.

ACTION_THRUST 

Enable thrust for this tick.

Ship speed is binary: either zero or the configured maximum. If ACTION_THRUST is set, the ship's velocity is set to Config::ship_max_velocity. If ACTION_THRUST is not set, the ship's velocity is set to zero.

ACTION_TURN_LEFT 

Turn left for this tick.

Turning is binary: if ACTION_TURN_LEFT is set, the ship's heading is changed by Config::ship_max_turn_rate degrees (left) for this tick. If it is not set, no left turn is applied.

ACTION_TURN_RIGHT 

Turn right for this tick.

Turning is binary: if ACTION_TURN_RIGHT is set, the ship's heading is changed by exactly Config::ship_max_turn_rate degrees (right) for this tick. If it is not set, no right turn is applied.

ACTION_FIRE 

Fire a shot.

Shots travel with velocity Config::shot_velocity and expire after Config::shot_lifetime ticks. Each agent_id may have at most one active shot at a time; if a shot is already active, firing is ignored.

Function Documentation

◆ add_agent()

uint32_t add_agent ( struct Context *  ctx,
const struct Pose pose 
)

Add a new ship/agent to the round.

Registers a new ship in ctx and assigns it a fresh 32-bit agent_id. The returned agent_id is the handle used by all other per-ship engine API calls (e.g., set_action(), get_ship_pose(), get_shot_pose(), is_alive(), get_score()).

This function is intended for round setup after create_context() and before the first tick().

Parameters
ctxEngine context.
poseInitial ship pose (see Pose).
Returns
Opaque 32-bit agent_id identifying the newly added ship. Agent IDs are not required to be zero-based nor sequential; callers must not assume any particular numbering scheme.
Warning
ctx and pose must not be NULL.
Note
In freestanding/WASM builds, pose is typically populated via get_pose_buffer() (module-owned singleton storage).

◆ create_context()

struct Context * create_context ( const struct Config cfg)

Create a new engine context (start a new round).

Allocates and initializes a fresh Context using the provided configuration. The returned context is independent from any other context and represents a single round.

Parameters
cfgImmutable configuration for the new round.
Returns
A new Context on success, or NULL on failure (for example, if static resources are exhausted in freestanding builds).
Warning
cfg must not be NULL.
See also
Config
free_context

◆ free_context()

void free_context ( struct Context *  ctx)

Destroy a context created by create_context().

Releases all resources owned by ctx.

Parameters
ctxContext pointer returned by create_context(). If NULL, the function has no effect.
See also
create_context

◆ get_config_buffer()

struct Config * get_config_buffer ( void  )

Return a pointer to a module-owned Config singleton buffer.

This helper exists for freestanding/WASM builds where the caller cannot conveniently allocate a Config inside the engine module. It provides module-owned storage that can be populated in-place and then passed to create_context().

Typical usage:

struct Config *cfg = get_config_buffer();
cfg->ship_max_velocity = .02F;
struct Context *ctx = create_context(cfg);
struct Config * get_config_buffer(void)
Return a pointer to a module-owned Config singleton buffer.
float ship_max_velocity
Ship speed when thrust is enabled (in torus-units per tick).
Definition scubywasm_engine.h:189
Returns
Pointer to a module-owned scratch Config.
Warning
The returned pointer refers to a singleton buffer and is not re-entrant. Its contents are overwritten by the caller; if multiple independent configurations must be retained, copy the struct elsewhere before reusing the buffer.

◆ get_pose_buffer()

struct Pose * get_pose_buffer ( void  )

Return a pointer to a module-owned Pose singleton buffer.

This helper exists for freestanding/WASM builds where the caller cannot conveniently allocate a Pose inside the engine module. The returned buffer can be used to pass poses into the engine without directly interacting with the module's linear memory allocation scheme.

Typical usage:

struct Pose *pose = get_pose_buffer();
pose->x = .5F;
pose->y = .5F;
pose->heading = 90.F;
uint32_t id = add_agent(ctx, pose);
struct Pose * get_pose_buffer(void)
Return a pointer to a module-owned Pose singleton buffer.
float heading
Heading in degrees in [0, 360).
Definition scubywasm_engine.h:239
float x
x-position in [0, 1).
Definition scubywasm_engine.h:233
uint32_t add_agent(struct Context *ctx, const struct Pose *pose)
Add a new ship/agent to the round.
float y
y-position in [0, 1).
Definition scubywasm_engine.h:236
Pose on the unit torus.
Definition scubywasm_engine.h:231
Returns
Pointer to a module-owned scratch Pose.
Warning
The returned pointer refers to a singleton buffer and is not re-entrant. Its contents are overwritten by the caller; if multiple independent values must be retained, copy the struct elsewhere before reusing the buffer.

◆ get_score()

int32_t get_score ( const struct Context *  ctx,
uint32_t  agent_id 
)

Get the current score of a ship.

Returns the score accumulated by agent_id in the current round. Score updates occur during tick() (e.g., when shots hit or ships are destroyed), so callers typically query scores after advancing the simulation.

Parameters
ctxEngine context.
agent_idShip/agent identifier.
Returns
Current score (signed).
Warning
ctx must not be NULL.
See also
tick

◆ get_ship_pose()

void get_ship_pose ( const struct Context *  ctx,
uint32_t  agent_id,
struct Pose pose 
)

Get the current pose of a ship.

Writes the current ship pose (position on the unit torus and heading) of agent_id into pose.

Parameters
ctxEngine context.
agent_idShip/agent identifier.
poseOutput pointer receiving the ship pose.
Warning
ctx and pose must not be NULL.
See also
Pose
get_pose_buffer (FREESTANDING only)
tick

◆ get_shot_pose()

int32_t get_shot_pose ( const struct Context *  ctx,
uint32_t  agent_id,
struct Pose pose 
)

Get the current pose of the active shot of a ship.

If agent_id currently has an active shot, writes its pose into pose and returns the remaining lifetime of that shot (in ticks). If no shot is active, returns 0.

Parameters
ctxEngine context.
agent_idShip/agent identifier (shot owner).
poseOutput pointer receiving the shot pose if a shot is active.
Returns
Remaining shot lifetime in ticks (> 0) if a shot is active, 0 if shot is inactive.
Warning
ctx and pose must not be NULL.
Note
If the return value is <= 0, the contents of pose are unspecified.
See also
Pose
get_pose_buffer (FREESTANDING only)
tick

◆ is_alive()

int32_t is_alive ( const struct Context *  ctx,
uint32_t  agent_id 
)

Check whether a ship is alive.

A ship is either alive (1) or not alive (0). Ships may transition from alive to not alive during tick() (e.g., due to collisions).

Parameters
ctxEngine context.
agent_idShip/agent identifier.
Returns
1 if the ship is alive, 0 if it is not alive.
Warning
ctx must not be NULL.
See also
tick

◆ set_action()

int32_t set_action ( struct Context *  ctx,
uint32_t  agent_id,
enum ActionFlags  flags 
)

Process an agent action for the next tick.

Interprets flags as a bitwise OR of ActionFlags and updates the corresponding state in ctx immediately (ship kinematics and, if requested and allowed, shot creation), using the configuration embedded in the context (see Config).

Call this exactly once per ship per simulation tick, before calling tick().

Warning
Do not call this multiple times for the same agent_id within a single tick. The function performs immediate state updates; repeated calls will stack effects in engine-defined ways. Nothing good comes of it.
Parameters
ctxEngine context.
agent_idShip/agent identifier.
flagsBitmask of ActionFlags.
Returns
0 on success, a negative integer otherwise
See also
ActionFlags
tick

◆ set_default_config()

void set_default_config ( struct Config cfg)

Initialize a Config to engine defaults.

Writes a complete default configuration into cfg. Callers may override individual fields afterwards before passing the configuration to create_context().

Parameters
cfgOutput pointer to the configuration to initialize.
Warning
cfg must not be NULL.
See also
Config
create_context
get_config_buffer (FREESTANDING only)

◆ tick()

uint32_t tick ( struct Context *  ctx,
uint32_t  n_times 
)

Advance the simulation by one or more ticks.

Advances the engine state in ctx by n_times simulation ticks. This updates all time-dependent state (ship/shot motion, lifetimes, collisions, scoring, etc.).

tick() may be called repeatedly without any additional setup. In particular, if no new actions are processed via set_action(), the simulation simply continues from the current ship state.

Parameters
ctxEngine context.
n_timesNumber of ticks to simulate.
Returns
The number of ships that are alive after advancing the simulation.
Warning
ctx must not be NULL.
See also
set_action
is_alive