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

`clunkyard` exposes the native API as deterministic IO predicates. Inputs
come first, followed by the status and the IO state. Copy-out arguments use
typed references: allocate with `new_float_ref`, call the core predicate,
then use `read_float_ref`. `check(Robot, Status, !IO)` throws the core's error
text for a negative status; `cy_none` means absent.

Native record types are opaque and owned by Mercury's collector. Allocate
with `new_contact` or `new_contact_array(Count, ...)`, then read fields with
predicates such as `contact_range`. `contact_at` selects an element of an
array. `set_vec3` and the other record constructors accept every field in
schema order. References and record views must stay within their allocated
capacity. Buffers preserve explicit byte lengths, including embedded NUL:
`text_buffer` copies a string, while `new_buffer` and `buffer_set_byte`
construct arbitrary UTF-8 bytes. `buffer_text` copies a returned text value.

Install with `make install SDK=/opt/clunkyard/sdk`. Build in the source
directory with `mmc --make --mld /opt/clunkyard/sdk/bindings/mercury/lib/mercury --ml clunkyard --mercury-linkage static --c-include-directory /opt/clunkyard/sdk/include --ld-flags -Wl,--whole-archive,/opt/clunkyard/sdk/lib/libclunkyard.a,--no-whole-archive -l pthread -l dl -l m bot`.
The core and Mercury runtime link statically; the artifact carries the executable.
The archive flags make core symbols available before Mercury's library is linked.

`run(Turn, Start, !IO)` owns the session and protocol loop. Callbacks receive
the core handle and IO state. Exceptions are logged and the turn sends any
controls already set; `forfeit(Code, Message)` makes an explicit forfeit.
A core fault sends the empty reply and continues. `play(Lines, Turn, Start,
Replies, !IO)` runs a memory session.

## A tiny robot

```mercury
:- module bot.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module clunkyard, int.
:- pred set(pred(robot, float, int, io, io)::in(pred(in, in, out, di, uo) is det), robot::in, float::in, io::di, io::uo) is det.
set(Control, R, Value, !IO) :- Control(R, Value, Status, !IO), check(R, Status, !IO).
:- pred status(robot::in, string::in, float::in, io::di, io::uo) is det.
status(R, Text, Value, !IO) :- text_buffer(Text, Bytes, Length, !IO), cy_status_text(R, Bytes, Length, S, !IO), check(R, S, !IO), set(cy_status_value, R, Value, !IO).
:- pred start(robot::in, io::di, io::uo) is det.
start(_, !IO).
:- pred turn(robot::in, io::di, io::uo) is det.
turn(R, !IO) :-
    new_contact(Target, !IO), cy_sensors_nearest(R, Target, Found, !IO),
    new_float_ref(Number, !IO), cy_me_heading(R, Number, HS, !IO), check(R, HS, !IO), read_float_ref(Number, Heading, !IO),
    (if Found = cy_ok then
        contact_range(Target, Range, !IO), contact_bearing(Target, Bearing, !IO), contact_elevation(Target, Elevation, !IO),
        cy_gun_power_for_flight_time(R, Range, 1.0, Number, PS, !IO), check(R, PS, !IO), read_float_ref(Number, Power, !IO),
        new_solution(Aim, !IO), cy_gun_lead(R, Target, Power, Aim, AS, !IO),
        (if AS = cy_ok then solution_heading(Aim, AH, !IO), solution_pitch(Aim, AP, !IO) else AH = Bearing, AP = Elevation),
        set(cy_gun_aim_heading, R, AH, !IO), set(cy_gun_aim_pitch, R, AP, !IO),
        new_int_ref(Fire, !IO), cy_gun_can_fire(R, Power, 10.0, Fire, FS, !IO), check(R, FS, !IO), read_int_ref(Fire, CanFire, !IO),
        (if CanFire \= 0 then cy_gun_fire(R, Fired, !IO), check(R, Fired, !IO), set(cy_gun_power, R, Power, !IO) else true),
        cy_angles_angle_diff(Bearing, Heading, Number, DS, !IO), check(R, DS, !IO), read_float_ref(Number, Delta, !IO),
        cy_angles_clamp(Delta, -1.0, 1.0, Number, CS, !IO), check(R, CS, !IO), read_float_ref(Number, Control, !IO),
        set(cy_drive_throttle, R, 0.5, !IO), set(cy_drive_turn, R, Control, !IO),
        set(cy_scanner_heading, R, Bearing, !IO), set(cy_scanner_arc, R, 10.0, !IO), status(R, "TRACK", Range, !IO)
    else
        set(cy_drive_throttle, R, 0.75, !IO), set(cy_drive_turn, R, 0.25, !IO),
        set(cy_scanner_heading, R, Heading, !IO), set(cy_scanner_arc, R, 40.0, !IO),
        new_scan(Last, !IO), cy_scanner_last(R, Last, LS, !IO), check(R, LS, !IO), scan_arc(Last, Arc, !IO), status(R, "SEARCH", Arc, !IO)
    ).
main(!IO) :- run(turn, start, !IO).
```

`make test` checks every generated Mercury predicate against the core's full
vectors, including typed foreign arguments and records, exact floating-point
results and serialized acts.

## Names

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

| Canonical | Form | Mercury |
| --- | --- | --- |
| `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_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_throttle, cy_drive_turn, cy_drive_pitch_turn |
| `drive.forces` | control | cy_drive_thrust, cy_drive_strafe, cy_drive_lift, cy_drive_yaw_torque, cy_drive_pitch_torque, cy_drive_roll_torque, cy_drive_brake |
| `drive.get` | call | cy_drive_get_throttle, cy_drive_get_turn, cy_drive_get_pitch_turn, cy_drive_get_thrust, cy_drive_get_strafe, cy_drive_get_lift, cy_drive_get_yaw_torque, cy_drive_get_pitch_torque, cy_drive_get_roll_torque, cy_drive_get_brake |
| `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, cy_gun_aim_pitch |
| `gun.fire` | control | cy_gun_fire, cy_gun_power |
| `gun.get` | call | cy_gun_get_aim_heading, cy_gun_get_aim_pitch, cy_gun_get_fire, cy_gun_get_power |
| `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_heading, cy_scanner_arc, cy_scanner_pitch |
| `scanner.get` | call | cy_scanner_get_heading, cy_scanner_get_arc, cy_scanner_get_pitch |
| `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_text, cy_status_value |
| `status.get` | call | cy_status_get_text, cy_status_get_value |
| `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 |
