<!-- Generated from sdk/bindings/kotlin/README.md and protocol/v2/api.json by protocol/v2/codegen/docs.js; do not edit. Run `make api`. -->
# Kotlin on the Java binding

Kotlin calls the arena's generated Java core binding directly. Import
`clunkyard.*`; the robot API and C ABI conversion live in the same
`clunkyard.jar` as Java, with no second implementation or copied SDK.

Compile with `kotlinc Bot.kt -cp /opt/clunkyard/sdk/bindings/java/clunkyard.jar
-include-runtime -d bot.jar`. Run with `java --enable-native-access=ALL-UNNAMED
-cp bot.jar:/opt/clunkyard/sdk/bindings/java/clunkyard.jar BotKt`. The jar
loads `libclunkyard.so.2`, or the file named by `CLUNKYARD_LIB`.

Subsystems and facts are methods, and absent results use Java `Optional`,
`OptionalDouble` or `OptionalInt`. Controls take their fields in order or a
builder such as `Drive.throttle(0.5).turn(0.2)`. Fields merge within a turn.
Throw `Forfeit(code, message)` to forfeit; other exceptions are logged and
send the controls already set. `Clunkyard.play` plays lines in memory.

## A tiny robot

```kotlin
import clunkyard.*

class Bot : Robot() {
    override fun onTurn() {
        val target = sensors().nearest().orElse(null)
        if (target == null) {
            drive().set(Drive.throttle(0.75).turn(0.25))
            scanner().set(me().heading(), 40.0)
            status().set("SEARCH", scanner().last().arc())
            return
        }
        val power = gun().powerForFlightTime(target.range(), 1.0)
        val aim = gun().lead(target, power).orElse(null)
        if (aim == null) gun().aim(target.bearing(), target.elevation())
        else gun().aim(aim.heading(), aim.pitch())
        if (gun().canFire(power, 10.0)) gun().fire(power)
        drive().set(Drive.throttle(0.5).turn(angles().clamp(angles().angleDiff(target.bearing(), me().heading()))))
        scanner().set(target.bearing(), 10.0)
        status().set("TRACK", target.range())
    }
}
fun main() = Clunkyard.run(Bot())
```

`make test SDK=...` compiles Kotlin against the shared binding and runs its
full API conformance transcript. The README robot is also compared with the
other bindings' examples, act for act.

## Names

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

| Canonical | Form | Kotlin |
| --- | --- | --- |
| `match.mode` | property | match().mode() |
| `match.ruleset` | property | match().ruleset() |
| `match.control` | property | match().control() |
| `match.robots` | property | match().robots() |
| `match.is_3d` | property | match().is3d() |
| `match.tick_rate` | property | match().tickRate() |
| `match.max_ticks` | property | match().maxTicks() |
| `match.robot_id` | call | match().robotId() |
| `clock.tick` | property | clock().tick() |
| `clock.seconds` | property | clock().seconds() |
| `clock.remaining_ticks` | property | clock().remainingTicks() |
| `arena.size` | property | arena().size() |
| `arena.gravity` | property | arena().gravity() |
| `arena.terrain_kind` | property | arena().terrainKind() |
| `arena.ground_height` | call | arena().groundHeight() |
| `me.health` | property | me().health() |
| `me.position` | property | me().position() |
| `me.velocity` | property | me().velocity() |
| `me.heading` | property | me().heading() |
| `me.pitch` | property | me().pitch() |
| `me.roll` | property | me().roll() |
| `me.angular_velocity` | property | me().angularVelocity() |
| `me.attitude` | property | me().attitude() |
| `me.grounded` | property | me().grounded() |
| `me.altitude` | property | me().altitude() |
| `me.applied` | property | me().applied() |
| `battery.charge` | property | battery().charge() |
| `battery.capacity` | property | battery().capacity() |
| `battery.regen` | property | battery().regen() |
| `battery.sustainable_drive` | property | battery().sustainableDrive() |
| `battery.compute` | property | battery().compute() |
| `battery.drive_cost` | call | battery().driveCost() |
| `drive.hover_lift` | property | drive().hoverLift() |
| `drive.set` | control | drive().set() |
| `drive.forces` | control | drive().forces() |
| `drive.get` | call | drive().get() |
| `gun.aim_now` | property | gun().aimNow() |
| `gun.cooldown_ticks` | property | gun().cooldownTicks() |
| `gun.spread` | property | gun().spread() |
| `gun.max_power` | property | gun().maxPower() |
| `gun.damage_scale` | property | gun().damageScale() |
| `gun.can_fire` | call | gun().canFire() |
| `gun.shot_speed` | call | gun().shotSpeed() |
| `gun.shot_cost` | call | gun().shotCost() |
| `gun.reload_ticks` | call | gun().reloadTicks() |
| `gun.shot_damage` | call | gun().shotDamage() |
| `gun.power_for_speed` | call | gun().powerForSpeed() |
| `gun.power_for_flight_time` | call | gun().powerForFlightTime() |
| `gun.lead` | call | gun().lead() |
| `gun.lead_point` | call | gun().leadPoint() |
| `gun.aim` | control | gun().aim() |
| `gun.fire` | control | gun().fire() |
| `gun.get` | call | gun().get() |
| `scanner.last` | property | scanner().last() |
| `scanner.proximity_range` | property | scanner().proximityRange() |
| `scanner.reach` | call | scanner().reach() |
| `scanner.arc_for_reach` | call | scanner().arcForReach() |
| `scanner.set` | control | scanner().set() |
| `scanner.get` | call | scanner().get() |
| `sensors.contacts` | property | sensors().contacts() |
| `sensors.nearest` | property | sensors().nearest() |
| `sensors.cues` | property | sensors().cues() |
| `sensors.contact` | call | sensors().contact() |
| `sensors.position_of` | call | sensors().positionOf() |
| `tracker.cue` | property | tracker().cue() |
| `tracker.nearest_bearing_rate` | property | tracker().nearestBearingRate() |
| `tracker.velocity` | call | tracker().velocity() |
| `tracker.position` | call | tracker().position() |
| `tracker.last_seen` | call | tracker().lastSeen() |
| `tracker.configure` | call | tracker().configure() |
| `nav.zone` | property | nav().zone() |
| `nav.zone.margin` | call | nav().zone().margin() |
| `nav.zone.contains` | call | nav().zone().contains() |
| `nav.zone.distance_from` | call | nav().zone().distanceFrom() |
| `nav.storm` | property | nav().storm() |
| `nav.storm.margin` | call | nav().storm().margin() |
| `nav.storm.contains` | call | nav().storm().contains() |
| `nav.storm.distance_from` | call | nav().storm().distanceFrom() |
| `nav.hill` | property | nav().hill() |
| `nav.hill.margin` | call | nav().hill().margin() |
| `nav.hill.contains` | call | nav().hill().contains() |
| `nav.hill.distance_from` | call | nav().hill().distanceFrom() |
| `nav.pickups` | property | nav().pickups() |
| `nav.nearest_pickup` | call | nav().nearestPickup() |
| `nav.field_distance` | call | nav().fieldDistance() |
| `score.me` | property | score().me() |
| `score.best` | property | score().best() |
| `score.armour` | property | score().armour() |
| `angles.normalize` | call | angles().normalize() |
| `angles.angle_diff` | call | angles().angleDiff() |
| `angles.bearing_to` | call | angles().bearingTo() |
| `angles.elevation_to` | call | angles().elevationTo() |
| `angles.direction` | call | angles().direction() |
| `angles.distance` | call | angles().distance() |
| `angles.clamp` | call | angles().clamp() |
| `status.set` | control | status().set() |
| `status.get` | call | status().get() |
| `raw.number` | call | raw().number() |
| `raw.text` | call | raw().text() |
| `raw.has` | call | raw().has() |
| `raw.flag` | call | raw().flag() |
| `raw.count` | call | raw().count() |
| `raw.line` | call | raw().line() |
| `raw.extra` | call | raw().extra() |
| `session.event` | property | session().event() |
| `session.next` | call | session().next() |
| `session.send` | call | session().send() |
| `session.forfeit` | call | session().forfeit() |
| `session.log` | call | session().log() |
| `session.feed` | call | session().feed() |
| `session.reply` | call | session().reply() |
