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

C#, F# and PowerShell share one generated `net8.0` assembly, `Clunkyard.dll`,
which calls the core's C ABI through P/Invoke. It loads under the image's
.NET 10 runtime and PowerShell's .NET 9 runtime. The API, record layouts,
enums and conformance dispatch are generated from `protocol/v2/api.json`.
Only loading, the run loop and exception mapping are written by hand.

Install with `make install SDK=/opt/clunkyard/sdk`. Reference
`/opt/clunkyard/sdk/bindings/csharp/Clunkyard.dll` when compiling and set
`Private` to `false` on that reference. Run with
`dotnet /opt/clunkyard/sdk/bindings/csharp/host/Clunkyard.Host.dll bot.dll`.
The SDK host resolves the binding from the image; the artifact holds only
its own program and other dependencies. The binding loads
`libclunkyard.so.2`, or the file named by `CLUNKYARD_LIB`.

Without Make, run `node install.cjs SDK_DIRECTORY` with your installed .NET SDK.
Both commands use the same installer and need no Unix shell utilities on Windows.
Build files stay in a temporary directory; only the binding and host assemblies
are installed into the selected SDK. Set `CLUNKYARD_LIB` to the matching native
core library, including `clunkyard.dll` on Windows or `libclunkyard.2.dylib` on
macOS. The player CLI selects that path for local matches.

Subsystems and facts are properties. Methods use PascalCase. Optional
records are `null`, and optional scalars are nullable value types. Controls
accept nullable fields, with named arguments for changing only one field;
fields merge within the turn. `Drive.Get("throttle")` reads one back.
Throw `new Forfeit(code, message)` to forfeit. Other exceptions are logged
with their trace and send the controls already set. `Clunkyard.Play` plays
lines in memory for tests.

## A tiny robot

```csharp
using Clunkyard;

class Bot : Robot {
    public override void OnTurn(){
        var target=Sensors.Nearest;
        if(target==null){
            Drive.Set(throttle:0.75,turn:0.25);
            Scanner.Set(Me.Heading,40);
            Status.Set("SEARCH",Scanner.Last.Arc);
            return;
        }
        double power=Gun.PowerForFlightTime(target.Range,1);
        var aim=Gun.Lead(target,power);
        Gun.Aim(aim?.Heading??target.Bearing,aim?.Pitch??target.Elevation);
        if(Gun.CanFire(power,10))Gun.Fire(power);
        Drive.Set(throttle:0.5,turn:Angles.Clamp(Angles.AngleDiff(target.Bearing,Me.Heading)));
        Scanner.Set(target.Bearing,10);
        Status.Set("TRACK",target.Range);
    }
    static void Main()=>Clunkyard.Clunkyard.Run(new Bot());
}
```

`make test SDK=...` compares every API vector with the core's transcript,
including exact floating-point results and act lines. The tiny robot also
participates in the cross-language act identity check.

## Names

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

| Canonical | Form | C# / .NET |
| --- | --- | --- |
| `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 |
