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

One persistent CBQN process runs the tactic. The installed JavaScript
binding owns its robot core session. Generated `cy.*` functions synchronously
query that core over the existing pipe; geometry, lead, tracking and protocol
logic remain in the core. Install with `make install SDK=/opt/clunkyard/sdk`
after the JavaScript binding. Run with
`NODE_PATH=/opt/clunkyard/sdk/bindings/bqn:/opt/clunkyard/sdk/bindings/javascript /opt/clunkyard/sdk/bindings/javascript/run bot.js`.

The namespace is passed to the tactic through `•args`; start with
`cy ← ⊑•args`. A property call takes `@`, a single argument is passed directly,
and multiple arguments form a list: `cy.GunPowerForFlightTime range‿1`.
Vectors are three numbers. Other records are namespaces with camelCase
fields. An absent result is `@`. Controls take namespaces;
`cy.DriveSet {throttle⇐0.5}` sets only that field. Getters take a field name.
All names and record shapes are generated from `protocol/v2/api.json`.

The familiar `g cy.ShotSpeed power`, `g cy.Lead args`, region helpers and
control builders are also available. `Turn` returns a list of control pairs,
or calls the canonical control functions and returns `⟨⟩`. Controls from
both paths reach the same core. `Start` is optional. `run` and `play` handle
session failures and forfeits through the JavaScript binding. Tactic errors
are logged and the turn preserves controls already set.

## A tiny robot

Save this tactic as `tiny.bqn`:

```bqn
cy ← ⊑•args
Search ← {𝕊:
  cy.DriveSet {throttle⇐0.75 ⋄ turn⇐0.25}
  cy.ScannerSet {heading⇐cy.MeHeading @ ⋄ arc⇐40}
  cy.StatusSet {text⇐"SEARCH" ⋄ value⇐(cy.ScannerLast @).arc}
}
Track ← {target←𝕩
  power←cy.GunPowerForFlightTime target.range‿1
  aim←cy.GunLead target‿power
  direction←{6=•Type 𝕩 ? 𝕩 ; {heading⇐target.bearing ⋄ pitch⇐target.elevation}} aim
  cy.GunAim {heading⇐direction.heading ⋄ pitch⇐direction.pitch}
  {cy.GunCanFire power‿10 ? cy.GunFire {power⇐power} ; @}
  cy.DriveSet {throttle⇐0.5 ⋄ turn⇐cy.AnglesClamp ⟨cy.AnglesAngleDiff target.bearing‿(cy.MeHeading @),¯1,1⟩}
  cy.ScannerSet {heading⇐target.bearing ⋄ arc⇐10}
  cy.StatusSet {text⇐"TRACK" ⋄ value⇐target.range}
}
Turn ⇐ {g 𝕊 state:
  target←cy.SensorsNearest @
  (6=•Type target)◶Search‿Track target
  ⟨⟩
}
```

Save the launcher as `bot.js`:

```javascript
const {run} = require('clunkyard_bqn');
run(require('node:path').join(__dirname, 'tiny.bqn'));
```

`make test` calls the actual CBQN query and control functions against the
API vectors. [Complete names](bqn.md).

## Names

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

| Canonical | Form | BQN |
| --- | --- | --- |
| `match.mode` | property | cy.MatchMode |
| `match.ruleset` | property | cy.MatchRuleset |
| `match.control` | property | cy.MatchControl |
| `match.robots` | property | cy.MatchRobots |
| `match.is_3d` | property | cy.MatchIs3d |
| `match.tick_rate` | property | cy.MatchTickRate |
| `match.max_ticks` | property | cy.MatchMaxTicks |
| `match.robot_id` | call | cy.MatchRobotId |
| `clock.tick` | property | cy.ClockTick |
| `clock.seconds` | property | cy.ClockSeconds |
| `clock.remaining_ticks` | property | cy.ClockRemainingTicks |
| `arena.size` | property | cy.ArenaSize |
| `arena.gravity` | property | cy.ArenaGravity |
| `arena.terrain_kind` | property | cy.ArenaTerrainKind |
| `arena.ground_height` | call | cy.ArenaGroundHeight |
| `me.health` | property | cy.MeHealth |
| `me.position` | property | cy.MePosition |
| `me.velocity` | property | cy.MeVelocity |
| `me.heading` | property | cy.MeHeading |
| `me.pitch` | property | cy.MePitch |
| `me.roll` | property | cy.MeRoll |
| `me.angular_velocity` | property | cy.MeAngularVelocity |
| `me.attitude` | property | cy.MeAttitude |
| `me.grounded` | property | cy.MeGrounded |
| `me.altitude` | property | cy.MeAltitude |
| `me.applied` | property | cy.MeApplied |
| `battery.charge` | property | cy.BatteryCharge |
| `battery.capacity` | property | cy.BatteryCapacity |
| `battery.regen` | property | cy.BatteryRegen |
| `battery.sustainable_drive` | property | cy.BatterySustainableDrive |
| `battery.compute` | property | cy.BatteryCompute |
| `battery.drive_cost` | call | cy.BatteryDriveCost |
| `drive.hover_lift` | property | cy.DriveHoverLift |
| `drive.set` | control | cy.DriveSet |
| `drive.forces` | control | cy.DriveForces |
| `drive.get` | call | cy.DriveGet |
| `gun.aim_now` | property | cy.GunAimNow |
| `gun.cooldown_ticks` | property | cy.GunCooldownTicks |
| `gun.spread` | property | cy.GunSpread |
| `gun.max_power` | property | cy.GunMaxPower |
| `gun.damage_scale` | property | cy.GunDamageScale |
| `gun.can_fire` | call | cy.GunCanFire |
| `gun.shot_speed` | call | cy.GunShotSpeed |
| `gun.shot_cost` | call | cy.GunShotCost |
| `gun.reload_ticks` | call | cy.GunReloadTicks |
| `gun.shot_damage` | call | cy.GunShotDamage |
| `gun.power_for_speed` | call | cy.GunPowerForSpeed |
| `gun.power_for_flight_time` | call | cy.GunPowerForFlightTime |
| `gun.lead` | call | cy.GunLead |
| `gun.lead_point` | call | cy.GunLeadPoint |
| `gun.aim` | control | cy.GunAim |
| `gun.fire` | control | cy.GunFire |
| `gun.get` | call | cy.GunGet |
| `scanner.last` | property | cy.ScannerLast |
| `scanner.proximity_range` | property | cy.ScannerProximityRange |
| `scanner.reach` | call | cy.ScannerReach |
| `scanner.arc_for_reach` | call | cy.ScannerArcForReach |
| `scanner.set` | control | cy.ScannerSet |
| `scanner.get` | call | cy.ScannerGet |
| `sensors.contacts` | property | cy.SensorsContacts |
| `sensors.nearest` | property | cy.SensorsNearest |
| `sensors.cues` | property | cy.SensorsCues |
| `sensors.contact` | call | cy.SensorsContact |
| `sensors.position_of` | call | cy.SensorsPositionOf |
| `tracker.cue` | property | cy.TrackerCue |
| `tracker.nearest_bearing_rate` | property | cy.TrackerNearestBearingRate |
| `tracker.velocity` | call | cy.TrackerVelocity |
| `tracker.position` | call | cy.TrackerPosition |
| `tracker.last_seen` | call | cy.TrackerLastSeen |
| `tracker.configure` | call | cy.TrackerConfigure |
| `nav.zone` | property | cy.NavZone |
| `nav.zone.margin` | call | cy.NavZoneMargin |
| `nav.zone.contains` | call | cy.NavZoneContains |
| `nav.zone.distance_from` | call | cy.NavZoneDistanceFrom |
| `nav.storm` | property | cy.NavStorm |
| `nav.storm.margin` | call | cy.NavStormMargin |
| `nav.storm.contains` | call | cy.NavStormContains |
| `nav.storm.distance_from` | call | cy.NavStormDistanceFrom |
| `nav.hill` | property | cy.NavHill |
| `nav.hill.margin` | call | cy.NavHillMargin |
| `nav.hill.contains` | call | cy.NavHillContains |
| `nav.hill.distance_from` | call | cy.NavHillDistanceFrom |
| `nav.pickups` | property | cy.NavPickups |
| `nav.nearest_pickup` | call | cy.NavNearestPickup |
| `nav.field_distance` | call | cy.NavFieldDistance |
| `score.me` | property | cy.ScoreMe |
| `score.best` | property | cy.ScoreBest |
| `score.armour` | property | cy.ScoreArmour |
| `angles.normalize` | call | cy.AnglesNormalize |
| `angles.angle_diff` | call | cy.AnglesAngleDiff |
| `angles.bearing_to` | call | cy.AnglesBearingTo |
| `angles.elevation_to` | call | cy.AnglesElevationTo |
| `angles.direction` | call | cy.AnglesDirection |
| `angles.distance` | call | cy.AnglesDistance |
| `angles.clamp` | call | cy.AnglesClamp |
| `status.set` | control | cy.StatusSet |
| `status.get` | call | cy.StatusGet |
| `raw.number` | call | cy.RawNumber |
| `raw.text` | call | cy.RawText |
| `raw.has` | call | cy.RawHas |
| `raw.flag` | call | cy.RawFlag |
| `raw.count` | call | cy.RawCount |
| `raw.line` | call | cy.RawLine |
| `raw.extra` | call | cy.RawExtra |
| `session.event` | property | cy.SessionEvent |
| `session.next` | call | cy.SessionNext |
| `session.send` | call | cy.SessionSend |
| `session.forfeit` | call | cy.SessionForfeit |
| `session.log` | call | cy.SessionLog |
| `session.feed` | call | cy.SessionFeed |
| `session.reply` | call | cy.SessionReply |
