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

The Clunkyard robot API in SBCL, directly over the core's C ABI through
`sb-alien`. Native declarations, records and wrappers are generated from
`protocol/v2/api.json`. Loading, the run loop and exception mapping are
written by hand. Tracking, geometry, parsing and serialization stay in the
core.

Install with `make install SDK=/opt/clunkyard/sdk`. The installer compiles
the binding into FASL files with the same SBCL version as the runtime image.
Run with `sbcl --noinform --disable-debugger --load
/opt/clunkyard/sdk/bindings/common-lisp/clunkyard.fasl --script bot.lisp`.
The binding loads `libclunkyard.so.2`, or `CLUNKYARD_LIB`.

API functions use kebab-case and take the robot first, except stateless
angle functions. Records use structure accessors, and enums are keywords
matching their protocol names. Optional values return two values: the
value and whether it is present, so an absent optional boolean differs
from false. API lists are vectors. Controls take keyword fields and merge
in the turn. `(cy:drive-get bot :throttle)` reads a pending field.
Call `(cy:forfeit code message)` to forfeit. Other errors are logged with a
backtrace and send the controls already set. `cy:play` plays lines in memory.

## A tiny robot

```lisp
(defun tiny-turn (bot)
  (let ((target (cy:sensors-nearest bot)))
    (unless target
      (cy:drive-set bot :throttle 0.75d0 :turn 0.25d0)
      (cy:scanner-set bot :heading (cy:me-heading bot) :arc 40d0)
      (cy:status-set bot :text "SEARCH" :value (cy:scan-arc (cy:scanner-last bot)))
      (return-from tiny-turn))
    (let* ((power (cy:gun-power-for-flight-time bot (cy:contact-range target) 1d0))
           (aim (cy:gun-lead bot target power)))
      (cy:gun-aim bot :heading (if aim (cy:solution-heading aim) (cy:contact-bearing target))
                      :pitch (if aim (cy:solution-pitch aim) (cy:contact-elevation target)))
      (when (cy:gun-can-fire bot power 10d0) (cy:gun-fire bot :power power))
      (cy:drive-set bot :throttle 0.5d0 :turn (cy:angles-clamp (cy:angles-angle-diff (cy:contact-bearing target) (cy:me-heading bot))))
      (cy:scanner-set bot :heading (cy:contact-bearing target) :arc 10d0)
      (cy:status-set bot :text "TRACK" :value (cy:contact-range target)))))
(cy:run (cy:make-robot :on-turn #'tiny-turn))
```

`make test SDK=...` compares every API vector with the core's transcript,
including exact floating-point bits and act lines. The README robot also
participates in the cross-language identity check.

## Names

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

| Canonical | Form | Common Lisp |
| --- | --- | --- |
| `match.mode` | property | (match-mode robot) |
| `match.ruleset` | property | (match-ruleset robot) |
| `match.control` | property | (match-control robot) |
| `match.robots` | property | (match-robots robot) |
| `match.is_3d` | property | (match-is-3d robot) |
| `match.tick_rate` | property | (match-tick-rate robot) |
| `match.max_ticks` | property | (match-max-ticks robot) |
| `match.robot_id` | call | (match-robot-id robot slot) |
| `clock.tick` | property | (clock-tick robot) |
| `clock.seconds` | property | (clock-seconds robot) |
| `clock.remaining_ticks` | property | (clock-remaining-ticks robot) |
| `arena.size` | property | (arena-size robot) |
| `arena.gravity` | property | (arena-gravity robot) |
| `arena.terrain_kind` | property | (arena-terrain-kind robot) |
| `arena.ground_height` | call | (arena-ground-height robot x y) |
| `me.health` | property | (me-health robot) |
| `me.position` | property | (me-position robot) |
| `me.velocity` | property | (me-velocity robot) |
| `me.heading` | property | (me-heading robot) |
| `me.pitch` | property | (me-pitch robot) |
| `me.roll` | property | (me-roll robot) |
| `me.angular_velocity` | property | (me-angular-velocity robot) |
| `me.attitude` | property | (me-attitude robot) |
| `me.grounded` | property | (me-grounded robot) |
| `me.altitude` | property | (me-altitude robot) |
| `me.applied` | property | (me-applied robot) |
| `battery.charge` | property | (battery-charge robot) |
| `battery.capacity` | property | (battery-capacity robot) |
| `battery.regen` | property | (battery-regen robot) |
| `battery.sustainable_drive` | property | (battery-sustainable-drive robot) |
| `battery.compute` | property | (battery-compute robot) |
| `battery.drive_cost` | call | (battery-drive-cost robot fraction) |
| `drive.hover_lift` | property | (drive-hover-lift robot) |
| `drive.set` | control | (drive-set robot) |
| `drive.forces` | control | (drive-forces robot) |
| `drive.get` | call | (drive-get robot) |
| `gun.aim_now` | property | (gun-aim-now robot) |
| `gun.cooldown_ticks` | property | (gun-cooldown-ticks robot) |
| `gun.spread` | property | (gun-spread robot) |
| `gun.max_power` | property | (gun-max-power robot) |
| `gun.damage_scale` | property | (gun-damage-scale robot) |
| `gun.can_fire` | call | (gun-can-fire robot power reserve) |
| `gun.shot_speed` | call | (gun-shot-speed robot power) |
| `gun.shot_cost` | call | (gun-shot-cost robot power) |
| `gun.reload_ticks` | call | (gun-reload-ticks robot power) |
| `gun.shot_damage` | call | (gun-shot-damage robot power) |
| `gun.power_for_speed` | call | (gun-power-for-speed robot speed) |
| `gun.power_for_flight_time` | call | (gun-power-for-flight-time robot range seconds) |
| `gun.lead` | call | (gun-lead robot contact power) |
| `gun.lead_point` | call | (gun-lead-point robot position velocity power) |
| `gun.aim` | control | (gun-aim robot) |
| `gun.fire` | control | (gun-fire robot) |
| `gun.get` | call | (gun-get robot) |
| `scanner.last` | property | (scanner-last robot) |
| `scanner.proximity_range` | property | (scanner-proximity-range robot) |
| `scanner.reach` | call | (scanner-reach robot arc) |
| `scanner.arc_for_reach` | call | (scanner-arc-for-reach robot range) |
| `scanner.set` | control | (scanner-set robot) |
| `scanner.get` | call | (scanner-get robot) |
| `sensors.contacts` | property | (sensors-contacts robot) |
| `sensors.nearest` | property | (sensors-nearest robot) |
| `sensors.cues` | property | (sensors-cues robot) |
| `sensors.contact` | call | (sensors-contact robot slot) |
| `sensors.position_of` | call | (sensors-position-of robot contact) |
| `tracker.cue` | property | (tracker-cue robot) |
| `tracker.nearest_bearing_rate` | property | (tracker-nearest-bearing-rate robot) |
| `tracker.velocity` | call | (tracker-velocity robot slot) |
| `tracker.position` | call | (tracker-position robot slot) |
| `tracker.last_seen` | call | (tracker-last-seen robot slot) |
| `tracker.configure` | call | (tracker-configure robot smoothing forget-ticks cue-ticks) |
| `nav.zone` | property | (nav-zone robot) |
| `nav.zone.margin` | call | (nav-zone-margin region point) |
| `nav.zone.contains` | call | (nav-zone-contains region point margin) |
| `nav.zone.distance_from` | call | (nav-zone-distance-from region point) |
| `nav.storm` | property | (nav-storm robot) |
| `nav.storm.margin` | call | (nav-storm-margin region point) |
| `nav.storm.contains` | call | (nav-storm-contains region point margin) |
| `nav.storm.distance_from` | call | (nav-storm-distance-from region point) |
| `nav.hill` | property | (nav-hill robot) |
| `nav.hill.margin` | call | (nav-hill-margin region point) |
| `nav.hill.contains` | call | (nav-hill-contains region point margin) |
| `nav.hill.distance_from` | call | (nav-hill-distance-from region point) |
| `nav.pickups` | property | (nav-pickups robot) |
| `nav.nearest_pickup` | call | (nav-nearest-pickup robot kind) |
| `nav.field_distance` | call | (nav-field-distance robot a b) |
| `score.me` | property | (score-me robot) |
| `score.best` | property | (score-best robot) |
| `score.armour` | property | (score-armour robot) |
| `angles.normalize` | call | (angles-normalize degrees) |
| `angles.angle_diff` | call | (angles-angle-diff target current) |
| `angles.bearing_to` | call | (angles-bearing-to from to) |
| `angles.elevation_to` | call | (angles-elevation-to from to) |
| `angles.direction` | call | (angles-direction heading pitch) |
| `angles.distance` | call | (angles-distance a b) |
| `angles.clamp` | call | (angles-clamp value low high) |
| `status.set` | control | (status-set robot) |
| `status.get` | call | (status-get robot) |
| `raw.number` | call | (raw-number robot path) |
| `raw.text` | call | (raw-text robot path) |
| `raw.has` | call | (raw-has robot path) |
| `raw.flag` | call | (raw-flag robot path) |
| `raw.count` | call | (raw-count robot path) |
| `raw.line` | call | (raw-line robot) |
| `raw.extra` | call | (raw-extra robot json) |
| `session.event` | property | (session-event robot) |
| `session.next` | call | (session-next robot) |
| `session.send` | call | (session-send robot) |
| `session.forfeit` | call | (session-forfeit robot code message) |
| `session.log` | call | (session-log robot line) |
| `session.feed` | call | (session-feed robot line) |
| `session.reply` | call | (session-reply robot) |
