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

The Clunkyard robot API in Lua 5.4, through a generated C module linked to
the robot core. The arena supplies the binding; a robot carries its own
source and imports `clunkyard`. No compiler runs in the robot container.

Install with `make install SDK=/opt/clunkyard/sdk`. Run with
`LUA_PATH='/opt/clunkyard/sdk/bindings/lua/?.lua;;'
LUA_CPATH='/opt/clunkyard/sdk/bindings/lua/?.so;;' lua5.4 bot.lua`.
The shim loads `libclunkyard.so.2` from the SDK.

Facts are properties, calls use Lua's colon syntax, and controls take a
table of the fields to set. Later calls merge with earlier ones in the
same tick. Records are tables and an absent result is `nil`.
`cy.forfeit(code, message)` forfeits the match. Other errors are logged
with their trace; the turn sends the controls already set.
`cy.play(robot, lines)` plays protocol lines in memory for tests.

## A tiny robot

```lua
local cy = require("clunkyard")
local bot = {}

function bot:on_turn()
  local target = self.sensors.nearest
  if not target then
    self.drive:set({throttle=0.75, turn=0.25})
    self.scanner:set({heading=self.me.heading, arc=40})
    self.status:set({text="SEARCH", value=self.scanner.last.arc})
    return
  end
  local power = self.gun:power_for_flight_time(target.range, 1)
  local aim = self.gun:lead(target, power)
  self.gun:aim({heading=aim and aim.heading or target.bearing,
    pitch=aim and aim.pitch or target.elevation})
  if self.gun:can_fire(power, 10) then self.gun:fire({power=power}) end
  self.drive:set({throttle=0.5,
    turn=cy.angles:clamp(cy.angles:angle_diff(target.bearing, self.me.heading))})
  self.scanner:set({heading=target.bearing, arc=10})
  self.status:set({text="TRACK", value=target.range})
end

cy.run(bot)
```

`make test SDK=...` checks the generated API against every core vector,
including exact floating-point results and act lines. The declarations,
record conversions, wrapper and conformance runner are generated from
`protocol/v2/api.json`; only the runtime integration is specific to Lua.

## Names

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

| Canonical | Form | Lua |
| --- | --- | --- |
| `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 |
