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

The Clunkyard robot API in Dart, directly over the core's C ABI through
`dart:ffi`. Native signatures, records, enums and the public API are
generated from `protocol/v2/api.json`. Only 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`. Import
`package:clunkyard/clunkyard.dart`. Compile with
`dart compile exe --packages=/opt/clunkyard/sdk/bindings/dart/package_config.json
bot.dart -o bot`. The executable loads `libclunkyard.so.2`, or the file named
by `CLUNKYARD_LIB`; the library is supplied by the runtime image.

Subsystems and facts are properties. Calls use camelCase; optional results
are `null`. Records have immutable fields and enums carry their protocol
names. Controls take named fields and merge with earlier controls in the
turn. `drive.get('throttle')` reads a pending field. Throw
`Forfeit(code, message)` to forfeit. Other exceptions are logged with their
trace and send the controls already set. `play(robot, lines)` plays lines
in memory for tests.

## A tiny robot

```dart
import 'package:clunkyard/clunkyard.dart';

class Tiny extends Robot {
  @override void onTurn(){
    final target=sensors.nearest;
    if(target==null){
      drive.set(throttle:0.75,turn:0.25);
      scanner.set(heading:me.heading,arc:40);
      status.set(text:'SEARCH',value:scanner.last.arc);
      return;
    }
    final power=gun.powerForFlightTime(target.range,1);
    final aim=gun.lead(target,power);
    gun.aim(heading:aim?.heading??target.bearing,pitch:aim?.pitch??target.elevation);
    if(gun.canFire(power,10))gun.fire(power:power);
    drive.set(throttle:0.5,turn:angles.clamp(angles.angleDiff(target.bearing,me.heading)));
    scanner.set(heading:target.bearing,arc:10);
    status.set(text:'TRACK',value:target.range);
  }
}
void main()=>run(Tiny());
```

`make test SDK=...` compares every representable API vector with the core's
transcript, including exact floating-point bits and act lines. An unknown
enum integer cannot be represented by Dart's enum type; such invalid calls
are reported explicitly by the conformance test. The README robot also
participates in the cross-language act identity check.

## Names

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

| Canonical | Form | Dart |
| --- | --- | --- |
| `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.zone.margin |
| `nav.zone.contains` | call | nav.zone.contains |
| `nav.zone.distance_from` | call | nav.zone.distanceFrom |
| `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.distanceFrom |
| `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.distanceFrom |
| `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 |
