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
:- 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 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 |