<!-- Generated from sdk/bindings/zig/README.md and protocol/v2/api.json by protocol/v2/codegen/docs.js; do not edit. Run `make api`. -->
# The Zig binding

Clunkyard's Zig API calls the shared core through direct typed extern
functions. C layouts, declarations, subsystem methods and conformance
bridges are generated from `protocol/v2/api.json`. The run loop and error
mapping are written by hand. Tracking, geometry, parsing and serialization
stay in the core.

Install with `make install SDK=/opt/clunkyard/sdk`. Build a robot with:

```
zig build-exe -O ReleaseSafe -mcpu=baseline --dep clunkyard -Mroot=bot.zig \
  -Mclunkyard=/opt/clunkyard/sdk/bindings/zig/clunkyard.zig \
  /opt/clunkyard/sdk/lib/libclunkyard.a -lc -lunwind -lm -ldl -lpthread \
  -femit-bin=bot
```

Subsystem methods return error unions. Core errors set `robot.core.last_status`
and return `error.Core`; `cy.c.cy_last_error` retrieves the core's message.
Optional results use `null`, records are copied values, and enums are i32
constants so unknown values can reach the core for validation. Text and list
results are allocated with `robot.core.allocator` and belong to the caller;
free each with that allocator. Controls take an options struct and merge
within the turn. Getters such as `drive.get_throttle()` read pending fields.
`robot.forfeit(code,message)` sends a forfeit and returns `error.Forfeit`.
Other callback errors are logged before sending the controls already set.
`cy.play(Strategy,lines,allocator)` returns owned reply lines.

## A tiny robot

```zig
const cy=@import("clunkyard");
const Bot=struct {
    pub fn onStart(_: *@This(),_: *cy.Robot) !void {}
    pub fn onTurn(_: *@This(),robot:*cy.Robot) !void {
        const target=(try robot.sensors.nearest()) orelse {
            try robot.drive.set(.{.throttle=0.75,.turn=0.25});
            try robot.scanner.set(.{.heading=try robot.me.heading(),.arc=40});
            try robot.status.set(.{.text="SEARCH",.value=(try robot.scanner.last()).arc});
            return;
        };
        const power=try robot.gun.power_for_flight_time(target.range,1);
        const aim=try robot.gun.lead(target,power);
        try robot.gun.aim(.{.heading=if(aim) |a| a.heading else target.bearing,.pitch=if(aim) |a| a.pitch else target.elevation});
        if(try robot.gun.can_fire(power,10))try robot.gun.fire(.{.power=power});
        try robot.drive.set(.{.throttle=0.5,.turn=try robot.angles.clamp(try robot.angles.angle_diff(target.bearing,try robot.me.heading()),-1,1)});
        try robot.scanner.set(.{.heading=target.bearing,.arc=10});
        try robot.status.set(.{.text="TRACK",.value=target.range});
    }
};
pub fn main() !void {try cy.run(Bot);}
```

`make test SDK=...` compares every API vector with the core transcript,
including exact floating-point bits and act lines.

## Names

Every entry of the robot API 2.0.0 in Zig.
[names.md](names.md) has every language side by side.

| Canonical | Form | Zig |
| --- | --- | --- |
| `match.mode` | property | match.mode() |
| `match.ruleset` | property | match.ruleset() |
| `match.control` | property | match.control() |
| `match.robots` | property | match.robots() |
| `match.is_3d` | property | match.is_3d() |
| `match.tick_rate` | property | match.tick_rate() |
| `match.max_ticks` | property | match.max_ticks() |
| `match.robot_id` | call | match.robot_id() |
| `clock.tick` | property | clock.tick() |
| `clock.seconds` | property | clock.seconds() |
| `clock.remaining_ticks` | property | clock.remaining_ticks() |
| `arena.size` | property | arena.size() |
| `arena.gravity` | property | arena.gravity() |
| `arena.terrain_kind` | property | arena.terrain_kind() |
| `arena.ground_height` | call | arena.ground_height() |
| `me.health` | property | me.health() |
| `me.position` | property | me.position() |
| `me.velocity` | property | me.velocity() |
| `me.heading` | property | me.heading() |
| `me.pitch` | property | me.pitch() |
| `me.roll` | property | me.roll() |
| `me.angular_velocity` | property | me.angular_velocity() |
| `me.attitude` | property | me.attitude() |
| `me.grounded` | property | me.grounded() |
| `me.altitude` | property | me.altitude() |
| `me.applied` | property | me.applied() |
| `battery.charge` | property | battery.charge() |
| `battery.capacity` | property | battery.capacity() |
| `battery.regen` | property | battery.regen() |
| `battery.sustainable_drive` | property | battery.sustainable_drive() |
| `battery.compute` | property | battery.compute() |
| `battery.drive_cost` | call | battery.drive_cost() |
| `drive.hover_lift` | property | drive.hover_lift() |
| `drive.set` | control | drive.set() |
| `drive.forces` | control | drive.forces() |
| `drive.get` | call | drive.get() |
| `gun.aim_now` | property | gun.aim_now() |
| `gun.cooldown_ticks` | property | gun.cooldown_ticks() |
| `gun.spread` | property | gun.spread() |
| `gun.max_power` | property | gun.max_power() |
| `gun.damage_scale` | property | gun.damage_scale() |
| `gun.can_fire` | call | gun.can_fire() |
| `gun.shot_speed` | call | gun.shot_speed() |
| `gun.shot_cost` | call | gun.shot_cost() |
| `gun.reload_ticks` | call | gun.reload_ticks() |
| `gun.shot_damage` | call | gun.shot_damage() |
| `gun.power_for_speed` | call | gun.power_for_speed() |
| `gun.power_for_flight_time` | call | gun.power_for_flight_time() |
| `gun.lead` | call | gun.lead() |
| `gun.lead_point` | call | gun.lead_point() |
| `gun.aim` | control | gun.aim() |
| `gun.fire` | control | gun.fire() |
| `gun.get` | call | gun.get() |
| `scanner.last` | property | scanner.last() |
| `scanner.proximity_range` | property | scanner.proximity_range() |
| `scanner.reach` | call | scanner.reach() |
| `scanner.arc_for_reach` | call | scanner.arc_for_reach() |
| `scanner.set` | control | scanner.set() |
| `scanner.get` | call | scanner.get() |
| `sensors.contacts` | property | sensors.contacts() |
| `sensors.nearest` | property | sensors.nearest() |
| `sensors.cues` | property | sensors.cues() |
| `sensors.contact` | call | sensors.contact() |
| `sensors.position_of` | call | sensors.position_of() |
| `tracker.cue` | property | tracker.cue() |
| `tracker.nearest_bearing_rate` | property | tracker.nearest_bearing_rate() |
| `tracker.velocity` | call | tracker.velocity() |
| `tracker.position` | call | tracker.position() |
| `tracker.last_seen` | call | tracker.last_seen() |
| `tracker.configure` | call | tracker.configure() |
| `nav.zone` | property | nav.zone() |
| `nav.zone.margin` | call | nav.zone_margin() |
| `nav.zone.contains` | call | nav.zone_contains() |
| `nav.zone.distance_from` | call | nav.zone_distance_from() |
| `nav.storm` | property | nav.storm() |
| `nav.storm.margin` | call | nav.storm_margin() |
| `nav.storm.contains` | call | nav.storm_contains() |
| `nav.storm.distance_from` | call | nav.storm_distance_from() |
| `nav.hill` | property | nav.hill() |
| `nav.hill.margin` | call | nav.hill_margin() |
| `nav.hill.contains` | call | nav.hill_contains() |
| `nav.hill.distance_from` | call | nav.hill_distance_from() |
| `nav.pickups` | property | nav.pickups() |
| `nav.nearest_pickup` | call | nav.nearest_pickup() |
| `nav.field_distance` | call | nav.field_distance() |
| `score.me` | property | score.me() |
| `score.best` | property | score.best() |
| `score.armour` | property | score.armour() |
| `angles.normalize` | call | angles.normalize() |
| `angles.angle_diff` | call | angles.angle_diff() |
| `angles.bearing_to` | call | angles.bearing_to() |
| `angles.elevation_to` | call | angles.elevation_to() |
| `angles.direction` | call | angles.direction() |
| `angles.distance` | call | angles.distance() |
| `angles.clamp` | call | angles.clamp() |
| `status.set` | control | status.set() |
| `status.get` | call | status.get() |
| `raw.number` | call | raw.number() |
| `raw.text` | call | raw.text() |
| `raw.has` | call | raw.has() |
| `raw.flag` | call | raw.flag() |
| `raw.count` | call | raw.count() |
| `raw.line` | call | raw.line() |
| `raw.extra` | call | raw.extra() |
| `session.event` | property | session.event() |
| `session.next` | call | session.next() |
| `session.send` | call | session.send() |
| `session.forfeit` | call | session.forfeit() |
| `session.log` | call | session.log() |
| `session.feed` | call | session.feed() |
| `session.reply` | call | session.reply() |
