The MiniZinc adapter
The installed adapter solves your model once at hello, then Python plays
its plan through the canonical robot core. clunkyard.mzn declares typed
hello parameters generated from protocol/v2/schema.json. Its core_*
tables are computed through core queries; the model contains no copied game
formula. Install with make install SDK=/opt/clunkyard/sdk in this directory,
after the Python binding. Run with
PYTHONPATH=/opt/clunkyard/sdk/bindings/minizinc:/opt/clunkyard/sdk/bindings/python python3 bot.py.
Subclass Planner, set model, and implement on_turn. self.plan reads
the solver's JSON output. Override model_data for extra parameters;
self.core_query("gun.shot_speed", power) calls any canonical core API while
preparing them. This hook returns the Python binding's typed records and
optional None values. Controls, helpers, tracker and live turn data use
the Python API. MiniZinc runs only at hello; it never starts a solver per
turn. run and play accept a Planner; exceptions and forfeits use the
Python binding's behaviour.
core_steer and core_home index -180..180. core_power and core_cue_arc
index 0..core_range_steps with core_range_step pixels between entries.
core_sustainable_drive and core_hover_lift are core values. Plan.at
rounds a requested index and bounds it to the table. Solver JSON represents
floating point numbers at solver precision.
A tiny robot
Save this model as tiny.mzn:
include "clunkyard.mzn";
output ["{}"];
Save this launcher as bot.py:
import clunkyard as cy
import clunkyard_minizinc as mz
class Tiny(mz.Planner):
model = "tiny.mzn"
def on_turn(self):
target = self.sensors.nearest
if target is None:
self.drive.set(throttle=0.75, turn=0.25)
self.scanner.set(heading=self.me.heading, arc=40)
self.status.set("SEARCH", self.scanner.last.arc)
return
power = self.gun.power_for_flight_time(target.range, 1)
aim = self.gun.lead(target, power)
if aim is None:
self.gun.aim(target.bearing, target.elevation)
else:
self.gun.aim(aim.heading, aim.pitch)
if self.gun.can_fire(power, 10):
self.gun.fire(power)
self.drive.set(throttle=0.5, turn=cy.angles.clamp(cy.angles.angle_diff(target.bearing, self.me.heading)))
self.scanner.set(heading=target.bearing, arc=10)
self.status.set("TRACK", target.range)
mz.run(Tiny())
make test checks the complete query hook against the API vectors and runs
six real solver plans, comparing their tables with core query results.
The complete API reference is MiniZinc names.
Names
Every entry of the robot API 2.0.0 in MiniZinc. names.md has every language side by side.
| Canonical | Form | MiniZinc |
|---|---|---|
match.mode |
property | Planner.core_query("match.mode", ...) |
match.ruleset |
property | Planner.core_query("match.ruleset", ...) |
match.control |
property | Planner.core_query("match.control", ...) |
match.robots |
property | Planner.core_query("match.robots", ...) |
match.is_3d |
property | Planner.core_query("match.is_3d", ...) |
match.tick_rate |
property | Planner.core_query("match.tick_rate", ...) |
match.max_ticks |
property | Planner.core_query("match.max_ticks", ...) |
match.robot_id |
call | Planner.core_query("match.robot_id", ...) |
clock.tick |
property | Planner.core_query("clock.tick", ...) |
clock.seconds |
property | Planner.core_query("clock.seconds", ...) |
clock.remaining_ticks |
property | Planner.core_query("clock.remaining_ticks", ...) |
arena.size |
property | Planner.core_query("arena.size", ...) |
arena.gravity |
property | Planner.core_query("arena.gravity", ...) |
arena.terrain_kind |
property | Planner.core_query("arena.terrain_kind", ...) |
arena.ground_height |
call | Planner.core_query("arena.ground_height", ...) |
me.health |
property | Planner.core_query("me.health", ...) |
me.position |
property | Planner.core_query("me.position", ...) |
me.velocity |
property | Planner.core_query("me.velocity", ...) |
me.heading |
property | Planner.core_query("me.heading", ...) |
me.pitch |
property | Planner.core_query("me.pitch", ...) |
me.roll |
property | Planner.core_query("me.roll", ...) |
me.angular_velocity |
property | Planner.core_query("me.angular_velocity", ...) |
me.attitude |
property | Planner.core_query("me.attitude", ...) |
me.grounded |
property | Planner.core_query("me.grounded", ...) |
me.altitude |
property | Planner.core_query("me.altitude", ...) |
me.applied |
property | Planner.core_query("me.applied", ...) |
battery.charge |
property | Planner.core_query("battery.charge", ...) |
battery.capacity |
property | Planner.core_query("battery.capacity", ...) |
battery.regen |
property | Planner.core_query("battery.regen", ...) |
battery.sustainable_drive |
property | Planner.core_query("battery.sustainable_drive", ...) |
battery.compute |
property | Planner.core_query("battery.compute", ...) |
battery.drive_cost |
call | Planner.core_query("battery.drive_cost", ...) |
drive.hover_lift |
property | Planner.core_query("drive.hover_lift", ...) |
drive.set |
control | Planner.core_query("drive.set", ...) |
drive.forces |
control | Planner.core_query("drive.forces", ...) |
drive.get |
call | Planner.core_query("drive.get", ...) |
gun.aim_now |
property | Planner.core_query("gun.aim_now", ...) |
gun.cooldown_ticks |
property | Planner.core_query("gun.cooldown_ticks", ...) |
gun.spread |
property | Planner.core_query("gun.spread", ...) |
gun.max_power |
property | Planner.core_query("gun.max_power", ...) |
gun.damage_scale |
property | Planner.core_query("gun.damage_scale", ...) |
gun.can_fire |
call | Planner.core_query("gun.can_fire", ...) |
gun.shot_speed |
call | Planner.core_query("gun.shot_speed", ...) |
gun.shot_cost |
call | Planner.core_query("gun.shot_cost", ...) |
gun.reload_ticks |
call | Planner.core_query("gun.reload_ticks", ...) |
gun.shot_damage |
call | Planner.core_query("gun.shot_damage", ...) |
gun.power_for_speed |
call | Planner.core_query("gun.power_for_speed", ...) |
gun.power_for_flight_time |
call | Planner.core_query("gun.power_for_flight_time", ...) |
gun.lead |
call | Planner.core_query("gun.lead", ...) |
gun.lead_point |
call | Planner.core_query("gun.lead_point", ...) |
gun.aim |
control | Planner.core_query("gun.aim", ...) |
gun.fire |
control | Planner.core_query("gun.fire", ...) |
gun.get |
call | Planner.core_query("gun.get", ...) |
scanner.last |
property | Planner.core_query("scanner.last", ...) |
scanner.proximity_range |
property | Planner.core_query("scanner.proximity_range", ...) |
scanner.reach |
call | Planner.core_query("scanner.reach", ...) |
scanner.arc_for_reach |
call | Planner.core_query("scanner.arc_for_reach", ...) |
scanner.set |
control | Planner.core_query("scanner.set", ...) |
scanner.get |
call | Planner.core_query("scanner.get", ...) |
sensors.contacts |
property | Planner.core_query("sensors.contacts", ...) |
sensors.nearest |
property | Planner.core_query("sensors.nearest", ...) |
sensors.cues |
property | Planner.core_query("sensors.cues", ...) |
sensors.contact |
call | Planner.core_query("sensors.contact", ...) |
sensors.position_of |
call | Planner.core_query("sensors.position_of", ...) |
tracker.cue |
property | Planner.core_query("tracker.cue", ...) |
tracker.nearest_bearing_rate |
property | Planner.core_query("tracker.nearest_bearing_rate", ...) |
tracker.velocity |
call | Planner.core_query("tracker.velocity", ...) |
tracker.position |
call | Planner.core_query("tracker.position", ...) |
tracker.last_seen |
call | Planner.core_query("tracker.last_seen", ...) |
tracker.configure |
call | Planner.core_query("tracker.configure", ...) |
nav.zone |
property | Planner.core_query("nav.zone", ...) |
nav.zone.margin |
call | Planner.core_query("nav.zone.margin", ...) |
nav.zone.contains |
call | Planner.core_query("nav.zone.contains", ...) |
nav.zone.distance_from |
call | Planner.core_query("nav.zone.distance_from", ...) |
nav.storm |
property | Planner.core_query("nav.storm", ...) |
nav.storm.margin |
call | Planner.core_query("nav.storm.margin", ...) |
nav.storm.contains |
call | Planner.core_query("nav.storm.contains", ...) |
nav.storm.distance_from |
call | Planner.core_query("nav.storm.distance_from", ...) |
nav.hill |
property | Planner.core_query("nav.hill", ...) |
nav.hill.margin |
call | Planner.core_query("nav.hill.margin", ...) |
nav.hill.contains |
call | Planner.core_query("nav.hill.contains", ...) |
nav.hill.distance_from |
call | Planner.core_query("nav.hill.distance_from", ...) |
nav.pickups |
property | Planner.core_query("nav.pickups", ...) |
nav.nearest_pickup |
call | Planner.core_query("nav.nearest_pickup", ...) |
nav.field_distance |
call | Planner.core_query("nav.field_distance", ...) |
score.me |
property | Planner.core_query("score.me", ...) |
score.best |
property | Planner.core_query("score.best", ...) |
score.armour |
property | Planner.core_query("score.armour", ...) |
angles.normalize |
call | Planner.core_query("angles.normalize", ...) |
angles.angle_diff |
call | Planner.core_query("angles.angle_diff", ...) |
angles.bearing_to |
call | Planner.core_query("angles.bearing_to", ...) |
angles.elevation_to |
call | Planner.core_query("angles.elevation_to", ...) |
angles.direction |
call | Planner.core_query("angles.direction", ...) |
angles.distance |
call | Planner.core_query("angles.distance", ...) |
angles.clamp |
call | Planner.core_query("angles.clamp", ...) |
status.set |
control | Planner.core_query("status.set", ...) |
status.get |
call | Planner.core_query("status.get", ...) |
raw.number |
call | Planner.core_query("raw.number", ...) |
raw.text |
call | Planner.core_query("raw.text", ...) |
raw.has |
call | Planner.core_query("raw.has", ...) |
raw.flag |
call | Planner.core_query("raw.flag", ...) |
raw.count |
call | Planner.core_query("raw.count", ...) |
raw.line |
call | Planner.core_query("raw.line", ...) |
raw.extra |
call | Planner.core_query("raw.extra", ...) |
session.event |
property | Planner.core_query("session.event", ...) |
session.next |
call | Planner.core_query("session.next", ...) |
session.send |
call | Planner.core_query("session.send", ...) |
session.forfeit |
call | Planner.core_query("session.forfeit", ...) |
session.log |
call | Planner.core_query("session.log", ...) |
session.feed |
call | Planner.core_query("session.feed", ...) |
session.reply |
call | Planner.core_query("session.reply", ...) |