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

The Clunkyard robot API in R, over the robot core's C ABI through a generated
`.Call` shim ([docs/design/robot-core.md](../design/robot-core.md),
section 5.3). `src/shim.c` and `R/api.R` are generated by
`protocol/v2/codegen/bindings/r.js` (`make api`); the run loop in `R/robot.R`
and the package files are written by hand. It works on R 4.3.
[docs/sdk/names.md](names.md) gives every name.

- **Install:** `make install SDK=/opt/clunkyard/sdk` builds the shim against the
  core and installs the package into the library `$(SDK)/bindings/r`. It needs
  no compiler at run time.
- **Run:** `R_LIBS=/opt/clunkyard/sdk/bindings/r Rscript --vanilla bot.R`.
- **Test:** `make test SDK=...` runs the installed package against every API
  vector, exact against the core.

Subsystems are environments on `bot` (`bot$me`, `bot$match`,
`bot$scanner$last`); facts read like fields and the rest are functions. A
record is a named list and an absent result is `NULL`. A control sets only
the arguments given, so `bot$drive$set(turn = 0.5)` keeps the throttle, and
`bot$drive$get("throttle")` reads a field back. `angles` holds the angle
helpers, the core's to the last bit. Call `forfeit(code, message)` to
forfeit; any other error is logged with its calls and the turn answers with
what it had set.

## A tiny robot

```r
library(clunkyard)

run(robot(on_turn = function(bot) {
  target <- bot$sensors$nearest
  if (is.null(target)) {
    bot$drive$set(throttle = 0.75, turn = 0.25)
    bot$scanner$set(heading = bot$me$heading, arc = 40)
    bot$status$set("SEARCH", bot$scanner$last$arc)
    return(invisible())
  }
  power <- bot$gun$power_for_flight_time(target$range, 1)
  aim <- bot$gun$lead(target, power)
  if (is.null(aim)) bot$gun$aim(target$bearing, target$elevation) else bot$gun$aim(aim$heading, aim$pitch)
  if (bot$gun$can_fire(power, 10)) bot$gun$fire(power)
  bot$drive$set(throttle = 0.5, turn = angles$clamp(angles$angle_diff(target$bearing, bot$me$heading)))
  bot$scanner$set(heading = target$bearing, arc = 10)
  bot$status$set("TRACK", target$range)
}))
```

## Names

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

| Canonical | Form | R |
| --- | --- | --- |
| `match.mode` | property | `bot$match$mode` |
| `match.ruleset` | property | `bot$match$ruleset` |
| `match.control` | property | `bot$match$control` |
| `match.robots` | property | `bot$match$robots` |
| `match.is_3d` | property | `bot$match$is_3d` |
| `match.tick_rate` | property | `bot$match$tick_rate` |
| `match.max_ticks` | property | `bot$match$max_ticks` |
| `match.robot_id` | call | `bot$match$robot_id(slot)` |
| `clock.tick` | property | `bot$clock$tick` |
| `clock.seconds` | property | `bot$clock$seconds` |
| `clock.remaining_ticks` | property | `bot$clock$remaining_ticks` |
| `arena.size` | property | `bot$arena$size` |
| `arena.gravity` | property | `bot$arena$gravity` |
| `arena.terrain_kind` | property | `bot$arena$terrain_kind` |
| `arena.ground_height` | call | `bot$arena$ground_height(x, y)` |
| `me.health` | property | `bot$me$health` |
| `me.position` | property | `bot$me$position` |
| `me.velocity` | property | `bot$me$velocity` |
| `me.heading` | property | `bot$me$heading` |
| `me.pitch` | property | `bot$me$pitch` |
| `me.roll` | property | `bot$me$roll` |
| `me.angular_velocity` | property | `bot$me$angular_velocity` |
| `me.attitude` | property | `bot$me$attitude` |
| `me.grounded` | property | `bot$me$grounded` |
| `me.altitude` | property | `bot$me$altitude` |
| `me.applied` | property | `bot$me$applied` |
| `battery.charge` | property | `bot$battery$charge` |
| `battery.capacity` | property | `bot$battery$capacity` |
| `battery.regen` | property | `bot$battery$regen` |
| `battery.sustainable_drive` | property | `bot$battery$sustainable_drive` |
| `battery.compute` | property | `bot$battery$compute` |
| `battery.drive_cost` | call | `bot$battery$drive_cost(fraction)` |
| `drive.hover_lift` | property | `bot$drive$hover_lift` |
| `drive.set` | control | `bot$drive$set(throttle = NULL, turn = NULL, pitch_turn = NULL)` |
| `drive.forces` | control | `bot$drive$forces(thrust = NULL, strafe = NULL, lift = NULL, yaw_torque = NULL, pitch_torque = NULL, roll_torque = NULL, brake = NULL)` |
| `drive.get` | call | `bot$drive$get("throttle")` |
| `gun.aim_now` | property | `bot$gun$aim_now` |
| `gun.cooldown_ticks` | property | `bot$gun$cooldown_ticks` |
| `gun.spread` | property | `bot$gun$spread` |
| `gun.max_power` | property | `bot$gun$max_power` |
| `gun.damage_scale` | property | `bot$gun$damage_scale` |
| `gun.can_fire` | call | `bot$gun$can_fire(power, reserve)` |
| `gun.shot_speed` | call | `bot$gun$shot_speed(power)` |
| `gun.shot_cost` | call | `bot$gun$shot_cost(power)` |
| `gun.reload_ticks` | call | `bot$gun$reload_ticks(power)` |
| `gun.shot_damage` | call | `bot$gun$shot_damage(power)` |
| `gun.power_for_speed` | call | `bot$gun$power_for_speed(speed)` |
| `gun.power_for_flight_time` | call | `bot$gun$power_for_flight_time(range, seconds)` |
| `gun.lead` | call | `bot$gun$lead(contact, power)` |
| `gun.lead_point` | call | `bot$gun$lead_point(position, velocity, power)` |
| `gun.aim` | control | `bot$gun$aim(heading = NULL, pitch = NULL)` |
| `gun.fire` | control | `bot$gun$fire(power = NULL)` |
| `gun.get` | call | `bot$gun$get("heading")` |
| `scanner.last` | property | `bot$scanner$last` |
| `scanner.proximity_range` | property | `bot$scanner$proximity_range` |
| `scanner.reach` | call | `bot$scanner$reach(arc)` |
| `scanner.arc_for_reach` | call | `bot$scanner$arc_for_reach(range)` |
| `scanner.set` | control | `bot$scanner$set(heading = NULL, arc = NULL, pitch = NULL)` |
| `scanner.get` | call | `bot$scanner$get("heading")` |
| `sensors.contacts` | property | `bot$sensors$contacts` |
| `sensors.nearest` | property | `bot$sensors$nearest` |
| `sensors.cues` | property | `bot$sensors$cues` |
| `sensors.contact` | call | `bot$sensors$contact(slot)` |
| `sensors.position_of` | call | `bot$sensors$position_of(contact)` |
| `tracker.cue` | property | `bot$tracker$cue` |
| `tracker.nearest_bearing_rate` | property | `bot$tracker$nearest_bearing_rate` |
| `tracker.velocity` | call | `bot$tracker$velocity(slot)` |
| `tracker.position` | call | `bot$tracker$position(slot)` |
| `tracker.last_seen` | call | `bot$tracker$last_seen(slot)` |
| `tracker.configure` | call | `bot$tracker$configure(smoothing = 0.5, forget_ticks = 40, cue_ticks = 60)` |
| `nav.zone` | property | `bot$nav$zone` |
| `nav.zone.margin` | call | `bot$nav$zone$margin(point)` |
| `nav.zone.contains` | call | `bot$nav$zone$contains(point, margin = 0)` |
| `nav.zone.distance_from` | call | `bot$nav$zone$distance_from(point)` |
| `nav.storm` | property | `bot$nav$storm` |
| `nav.storm.margin` | call | `bot$nav$storm$margin(point)` |
| `nav.storm.contains` | call | `bot$nav$storm$contains(point, margin = 0)` |
| `nav.storm.distance_from` | call | `bot$nav$storm$distance_from(point)` |
| `nav.hill` | property | `bot$nav$hill` |
| `nav.hill.margin` | call | `bot$nav$hill$margin(point)` |
| `nav.hill.contains` | call | `bot$nav$hill$contains(point, margin = 0)` |
| `nav.hill.distance_from` | call | `bot$nav$hill$distance_from(point)` |
| `nav.pickups` | property | `bot$nav$pickups` |
| `nav.nearest_pickup` | call | `bot$nav$nearest_pickup(kind = NULL)` |
| `nav.field_distance` | call | `bot$nav$field_distance(a, b)` |
| `score.me` | property | `bot$score$me` |
| `score.best` | property | `bot$score$best` |
| `score.armour` | property | `bot$score$armour` |
| `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 = -1, high = 1)` |
| `status.set` | control | `bot$status$set(text = NULL, value = NULL)` |
| `status.get` | call | `bot$status$get("text")` |
| `raw.number` | call | `bot$raw$number(path)` |
| `raw.text` | call | `bot$raw$text(path)` |
| `raw.has` | call | `bot$raw$has(path)` |
| `raw.flag` | call | `bot$raw$flag(path)` |
| `raw.count` | call | `bot$raw$count(path)` |
| `raw.line` | call | `bot$raw$line()` |
| `raw.extra` | call | `bot$raw$extra(json)` |
