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

One persistent Gforth process runs the tactic. The installed Python binding
owns the robot core session; generated `cy-*` words synchronously forward
calls over the existing pipe. Protocol, geometry, lead and tracking remain
in the core. Install with `make install SDK=/opt/clunkyard/sdk` after the
Python binding. Run with
`PYTHONPATH=/opt/clunkyard/sdk/bindings/forth:/opt/clunkyard/sdk/bindings/python python3 bot.py`.

Canonical words follow the API path in kebab case: `cy-me-heading`,
`cy-gun-shot-speed`, `cy-nav-zone-margin`. Arguments follow the API's order,
with numbers on the floating point stack and integers, booleans and enum
codes on the data stack. Text occupies an address and length. Records flatten
in declared field order, with nested records inline. Canonical enum codes
use the API's values, starting at 1. Every word returns a status on the data
stack: 0 for success, 1 for no value, or a negative core error. `cy-result`
throws errors and converts status to a presence flag. A list pushes its
records then its count. Query strings live until the next query.

Controls have one generated word per field,
`cy-drive-set-throttle` or `cy-status-set-text`; each returns status.
Getters take a field string: `s" throttle" cy-drive-get`. Familiar words
such as `lead`, `bearing-to`, `nearest`, `zone-margin`, `power-for-flight-time`
and local control builders (`drive!`, `scan!`, `status!`) remain available.
Their game helpers forward core queries. Compatibility enum constants start
at zero. `seconds`, `remaining-ticks` and `shot-damage` read the current tick.

Hello values and Python field tables are generated from
`protocol/v2/schema.json`, canonical words and control tables from
`protocol/v2/api.json`. Floating point query traffic carries IEEE bytes and
text carries UTF-8 bytes, preserving core results without decimal rounding.
`on-start` is optional; `on-turn` leaves both stacks as it found them. A
thrown turn logs its error and preserves controls already set through core
calls. Forfeits and session faults use the Python binding's behaviour.

## A tiny robot

Save this tactic as `tiny.fs`:

```forth
: on-turn
  cy-sensors-nearest cy-result 0= if
    0.75e cy-drive-set-throttle drop
    0.25e cy-drive-set-turn drop
    cy-me-heading cy-result drop cy-scanner-set-heading drop
    40e cy-scanner-set-arc drop
    s" SEARCH" cy-status-set-text drop
    cy-scanner-last cy-result drop { f: h f: p f: arc f: reach }
    arc cy-status-set-value drop exit
  then
  { slot source f: range f: bearing f: elevation f: be f: re f: x f: y f: z }
  range 1e cy-gun-power-for-flight-time cy-result drop { f: power }
  slot source range bearing elevation be re x y z power cy-gun-lead cy-result if
    { f: h f: p f: t }
    h cy-gun-aim-heading drop p cy-gun-aim-pitch drop
  else
    bearing cy-gun-aim-heading drop elevation cy-gun-aim-pitch drop
  then
  power 10e cy-gun-can-fire cy-result drop if power cy-gun-fire-power drop then
  0.5e cy-drive-set-throttle drop
  bearing cy-me-heading cy-result drop cy-angles-angle-diff cy-result drop
  -1e 1e cy-angles-clamp cy-result drop cy-drive-set-turn drop
  bearing cy-scanner-set-heading drop 10e cy-scanner-set-arc drop
  s" TRACK" cy-status-set-text drop range cy-status-set-value drop ;
```

Save the launcher as `bot.py`:

```python
import clunkyard_forth
clunkyard_forth.run("tiny.fs")
```

`run` and `play` take a tactic filename. `make test` exercises the actual
Gforth words against every API vector.
[Complete names and record layouts](forth.md).

## Names

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

| Canonical | Form | Forth |
| --- | --- | --- |
| `match.mode` | property | cy-match-mode |
| `match.ruleset` | property | cy-match-ruleset |
| `match.control` | property | cy-match-control |
| `match.robots` | property | cy-match-robots |
| `match.is_3d` | property | cy-match-is-3d |
| `match.tick_rate` | property | cy-match-tick-rate |
| `match.max_ticks` | property | cy-match-max-ticks |
| `match.robot_id` | call | cy-match-robot-id |
| `clock.tick` | property | cy-clock-tick |
| `clock.seconds` | property | cy-clock-seconds |
| `clock.remaining_ticks` | property | cy-clock-remaining-ticks |
| `arena.size` | property | cy-arena-size |
| `arena.gravity` | property | cy-arena-gravity |
| `arena.terrain_kind` | property | cy-arena-terrain-kind |
| `arena.ground_height` | call | cy-arena-ground-height |
| `me.health` | property | cy-me-health |
| `me.position` | property | cy-me-position |
| `me.velocity` | property | cy-me-velocity |
| `me.heading` | property | cy-me-heading |
| `me.pitch` | property | cy-me-pitch |
| `me.roll` | property | cy-me-roll |
| `me.angular_velocity` | property | cy-me-angular-velocity |
| `me.attitude` | property | cy-me-attitude |
| `me.grounded` | property | cy-me-grounded |
| `me.altitude` | property | cy-me-altitude |
| `me.applied` | property | cy-me-applied |
| `battery.charge` | property | cy-battery-charge |
| `battery.capacity` | property | cy-battery-capacity |
| `battery.regen` | property | cy-battery-regen |
| `battery.sustainable_drive` | property | cy-battery-sustainable-drive |
| `battery.compute` | property | cy-battery-compute |
| `battery.drive_cost` | call | cy-battery-drive-cost |
| `drive.hover_lift` | property | cy-drive-hover-lift |
| `drive.set` | control | cy-drive-set-throttle |
| `drive.forces` | control | cy-drive-forces-thrust |
| `drive.get` | call | cy-drive-get |
| `gun.aim_now` | property | cy-gun-aim-now |
| `gun.cooldown_ticks` | property | cy-gun-cooldown-ticks |
| `gun.spread` | property | cy-gun-spread |
| `gun.max_power` | property | cy-gun-max-power |
| `gun.damage_scale` | property | cy-gun-damage-scale |
| `gun.can_fire` | call | cy-gun-can-fire |
| `gun.shot_speed` | call | cy-gun-shot-speed |
| `gun.shot_cost` | call | cy-gun-shot-cost |
| `gun.reload_ticks` | call | cy-gun-reload-ticks |
| `gun.shot_damage` | call | cy-gun-shot-damage |
| `gun.power_for_speed` | call | cy-gun-power-for-speed |
| `gun.power_for_flight_time` | call | cy-gun-power-for-flight-time |
| `gun.lead` | call | cy-gun-lead |
| `gun.lead_point` | call | cy-gun-lead-point |
| `gun.aim` | control | cy-gun-aim-heading |
| `gun.fire` | control | cy-gun-fire |
| `gun.get` | call | cy-gun-get |
| `scanner.last` | property | cy-scanner-last |
| `scanner.proximity_range` | property | cy-scanner-proximity-range |
| `scanner.reach` | call | cy-scanner-reach |
| `scanner.arc_for_reach` | call | cy-scanner-arc-for-reach |
| `scanner.set` | control | cy-scanner-set-heading |
| `scanner.get` | call | cy-scanner-get |
| `sensors.contacts` | property | cy-sensors-contacts |
| `sensors.nearest` | property | cy-sensors-nearest |
| `sensors.cues` | property | cy-sensors-cues |
| `sensors.contact` | call | cy-sensors-contact |
| `sensors.position_of` | call | cy-sensors-position-of |
| `tracker.cue` | property | cy-tracker-cue |
| `tracker.nearest_bearing_rate` | property | cy-tracker-nearest-bearing-rate |
| `tracker.velocity` | call | cy-tracker-velocity |
| `tracker.position` | call | cy-tracker-position |
| `tracker.last_seen` | call | cy-tracker-last-seen |
| `tracker.configure` | call | cy-tracker-configure |
| `nav.zone` | property | cy-nav-zone |
| `nav.zone.margin` | call | cy-nav-zone-margin |
| `nav.zone.contains` | call | cy-nav-zone-contains |
| `nav.zone.distance_from` | call | cy-nav-zone-distance-from |
| `nav.storm` | property | cy-nav-storm |
| `nav.storm.margin` | call | cy-nav-storm-margin |
| `nav.storm.contains` | call | cy-nav-storm-contains |
| `nav.storm.distance_from` | call | cy-nav-storm-distance-from |
| `nav.hill` | property | cy-nav-hill |
| `nav.hill.margin` | call | cy-nav-hill-margin |
| `nav.hill.contains` | call | cy-nav-hill-contains |
| `nav.hill.distance_from` | call | cy-nav-hill-distance-from |
| `nav.pickups` | property | cy-nav-pickups |
| `nav.nearest_pickup` | call | cy-nav-nearest-pickup |
| `nav.field_distance` | call | cy-nav-field-distance |
| `score.me` | property | cy-score-me |
| `score.best` | property | cy-score-best |
| `score.armour` | property | cy-score-armour |
| `angles.normalize` | call | cy-angles-normalize |
| `angles.angle_diff` | call | cy-angles-angle-diff |
| `angles.bearing_to` | call | cy-angles-bearing-to |
| `angles.elevation_to` | call | cy-angles-elevation-to |
| `angles.direction` | call | cy-angles-direction |
| `angles.distance` | call | cy-angles-distance |
| `angles.clamp` | call | cy-angles-clamp |
| `status.set` | control | cy-status-set-text |
| `status.get` | call | cy-status-get |
| `raw.number` | call | cy-raw-number |
| `raw.text` | call | cy-raw-text |
| `raw.has` | call | cy-raw-has |
| `raw.flag` | call | cy-raw-flag |
| `raw.count` | call | cy-raw-count |
| `raw.line` | call | cy-raw-line |
| `raw.extra` | call | cy-raw-extra |
| `session.event` | property | cy-session-event |
| `session.next` | call | cy-session-next |
| `session.send` | call | cy-session-send |
| `session.forfeit` | call | cy-session-forfeit |
| `session.log` | call | cy-session-log |
| `session.feed` | call | cy-session-feed |
| `session.reply` | call | cy-session-reply |
