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

Clunkyard's Swift API calls the shared core through its generated C ABI.
The generator emits subsystem methods, records and conformance bridges
from `protocol/v2/api.json`. The run loop and exception mapping are written
by hand. Protocol parsing, tracking, physics and act serialization stay in
the core.

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

```
swiftc -O -parse-as-library -I/opt/clunkyard/sdk/bindings/swift bot.swift \
  -L/opt/clunkyard/sdk/bindings/swift -lClunkyardSwift \
  -L/opt/clunkyard/sdk/lib -Xlinker -l:libclunkyard.a -lm -ldl -lpthread -o bot
```

Properties and methods throw `CoreError`, which carries the core status.
Optional results are `nil`. Lists and text are owned Swift values. Records
use C layouts; snake-case fields also have camelCase accessors. Enum values
are Int32 constants, allowing unknown values to reach the core for validation.
Controls take named optional fields and merge within the turn. Getter
methods such as `drive.getThrottle()` read pending controls. Throw
`Forfeit(code,message)` to forfeit; other callback errors are logged with a
stack trace before sending the controls already set. `play(lines)` uses an
in-memory core session.

## A tiny robot

```swift
import Clunkyard
@main final class Bot:Robot {
    override func onTurn() throws {
        guard let target=try sensors.nearest else {
            try drive.set(throttle:0.75,turn:0.25)
            try scanner.set(heading:me.heading,arc:40)
            try status.set(text:"SEARCH",value:scanner.last.arc)
            return
        }
        let power=try gun.powerForFlightTime(target.range,1),aim=try gun.lead(target,power)
        try gun.aim(heading:aim?.heading ?? target.bearing,pitch:aim?.pitch ?? target.elevation)
        if try gun.canFire(power,10) {try gun.fire(power:power)}
        try drive.set(throttle:0.5,turn:angles.clamp(angles.angleDiff(target.bearing,me.heading),-1,1))
        try scanner.set(heading:target.bearing,arc:10)
        try status.set(text:"TRACK",value:target.range)
    }
    static func main() throws {try Bot().run()}
}
```

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

## Names

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

| Canonical | Form | Swift |
| --- | --- | --- |
| `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.is3d |
| `match.tick_rate` | property | match.tickRate |
| `match.max_ticks` | property | match.maxTicks |
| `match.robot_id` | call | match.robotId() |
| `clock.tick` | property | clock.tick |
| `clock.seconds` | property | clock.seconds |
| `clock.remaining_ticks` | property | clock.remainingTicks |
| `arena.size` | property | arena.size |
| `arena.gravity` | property | arena.gravity |
| `arena.terrain_kind` | property | arena.terrainKind |
| `arena.ground_height` | call | arena.groundHeight() |
| `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.angularVelocity |
| `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.sustainableDrive |
| `battery.compute` | property | battery.compute |
| `battery.drive_cost` | call | battery.driveCost() |
| `drive.hover_lift` | property | drive.hoverLift |
| `drive.set` | control | drive.set() |
| `drive.forces` | control | drive.forces() |
| `drive.get` | call | drive.get() |
| `gun.aim_now` | property | gun.aimNow |
| `gun.cooldown_ticks` | property | gun.cooldownTicks |
| `gun.spread` | property | gun.spread |
| `gun.max_power` | property | gun.maxPower |
| `gun.damage_scale` | property | gun.damageScale |
| `gun.can_fire` | call | gun.canFire() |
| `gun.shot_speed` | call | gun.shotSpeed() |
| `gun.shot_cost` | call | gun.shotCost() |
| `gun.reload_ticks` | call | gun.reloadTicks() |
| `gun.shot_damage` | call | gun.shotDamage() |
| `gun.power_for_speed` | call | gun.powerForSpeed() |
| `gun.power_for_flight_time` | call | gun.powerForFlightTime() |
| `gun.lead` | call | gun.lead() |
| `gun.lead_point` | call | gun.leadPoint() |
| `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.proximityRange |
| `scanner.reach` | call | scanner.reach() |
| `scanner.arc_for_reach` | call | scanner.arcForReach() |
| `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.positionOf() |
| `tracker.cue` | property | tracker.cue |
| `tracker.nearest_bearing_rate` | property | tracker.nearestBearingRate |
| `tracker.velocity` | call | tracker.velocity() |
| `tracker.position` | call | tracker.position() |
| `tracker.last_seen` | call | tracker.lastSeen() |
| `tracker.configure` | call | tracker.configure() |
| `nav.zone` | property | nav.zone |
| `nav.zone.margin` | call | nav.zoneMargin() |
| `nav.zone.contains` | call | nav.zoneContains() |
| `nav.zone.distance_from` | call | nav.zoneDistanceFrom() |
| `nav.storm` | property | nav.storm |
| `nav.storm.margin` | call | nav.stormMargin() |
| `nav.storm.contains` | call | nav.stormContains() |
| `nav.storm.distance_from` | call | nav.stormDistanceFrom() |
| `nav.hill` | property | nav.hill |
| `nav.hill.margin` | call | nav.hillMargin() |
| `nav.hill.contains` | call | nav.hillContains() |
| `nav.hill.distance_from` | call | nav.hillDistanceFrom() |
| `nav.pickups` | property | nav.pickups |
| `nav.nearest_pickup` | call | nav.nearestPickup() |
| `nav.field_distance` | call | nav.fieldDistance() |
| `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.angleDiff() |
| `angles.bearing_to` | call | angles.bearingTo() |
| `angles.elevation_to` | call | angles.elevationTo() |
| `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() |
