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

The Clunkyard robot API in Node, over the robot core's WebAssembly build.
The generated API and TypeScript declarations share the DSL's core loader;
the binding contains no physics, tracking or protocol parser.

Requires Node 20 or later. For local development, install the npm package under
the same name your robot imports in the arena:

```sh
npm install clunkyard@npm:@clunkyard/sdk
```

Keep `require('clunkyard')` or `import ... from 'clunkyard'` in robot source.
The alias includes the core's WebAssembly build and TypeScript declarations.
Commit `package.json` and `package-lock.json`; keep `node_modules/` out of source
uploads. The arena supplies its own matching binding during hosted builds.
Standalone Node applications may instead install `@clunkyard/sdk` and
import `@clunkyard/sdk` directly.

To build and play a real match, clone the
[JavaScript starter](https://github.com/clunkyard/starter-javascript) or
[TypeScript starter](https://github.com/clunkyard/starter-typescript) and run
`make`, `make match` and `make replay`. Without Make, use
`npx @clunkyard/cli build`, `match` and `replay`. The shared CLI
downloads the separate engine and launcher; the npm SDK package provides the
robot API. The [player kit](../player-kit.md) explains setup and
current tooling. `cy.play(robot, lines)` also accepts recorded protocol
lines for in-memory tests.

For a shared SDK installation, run `make install SDK=/opt/clunkyard/sdk`.
The arena's TypeScript 6 compiler resolves the declaration with
`tsc --ignoreConfig --ignoreDeprecations 6.0 --target ES2022 --module commonjs --baseUrl /opt/clunkyard/sdk/bindings/javascript bot.ts`.
Run either language with `NODE_PATH=/opt/clunkyard/sdk/bindings/javascript
/opt/clunkyard/sdk/bindings/javascript/run bot.js`. The launcher preserves
the protocol pipe and sends standard output to standard error before user
code runs, including writes made through `fs`.

Subsystems are properties of the robot. Facts are properties, calls use
camelCase, and controls take an object of the fields to set. Later controls
merge with earlier ones in the same turn. Optional results are `null`.
Throw `new cy.Forfeit(code, message)` to forfeit. Other exceptions are
logged with their trace and the turn sends the controls already set.

## A tiny robot

```javascript
const cy = require('clunkyard');

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

cy.run(new Tiny());
```

`make test SDK=/opt/clunkyard/sdk` checks the generated API against the
core's conformance vectors, including exact floating-point results and act
lines. The README example also participates in the cross-language identity
check.

## Names

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

| Canonical | Form | JavaScript / TypeScript |
| --- | --- | --- |
| `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 |
