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

The Clunkyard robot API in Ruby, through `Fiddle` and the core's C ABI.
`clunkyard/api.rb` is generated from `protocol/v2/api.json`. Only the run
loop and exception mapping in `clunkyard.rb` are written by hand; physics,
tracking, parsing and act serialization run in the core.

Install with `make install SDK=/opt/clunkyard/sdk`. Run a robot with
`RUBYLIB=/opt/clunkyard/sdk/bindings/ruby ruby bot.rb`. The binding loads
`libclunkyard.so.2`, or the file named by `CLUNKYARD_LIB`.

Subsystems and facts are methods. Optional results are `nil`; records have
named attributes, and enums use their protocol strings. Controls accept
positional arguments or keywords and change only the fields supplied.
`drive.get('throttle')` reads a pending field. Raise
`Clunkyard::Forfeit.new(code, message)` to forfeit. Other exceptions are
logged with their trace and send the controls already set.
`Clunkyard.play(robot, lines)` plays lines in memory for tests.

## A tiny robot

```ruby
require 'clunkyard'

class Tiny < Clunkyard::Robot
  def on_turn
    target = sensors.nearest
    unless target
      drive.set(throttle: 0.75, turn: 0.25)
      scanner.set(me.heading, 40)
      status.set('SEARCH', scanner.last.arc)
      return
    end
    power = gun.power_for_flight_time(target.range, 1)
    aim = gun.lead(target, power)
    aim ? gun.aim(aim.heading, aim.pitch) : gun.aim(target.bearing, target.elevation)
    gun.fire(power) if gun.can_fire(power, 10)
    drive.set(throttle: 0.5, turn: angles.clamp(angles.angle_diff(target.bearing, me.heading)))
    scanner.set(target.bearing, 10)
    status.set('TRACK', target.range)
  end
end

Clunkyard.run(Tiny.new)
```

`make test SDK=...` compares every API vector against the core, including
exact floating-point results and act lines. The README robot participates
in the cross-language act identity check.

## Names

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

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