Run the engagement engine: march, battle, siege, and crossing
This tutorial runs the military reference starter. An army marches with the reference world’s movement plugin, the host places its force on arrival, and a field battle is stepped day by day and then in half-hour steps; meanwhile a place is besieged, breached, and stormed, and a river force crosses past an enemy fleet. You will learn how canwu-military takes your rulebook and your inputs, when it steps a fight, and which consequences it leaves to your game.
The military simulation extension is split into three crates:
canwu-military: the engagement engine: forces, engagements, sieges, crossings, their records and reports, built oncanwu-api.canwu-military-reference-content: one explicitly synthetic combat rulebook.canwu-military-reference: a replaceable reference integration that installs that rulebook and six forces on the reference world.
Run it
Section titled “Run it”From the repository root:
cargo run -p canwu-military-reference --example military_starterengagement=canwu:military:crossing:capital-ford/ferry-0 kind=FerryFight outcome=Stalled steps=1 site_fell=false losses=0/0engagement=canwu:military:engagement:eastern-field kind=Field outcome=AttackerPrevailed steps=121 site_fell=false losses=16/424engagement=canwu:military:siege:westford/assault-1 kind=Assault outcome=AttackerPrevailed steps=5 site_fell=true losses=10/100siege=Fell cause=Some(Breach) crossing=Complete landed=2000…military_starter=complete engagements=3 signals=3 checkpoint=…military_starter=complete means every assertion passed: each closed fight signalled the host, the besieged place fell, and the restored and replayed copies have the same checkpoint hash as the live run. The lines between print the reports one government holds.
How a fight steps
Section titled “How a fight steps”View diagram source
flowchart TB
Inputs["Host: SupplyInput per side,<br/>PlaceForce, OpenEngagement"] --> Apply["Phase 7: apply commands"]
Wake["Daily calendar boundary<br/>wake system"] --> Packet["military_step_v1"]
Packet --> Step["Phase 7: step what is due<br/>resolver, losses, break rule"]
Apply --> Step
Step --> Summary["Closed: engagement_summary<br/>outcome signal"]
Step --> Reports["Phase 13: holder reports"]
Summary --> Host["Host applies control,<br/>stores, population"]
The starter’s rulebook trusts the reference host’s System issuer. Every command is a MilitaryCommandV1 sent through canonical ingress with military_command(); a command that changes a record names that record’s expected_host_revision. The engine steps only in a boundary that admits its step packet: here the default wake system schedules one on each daily calendar boundary that has work due. A host that prefers to drive stepping itself builds MilitaryPlugin::new().with_step_cadence(None) and sends Step { up_to } after its inputs, or queues a timed step (timed_step_ingress) for each daily boundary it settles anyway, so stepping adds no boundary.
What the starter does
Section titled “What the starter does”demo_engagement_scenario() starts from the reference world’s demo scenario, adds a rival government, and installs synthetic_rulebook_v1() with six forces: a force at the capital that matches the field army, a rival field force in the east, a siege pair in the west, and a river force facing a rival fleet at the capital.
| Day | Host commands | What the engine does |
|---|---|---|
| 0 | the army’s movement order (reference world); InvestSite at Westford with storm_on: BreachOrGate; OrderCrossing from the capital toward Westford past the fleet |
opens the siege and the crossing |
| 1 | PlaceForce for the arrived army; OpenEngagement { kind: Field } in the east |
the first wave is intercepted into a ferry fight; the siege’s guns emplace |
| 2 | SupplyInput for both sides; ReviseCrossing { night: true } |
the field fight steps; the ferry fight closes; the night wave lands |
| 3 | SupplyInput; ReviseStepProfile to half-hour steps |
the breach opens and the storm begins the same day |
| 4 and later | SupplyInput while the fight lasts |
the field fight closes in half-hour steps; the assault breaks the garrison, the place falls, and its defenders disperse |
The field engagement, as the starter opens it:
MilitaryCommandV1::OpenEngagement(Box::new(OpenEngagementV1 { id: field(), kind: EngagementKind::Field, profile: None, site: place(ids.eastern_territory), fort: None, attackers: vec![force_id("yun-field")], defenders: vec![force_id("rival-field")], holders: [ vec![ours.clone(), KnowledgeHolderRef::Person(ids.commander)], vec![theirs.clone()], ], inputs: [condition("field", 720), condition("camp", 680)], environment: environment(false), max_minutes: None,}))The host sends the PlaceForce and the OpenEngagement in one admission order: the engine checks co-location against the force as placed, so the two compose in the same boundary.
What stays with your game
Section titled “What stays with your game”The engine writes only its own records. A fallen place is site_fell in the assault’s summary and Fell with cause Breach on the siege; the starter’s host receives each closed fight’s EngagementOutcomeSignalV1 in a tiny plugin of its own. Control and occupation, casualties as people, and supply stores are your game’s to change from those facts; doctrine, betrayal, rations, and naval patrols are your game’s to decide and pass in. Military lists what each command checks and writes, the step formula, and the limits.
The reference rulebook is synthetic
Section titled “The reference rulebook is synthetic”synthetic_rulebook_v1() has six terrain rows, five fortification grades, five band cuts, four postures, a day and a half-hour step profile, the six kind rules, and the reference limits. Its values are demonstration numbers chosen so the starter’s fights end in a few days; they make no historical claim. A game compiles its own rulebook from its own content.
Read the full starter source for every command and the snapshot and replay checks.