11 — Combat
This document is the normative specification of combat resolution in Every Last City: the Clash
— a single exchange between two forces that produces losses and, independently, a decision about
whether the ground changes hands — together with bombardment, reactive fire, disengagement, retreat
and rout, encirclement, the persistent strength and disorder a unit carries between turns,
post-combat recovery, and the pre-commitment odds readout. It owns every number that decides who
loses what and who gives ground. It does not own the map, terrain vocabulary, movement, stacking
capacity, fog, or the unit roster; it states precisely what it requires of each.
Status: Draft v0.2 · Owner: unassigned · Depends on: docs/design/00-direction.md, docs/design/01-decision-turn-model.md, docs/design/combat.md, 01-game-rules.md (map, terrain, fog, adjacency, stacking, city ownership), 02-units-and-industry.md (unit combat stats, roles, bombard parameters, retreat class), 10-turn-model.md (Initiative, activation order, Stance, reaction capacity, creation sequence), 12-economy.md (supply state, depots), 13-command.md (Postures, Sanctions, the win-probability contract CM-150/CM-160), 03-architecture.md (determinism, integer arithmetic, seeded PRNG, canonical hashing), 04-ui-ux.md (presentation).
Naming note, binding on this document. Stance always means the per-turn tactical setting owned by 10-turn-model.md with values
March,Advance,Hold,Entrench. Posture always means the standing job owned by 13-command.md (CM-330). The source design proposal used the word posture for the tactical setting and named a fourth value Withdraw; that value is expressed here as the Disengagement procedure of §12, driven by theWithdrawReflex of CM-1400, and is not a Stance.
1. Scope, vocabulary, and what this document owns
CB-010 A Clash MUST be the only mechanism by which one unit reduces another unit's strength
as a direct consequence of an attack, a bombardment, or a reactive engagement. Strength loss from
supply attrition, scuttling, or scripted scenario effects is owned by the specification that inflicts
it and MUST NOT reuse the Clash procedure.
CB-020 A Clash MUST resolve as a single exchange. The engine MUST NOT loop, iterate rounds, sample repeatedly, or run any procedure whose step count depends on the units involved.
Rationale: this is the load-bearing decision of the whole document. A single exchange is what makes the outcome distribution closed-form, which is what makes the pre-commitment readout exact, which is what makes committing orders before seeing them resolve (docs/design/01-decision-turn-model.md) a skill rather than a gamble. It is also what makes worst-case turn time flat regardless of army size.
CB-030 A Clash MUST produce two independent results: a loss result (integer strength removed from each side) and a break result (whether either side loses its nerve). The two MUST be computed from different inputs — losses from the odds ratio, breaks from shock against resolve — and MUST be permitted to disagree.
Rationale: separating "how much did it cost" from "did the line move" is what gives combat texture without adding dice. You can bleed an enemy badly without shifting them, and shift them without killing them, and those are different plans requiring different forces. A system where damage and displacement are the same number is a system where the only question is "do I have more?".
CB-040 Combat MUST NOT be fight-to-the-death. Most Clashes MUST be capable of ending with both units alive. Destruction MUST occur only when (a) a unit's strength reaches 0, or (b) a unit breaks and has no legal retreat destination.
Rationale: retreat is what creates a front — a line that can be pushed, bent and broken through, where taking ground and destroying an enemy are different achievements. If every engagement ends with a corpse the map is a sequence of duels and positioning collapses into arithmetic.
CB-050 Encirclement MUST NOT be a special case. It MUST emerge entirely from the conjunction of CB-040(b) and the retreat-destination rules of §6, with no rule that names "surrounded", "pocket", or "cut off" as a trigger for a distinct code path.
Rationale: two individually trivial rules — "a broken unit retreats" and "a unit that cannot retreat is destroyed" — produce fronts, pockets, breakthroughs, and the entire operational layer for free. The player achieves encirclement by taking away the roads, not by rolling well, and the engine needs no surrounded-unit logic at all.
CB-060 Every quantity in this document MUST be an integer. Fractional quantities MUST be expressed per-mille. Floating-point arithmetic MUST NOT appear anywhere in combat resolution or combat prediction. Q16.16 fixed point MUST NOT be used by this subsystem.
Rationale: the accumulate-wide-divide-once discipline of §5 means every named quantity is produced by exactly one division, so there is no compounding rounding to reason about and no fixed-point representation is needed at all. Using Q16.16 here would add a representation without adding precision and would give a second way to be wrong.
CB-070 Every division in this document MUST be floor division toward negative infinity. Truncation toward zero MUST NOT be used. Implementations in languages whose integer division truncates toward zero MUST implement an explicit floor division helper and use it everywhere.
Rationale: this is not a style preference. The volatility swing s is negative for draws below 500,
and floor(−102.9) = −103 while trunc(−102.9) = −102. That one-unit difference changes break
probabilities by whole per-mille and would make two conforming implementations disagree, which
CB-2500 forbids. It is the single most likely source of a cross-platform desync in this subsystem.
CB-080 This document MUST own: the Clash procedure and all its constants; the terrain combat
profile schema and its default values; the effect of Stance, grade, supply, works and dig-in on
combat; retreat destination selection; recovery of strength and disorder; and the prediction
function of §16. It MUST NOT define terrain types, movement costs, stacking capacity, fog rules,
supply propagation, unit roster values, or city ownership transfer.
CB-090 The terms below are defined once and used throughout with exactly these meanings.
| Term | Meaning |
|---|---|
| Clash | One resolution of the procedure in §5, of one of the kinds in §3 |
| Lead attacker | The unit whose order initiated the Clash; the only attacker that may advance |
| Supporter | A friendly unit adjacent to the target tile that contributes reduced power |
| Attacking force | Lead attacker plus supporters |
| Defender | The single unit of the target stack that contributes defensive power |
| Defending stack | Every unit on the target tile, defender included |
| Flankers | Friendly units of the attacking side adjacent to the target tile, excluding the lead attacker |
| Break | A side's shock reaching its resolve; for the defender this means giving ground |
| Falter | An attacker break; the attack is thrown back |
| Rout | A break with no legal retreat destination, which destroys the broken stack |
| Shock | The pressure applied to a side's nerve: losses taken plus positional terms |
| Resolve | The integer a side's shock must reach for it to break |
| Draw | The single integer r ∈ [0, 1000) that is the only random input to a Clash |
2. What a unit carries
CB-100 Every unit capable of combat MUST carry the following mutable per-unit combat state, which MUST persist across turns and MUST be part of authoritative game state.
| Field | Type | Range | Initial | Meaning |
|---|---|---|---|---|
strength |
integer | 0–100 | 100 | Proportion of the unit remaining; scales its power linearly |
disorder |
integer | 0–100 | 0 | Accumulated shock; subtracts point-for-point from resolve |
xp |
integer | 0–999 | 0 | Drives grade (CB-1600) |
digIn |
integer | 0–4 | 0 | Complete turns spent stationary in Stance Entrench |
shaken |
integer | 0–2 | 0 | Turns remaining of the post-break penalty of CB-1130 |
CB-110 strength MUST be expressed on the same 0–100 scale for every unit type, so that "this
unit is at 60" means the same thing for every unit in the game.
Rationale: a normalised scale is what lets one bar in the UI mean one thing, lets tough express
"hard to grind down" independently of "hard to hurt", and lets a player compare a rifle company and
an armoured battalion without a lookup. It costs the roster designer the ability to express unit size
as hit points; 02-units-and-industry.md gets tough instead, which does that job better.
CB-120 A unit's power contribution MUST scale linearly with strength (CB-470). A unit's resolve
MUST fall by floor((100 − strength) / 2) (CB-710). No other automatic effect of strength on
combat MAY be added without a combatLogicVersion bump (CB-2530).
Rationale: the two condition penalties compound deliberately. A unit at 60 strength with 30 disorder has lost 50 resolve before anything else is counted, so worn units break sooner and attrition snowballs locally. That is what makes a front wear out, what makes rotation a genuine decision, and what makes bombardment worth doing.
CB-130 strength MUST be clamped to [0, 100] and disorder to [0, 100] after every
modification. A unit whose strength reaches 0 as a result of a Clash MUST be destroyed and removed
immediately, before the next unit activates.
CB-140 This document MUST NOT define a separate "suppressed" or "pinned" state. disorder is the
sole representation of a unit having been shaken, and every effect that a suppression flag would have
carried MUST be expressed through disorder.
Rationale: one number instead of two states, with a continuous gradient instead of a cliff, and it recovers on a schedule the player can plan around. A boolean suppression flag would additionally need its own clear condition, its own display, and its own interaction with every other modifier.
CB-150 The static per-class combat stats this document consumes MUST be exactly: atk, def,
tough, role, retreatClass, bombardRange, bombardShots. Their values are owned by
02-units-and-industry.md.
| Stat | Type | Meaning |
|---|---|---|
atk |
integer 0–255 | Offensive base; 0 means the class may never initiate a Clash |
def |
integer 0–255 | Defensive base |
tough |
integer 20–255 | Divides incoming losses; 100 is the reference |
role |
enum | Foot | Armor | Gun | Recon | Naval | Air |
retreatClass |
enum | Ground | Detached (CB-1330) |
bombardRange |
integer 0–4 | 0 means the class may not bombard |
bombardShots |
integer 0–4 | Bombardments permitted per turn |
CB-160 tough MUST divide incoming losses and MUST NOT affect power, resolve, or volatility.
Rationale: it is the roster's lever for "hard to grind down" as distinct from "hard to hurt". A
scout troop and an armoured battalion should not both lose 30 points in an even fight, and expressing
that through def would also make the scout harder to displace, which is wrong.
CB-170 A unit's Grade MUST be derived from xp by CB-1600 and MUST NOT be stored
independently.
CB-180 strength, disorder, xp, digIn and shaken MUST contribute to the canonical state
hash defined by 03-architecture.md.
CB-190 The client MUST be able to render strength and disorder as a single composite
condition badge (04-ui-ux.md owns the visual). This specification's requirement is that the two
numbers are always available together and always mean the same thing; a UI that exposes them as two
raw spreadsheet columns MUST be treated as failing CB-2410.
Rationale: two persistent numbers per unit is real bookkeeping, and at three hundred units it is the thing most likely to make this design worse than what it replaces. The composite badge is a promise the interface has to keep for the mechanical design to be worth its cost.
3. Kinds of engagement
CB-200 The engine MUST recognise exactly four kinds of engagement, and every strength loss caused by combat MUST arise from one of them.
| Kind | Initiated by | Intensity out / back | Break checks | Advance possible |
|---|---|---|---|---|
| Assault | An Attack order or a sanctioned automated attack | 60 / 60 | both sides | yes |
| Bombardment | A Bombard order, at range | 22 / 0 | neither side | no |
| Reaction | The turn model's reaction rule, out of activation | 34 / 14 | both sides | no |
| Disengagement | An attack landing on a unit under a Withdraw Reflex |
none — no draw | none | yes, unopposed |
CB-210 Assault, Bombardment and Reaction MUST all resolve through the identical procedure of §5, differing only in the constants named in CB-200 and in the specific requirements of §10 and §11. A separate resolution path for any of them MUST NOT exist.
Rationale: one procedure means one place to be wrong, one function to prove monotone, one preview to verify, and no possibility of a bombardment preview disagreeing with a bombardment.
CB-220 Disengagement MUST be resolved before any exchange and MUST consume no draw (§12).
CB-230 A unit whose class has atk = 0 MUST NOT initiate an Assault or a Reaction. It MAY still
be a supporter only if its atk > 0; a class with atk = 0 MUST NOT support.
CB-240 Initiating an Assault MUST cost the lead attacker one movement point. A unit with no movement point remaining MUST NOT initiate an Assault. Supporting MUST cost no movement.
Rationale: movement is the natural bound on attacks per turn and needs no separate per-turn attack cap. A unit with four movement points can make four attacks and arrive nowhere, which is a real and legitimate choice, and one the falter and disorder rules already price.
CB-250 A unit that has faltered this turn (CB-1150) MUST NOT initiate a further Assault or Bombardment this turn.
CB-260 A unit that has supported this turn MUST NOT bombard this turn, and a unit that has bombarded this turn MUST NOT support this turn. A unit MUST NOT support more than one Clash per turn.
Rationale: both are firing actions and a gun should not be able to do both. Without this a battery supports the assault it is adjacent to and then shells a second target, which doubles the value of every gun and collapses the positioning decision the support rule exists to create.
CB-270 All state changes produced by a Clash MUST be applied immediately and completely before the next unit activates, so that a later attacker in the same Cascade sees the reduced strength and raised disorder its predecessors caused.
Rationale: this is what makes the siege vector of CB-2040 work, what makes bombard-then-assault a plan rather than a coincidence, and what makes conditional orders evaluated at activation see a true board.
4. The Clash Specification
CB-280 The engine MUST construct a Clash Specification — a value containing every input the procedure of §5 reads — before evaluating any part of that procedure. The procedure MUST read nothing outside the Specification.
Rationale: this is what makes the prediction guarantee of §16 mechanically enforceable rather than aspirational. Resolution and prediction call the same function on the same value; a preview can disagree with a resolution only if the Specification differs, which is a data problem with an inspectable diff, not a logic problem hidden in two copies of the arithmetic.
CB-290 The Clash Specification MUST contain exactly the following fields and no others.
| Field | Type | Source |
|---|---|---|
kind |
enum (CB-200) | The initiating order |
terrain |
terrain combat profile (§7) | The defender's tile, or the mover's halt tile for a Reaction |
lead |
combatant record | The lead attacker |
supporters |
0–2 combatant records | CB-370 |
defender |
combatant record | CB-400 |
stackExtra |
integer 0–3 | CB-1930 |
flankers |
integer 0–255 | CB-980 |
defenderInOwnCity |
boolean | 01-game-rules.md |
defenderEncircled |
boolean | CB-1400 |
attackerEdgeMods |
integer per-mille | CB-590 |
defenderExtraMods |
integer per-mille | CB-620 |
hashKey |
(seed, turn, initiatorSeq, attackOrdinal) | CB-800 |
CB-300 A combatant record MUST contain exactly: atk, def, tough, role, strength,
disorder, grade, supplyTier, stance, digIn, works, shaken, creationSequence.
CB-310 Every field of the Clash Specification MUST be an integer, an enumeration value, or a boolean. It MUST NOT contain object references, entity handles, or map coordinates.
Rationale: a Specification that cannot reach back into the world is a Specification that can be serialised into a test vector, replayed, diffed against a golden master, and shipped to the client for a preview without shipping the world.
CB-320 The Clash Specification MUST be serialisable to a canonical byte form and MUST appear in the combat event emitted for every resolution (CB-2440).
CB-330 A Clash Specification MUST be constructed from the true world state when resolving, and from a player's knowledge view when predicting (§17). The construction function MUST be the same in both cases, parameterised only by the view it reads.
CB-340 A Specification whose lead.strength is 0, whose defender.strength is 0, or whose
defender is absent MUST be rejected before resolution; the initiating order MUST be treated as
unachievable at activation rather than as an error, per the turn model's handling of stale orders.
CB-350 The engine MUST expose buildClashSpec(view, order) → ClashSpec | null as a pure function
of its arguments.
CB-360 Two Clash Specifications that are byte-equal MUST produce byte-equal outcomes on every conforming implementation.
5. The resolution procedure
Every step below is normative in the order given. An implementation that reorders steps, or that combines two divisions into one, will produce different integers and is non-conforming.
5.1 Step 0 — Assemble the force
CB-370 Up to two supporters MAY join an Assault. A supporter MUST satisfy all of:
- it belongs to the attacking player or a player allied to them;
- it is adjacent (Chebyshev 1) to the target tile;
- it has not yet been activated this turn;
- it has not supported or bombarded this turn;
- it is not embarked in a transport;
- its class
atkis greater than 0 and itsstrengthis at least 1; - its order stack does not have a
WithdrawReflex active (CM-1400).
Rationale for "not yet activated": a unit that has taken its activation has spent its turn. The consequence is that support flows from later-activating units to earlier-activating ones, so a gun that is standing still — low Initiative, late activation — is the one available to support. That is exactly backwards from what it first looks like and exactly right: the battery that displaced forward this turn cannot also fire in support of the assault, and the one that sat in position can.
CB-380 Support MUST be opt-in on the Attack order, with a supportPolicy of Auto (the default)
or an explicit list of unit ids.
CB-390 Under supportPolicy: Auto the engine MUST select supporters by repeating twice over the
eligible candidates: prefer a candidate whose role is not already present in the attacking force;
among those, prefer the highest Step-1 contribution; ties break by lowest creationSequence. The
same selection MUST be used by the preview.
Rationale: role diversity first because the combined-arms bonus (CB-560) is usually worth more than the marginal power of a second unit of the same role, and because the preview must be able to explain the choice in one clause.
CB-400 The defender MUST be the unit of the defending stack with the greatest Step-1
defensive contribution; ties break by lowest creationSequence. Every other unit of the stack MUST
contribute only through stackExtra (CB-1930) and MUST NOT contribute power of its own.
CB-410 Supporting MUST cost the supporter its support for the turn and +10 disorder, and MUST NOT cost it movement or its activation.
CB-420 Support MUST key off physical adjacency and eligibility only. Formation or Group membership, shared Posting, or shared orders MUST NOT affect whether a unit may support, how much it contributes, or whether it is selected. (Satisfies CM-2160.)
CB-430 Supporters MUST take losses (CB-900) and MUST NOT advance under any circumstance.
CB-440 A Bombardment MUST have no supporters. Concentration of artillery MUST be expressed by firing several batteries at the same target in the same turn, not by one bombardment aggregating several guns.
Rationale: several independent bombardments against a target whose disorder is rising between them is a strictly more interesting thing than one aggregated shot, because each subsequent shot faces a weaker defence and the player can stop when the resolve threshold is crossed. It also makes the artillery concentration threshold of CB-1750 emerge from arithmetic rather than from a stacking rule.
CB-450 A Reaction MUST have no supporters.
CB-460 The attacking force MUST be frozen at the instant the lead attacker's order is evaluated. A supporter destroyed later in the Cascade MUST NOT retroactively alter a resolved Clash.
5.2 Step 1 — Each unit's raw contribution
CB-470 Each unit's contribution MUST be computed as exactly one expression with exactly one division:
contribution = floor( base × strength × (1000 + gradePower + supplyPower + roleTerrain) × factor
/ 100 000 000 )
CB-480 base MUST be the class atk for every member of the attacking force and the class def
for the defender.
CB-490 factor MUST be 1000 for the lead attacker and for the defender, and 400 for a
supporter.
Rationale: 400 makes a supporter worth about four-tenths of itself, which is enough that a second role is worth bringing and not enough that massing three of the same thing beats bringing two kinds. It is the number that makes the combined-arms bonus the reason to add a supporter rather than the supporter's raw power being the reason.
CB-500 gradePower MUST come from CB-1610 and supplyPower from CB-1650.
CB-510 roleTerrain MUST be looked up by the contributing unit's role against the
Specification's terrain, per CB-1450. Both sides MUST use the same terrain: there is exactly one
battlefield and it is the defender's tile.
CB-520 A_raw MUST be the sum of the contributions of the lead attacker and all supporters.
D_raw MUST be the defender's contribution alone.
CB-530 Each contribution MUST be floored independently before summation. The engine MUST NOT accumulate un-floored terms and divide once at the end of Step 1.
Rationale: the accumulate-wide-divide-once discipline applies within a single named quantity, not across units. Flooring per unit is what makes a supporter's line item in the readout (CB-2400) equal to the number that actually entered the sum, which is what lets a player learn the rules by reading the panel.
5.3 Step 2 — Force-level modifiers
CB-540 Force-level modifiers MUST be additive in per-mille, never multiplicative.
attackerMods = clamp( Σ attacker terms, −700, +1800 )
defenderMods = clamp( Σ defender terms, −700, +1800 )
A = max(1, floor( A_raw × (1000 + attackerMods) / 1000 ))
D = max(1, floor( D_raw × (1000 + defenderMods) / 1000 ))
Rationale: multiplicative stacking is more "realistic" and produces unassailable turtles — forest × dug-in × works × stack is four to five times defence, and each further bonus is worth more the more you already have. Additive stacking makes every bonus worth the same wherever you find it, which makes the one you are missing worth pursuing, and it makes the arithmetic something a player can do in their head while looking at a map.
CB-550 The attacker's terms MUST be exactly:
| Term | Value | Condition |
|---|---|---|
| Stance | CB-1520 | The lead attacker's Stance; Assault only |
| Prepared fire | +200 | Reaction only, replacing the Stance term (CB-1790) |
| Combined arms | CB-560 | Assault only |
| River crossing | −180 | The lead attacker's tile and the target tile are separated by a river edge |
| Uphill | −100 | The target tile's elevation exceeds the lead attacker's |
| Amphibious | −250 | The lead attacker is attacking from aboard a transport |
CB-560 The combined-arms term MUST be determined by the count of distinct roles present in the attacking force:
| Distinct roles | Term |
|---|---|
| 1 | 0 |
| 2 | +120 |
| 3 | +260 |
Rationale: +120 is roughly the value of a Trained grade, so bringing a second kind of unit is worth about as much as a level of experience — significant, learnable, not decisive. The jump to +260 for three is deliberately super-linear because a full combined-arms force is the thing the design most wants players to build, and because reaching three roles costs both supporter slots and a real positioning problem.
CB-570 A river-crossing or uphill term MUST apply only to the lead attacker's approach, and MUST NOT be recomputed per supporter.
Rationale: a supporter is firing across the same battlefield from wherever it stands; making each supporter's approach a separate modifier would multiply the panel's line items without changing any decision a player makes.
CB-580 River-crossing and elevation are map properties. 01-game-rules.md MUST supply the
predicates crossesRiverEdge(from, to) and elevation(tile); where it defines neither, both terms
MUST be 0 and the readout MUST NOT display them.
CB-590 attackerEdgeMods in the Specification MUST be the pre-summed total of the river,
elevation and amphibious terms, computed by buildClashSpec.
CB-600 The defender's terms MUST be exactly:
| Term | Value | Condition |
|---|---|---|
| Terrain cover | CB-1460 | Always |
| Stance | CB-1520 | The defender's Stance |
| Position | +100 × max(digIn, works) |
CB-1550 |
| Stack support | +100 × min(3, stackExtra) |
CB-1930 |
| Caught | −200 | Disengagement failed for want of a retreat tile (CB-1880) |
CB-610 A fortification of works level W MUST NOT contribute a defence modifier of its own. It
MUST instead set the occupant's dig-in floor to W (CB-600 Position term), add +6 × W resolve
(CB-710), add min(16, 8 × W) attacker shock (CB-1000), and forbid retreat (CB-1330).
Rationale: keeping works out of the additive defence stack is what stops a fortified city defender pinning at the modifier cap, and it reframes fortification as time saved and stubbornness rather than as a fourth multiplier. A player who builds works gets the dug-in position instantly instead of waiting four turns, and gets a garrison that will not run — which is what a fortification is for.
CB-620 defenderExtraMods MUST carry only the Caught term. No other source MAY write to it
without a combatLogicVersion bump.
CB-630 The maximum reachable defender additive total MUST be +1600 (Mountain cover 600 +
Stance Entrench 300 + position 400 + stack 300) and the maximum reachable attacker total MUST be
+510 (Stance Advance 250 + combined arms 260). CI MUST assert both bounds against the shipped
tables (CB-2680).
Rationale: the clamps at ±1800 and −700 are a safety rail, not an operating range. If a new terrain type or Stance value pushes the reachable maximum into the clamp, players discover a dead zone where their next bonus is worth nothing, and that feels like being cheated. The assertion is what keeps that a build failure rather than a bug report.
CB-640 The max(1, …) floors on A and D MUST be applied. A force MUST NOT have zero power.
Rationale: nothing is ever mathematically impossible. A player told "0 per-mille" who then wins, or told "certain" who then loses, stops trusting the readout, and the readout is the whole product.
CB-650 The engine MUST export A and D as integers. The odds ratio MUST be presented by
the client from those two integers; the core MUST NOT compute or store a decimal ratio.
5.4 Step 3 — Volatility
CB-660 Volatility MUST be computed as:
volX = gradeVolatility[grade] + supplyVolatility[supplyTier]
volatility = clamp( floor((volLead + volDefender) / 2) + terrainChaos, volatilityFloor, 1500 )
where volLead uses the lead attacker's grade and supply and volDefender the defender's.
CB-670 volatilityFloor MUST be 250 by default and MUST be settable at setup to 600 by the
rules option of CB-2630.
CB-680 Supporters MUST NOT affect volatility.
Rationale: volatility is how well the two commanders in contact can predict the fight. A supporting battery two tiles of frontage away does not make the assault more or less legible to either of them, and letting it do so would give supporters a second, invisible effect the panel could not explain.
CB-690 Terrain MUST contribute both cover (CB-600) and chaos (CB-660), and these MUST be independent columns of the terrain profile.
Rationale: this is the cheapest high-value idea in the design. Terrain does not only change the odds, it changes how much you can trust them. Open ground is predictable; forest, marsh and above all city fighting are gambles. One extra column per terrain buys a whole strategic dimension and gives experience and reconnaissance a job that is not merely "bigger number".
CB-700 The reliability figure shown to the player MUST be floor(volatility / 4) in
per-mille, and MUST be displayed as a first-class number alongside the odds, never folded into them.
5.5 Step 4 — Resolve
CB-710 Resolve MUST be computed for both sides as:
Resolve = clamp( 40 + gradeResolve + stanceResolve + 6 × works + supplyResolve
+ (inOwnCity ? 10 : 0)
− disorder
− floor((100 − strength) / 2)
− (encircled ? 20 : 0)
− (shaken > 0 ? 20 : 0),
5, 95 )
CB-720 The attacker's resolve MUST be computed for the lead attacker only. Supporters MUST NOT contribute to or receive a resolve value.
CB-730 inOwnCity MUST be true only for the defender, and only when the defender's tile holds a
city owned by the defender's player.
CB-740 encircled MUST be true only for the defender, and MUST be computed by CB-1400.
CB-750 The base of 40 MUST be common to every unit in the game. Class-specific resolve MUST NOT exist; resolve differences MUST come only from grade, Stance, works, supply, city, and condition.
Rationale: making resolve a property of the situation rather than of the unit type is what makes condition legible. A player who learns that "dug in and fresh is about 60, worn and shaken is about 15" has learned it for every unit in the game at once. A per-class morale stat would make that knowledge non-transferable and would give the roster a second axis to be unbalanced on.
CB-760 The clamp bounds 5 and 95 MUST both be reachable, and the engine MUST NOT assume resolve lies strictly inside them.
CB-770 Resolve MUST be recomputed for every Clash from current state. It MUST NOT be cached across activations.
CB-780 The resolve of the two sides MUST be computed by the same function with the same terms, differing only in which side supplies each input.
CB-790 The engine MUST expose both resolve values in the combat event and in the preview.
Rationale: resolve is the number that decides whether the tile changes hands, and it is not visible anywhere on the map. If the panel does not name it, the two-track structure of CB-030 is invisible and players will read the odds ratio as the whole story — which is precisely the misconception the system exists to replace.
5.6 Step 5 — The draw
CB-800 Exactly one random number MUST be drawn per Clash:
h = coreHash(seed, turn, initiatorSequenceNumber, attackOrdinal) // 64-bit
u = h & 0xFFFFFFFF // low 32 bits, unsigned
r = floor( u × 1000 / 4294967296 ) // uniform in [0, 1000)
CB-810 The hash MUST be positionally addressed — a counter-based function of the identity of the engagement — and MUST NOT be drawn from a stream cursor, a global PRNG sequence, or any state that depends on how many other combats have already resolved.
Rationale: this is not a nicety, it is what makes the value for a given attack independent of everything else in the turn. It means a preview computed during the Orders phase and the resolution computed during the Cascade address the same cell; it means a replay that skips an unrelated combat still reproduces this one; and it means the AI can evaluate a hundred thousand candidate attacks without perturbing a single draw.
CB-820 initiatorSequenceNumber MUST be the creation sequence number of the unit that initiated
the engagement — the lead attacker for an Assault or Bombardment, the reacting unit for a Reaction.
CB-830 attackOrdinal MUST be the count of Clashes of any kind that the initiating unit has
already resolved this turn, starting at 0, and MUST be incremented after each. A Disengagement MUST
NOT increment it.
Rationale: the pair (initiator, ordinal) is unique within a turn by construction, so the four hash inputs address a unique cell with no domain-separation tag needed. Making the ordinal shared across kinds is what guarantees a unit that reacts and then attacks does not reuse a draw.
CB-840 Deriving r by multiply-shift as in CB-800 MUST be used. u mod 1000 MUST NOT be used.
Rationale: mod 1000 over a 32-bit range is biased — 1000 does not divide 2³², so 296 of the
residues are very slightly more likely. The bias is about 2.3 × 10⁻⁷ and would never be noticed, and
that is exactly why it should not be shipped: it is a free correctness win that costs one multiply,
and a system whose entire selling point is an exact readout should not contain a known-biased draw.
CB-850 u × 1000 MUST be computed in at least 42 bits of integer precision. In an
IEEE-754-double implementation this is exact, since u × 1000 < 2⁴² < 2⁵³.
CB-860 The draw MUST NOT be consumed for a Clash that is rejected under CB-340, and MUST NOT be consumed by any preview.
5.7 Step 6 — The exchange
CB-870 The exchange MUST be computed as exactly:
s = floor( (r − 500) × volatility / 2000 ) // per-mille swing, ±volatility/4
A_eff = max(1, floor( A × (1000 + s) / 1000 ))
D_eff = max(1, floor( D × (1000 − s) / 1000 ))
shareA = floor( A_eff × 1000 / (A_eff + D_eff) )
shareD = 1000 − shareA
Ld = min( defenderStrength, floor( intensityOut × shareA × 100 / (1000 × toughDefender) ) )
La = min( leadStrength, floor( intensityBack × shareD × 100 / (1000 × toughAttacker) ) )
CB-880 intensityOut and intensityBack MUST be taken from CB-200 by kind.
CB-890 The single draw MUST push the odds both ways at once — A up and D down by the same
s. Two independent draws MUST NOT be used.
Rationale: pushing both ways keeps the total heat of the battle roughly constant and moves only the
split, so a good draw is a better trade rather than a bigger battle. It is also what makes every
downstream quantity monotone in r, which is what makes the preview exact (CB-2150). The honest cost
is that outcomes within one Clash are perfectly correlated: you cannot have "unlucky casualties but
they broke anyway". Some players will find that flat. The exactness is worth more.
CB-900 For an Assault, La MUST be distributed across the attacking force: the lead attacker
loses La, and each supporter loses floor(La × 400 / 1000).
Rationale: a supporter contributes at 400 per-mille and bleeds at 400 per-mille. Anything else makes support either free (and therefore always correct) or ruinous (and therefore never taken).
CB-910 For a Bombardment La MUST be 0 by CB-200's intensityBack = 0, and no special case
MUST be written.
CB-920 Ld MUST be allocated to the defending stack by carry-over: the defender absorbs up
to its current strength; any excess passes to the next unit of the stack in descending Step-1
defensive contribution (ties by lowest creationSequence); any excess beyond the last unit is
discarded.
Rationale: this settles the source design's open question on casualty distribution. Putting all
losses on the top defender makes stacking cheap right up to the moment the whole stack dies at once,
which is a violent and unreadable cliff. Carry-over is one line of deterministic arithmetic, removes
the cliff, preserves the exactness of the preview (the preview reports Ld and the allocation is a
pure function of it), and makes a stack genuinely absorb more total strength than one unit — which is
what justifies the modest +100-per-unit defence term rather than a larger one.
CB-930 The saturating shape of A/(A+D) MUST NOT be linearised or replaced. Overwhelming odds
MUST have diminishing returns on damage and sharply increasing returns on safety.
Rationale: at 4:1 the defender loses 48 and the attacker 12; at 10:1 the defender loses 54 and the attacker 5. The payoff for overwhelming force is that your own losses collapse and the break becomes certain, not that the hammer gets bigger. That is deliberate anti-doom-stack design: massing buys safety, not throughput, so a second army somewhere else is usually worth more than a bigger one here.
CB-940 At even odds each side MUST lose 30 strength at tough = 100, so that a unit survives
approximately three even fights. intensityOut = 60 for an Assault is calibrated to this and
MUST NOT be changed without re-deriving the recovery table of §15.
Rationale: three even fights is long enough that a unit is a persistent asset with a history worth protecting, and short enough that a contested tile resolves in two or three turns rather than ten.
CB-950 Reference values at tough = 100, for CI and for the tuning record:
shareA |
Approx. odds | Ld |
La (tough 100) |
La (tough 130) |
|---|---|---|---|---|
| 500 | 1.00 : 1 | 30 | 30 | 23 |
| 600 | 1.50 : 1 | 36 | 24 | 18 |
| 700 | 2.33 : 1 | 42 | 18 | 13 |
| 800 | 4.00 : 1 | 48 | 12 | 9 |
| 909 | 10.0 : 1 | 54 | 5 | 4 |
| 950 | 19.0 : 1 | 57 | 3 | 2 |
5.8 Step 7 — The break checks
CB-960 The break checks MUST be computed as exactly:
shockDefender = Ld + flankShock + stanceShockToDefender
shockAttacker = La + stanceShockToAttacker + min(16, 8 × defenderWorks)
defenderBreaks = shockDefender ≥ resolveDefender
attackerFalters = shockAttacker ≥ resolveAttacker AND NOT defenderBreaks
CB-970 flankShock MUST be 8 × min(4, flankers) for an Assault, 12 (a fixed ambush term)
for a Reaction, and MUST NOT apply to a Bombardment.
CB-980 flankers MUST be the count of units belonging to the attacking player or a player allied
to them that are adjacent (Chebyshev 1) to the target tile at the moment of resolution, excluding the
lead attacker, with strength ≥ 1. Supporters MUST be counted.
Rationale: a unit standing next to the defended tile is exerting pressure whether or not it fired. Excluding supporters would mean adding a supporter reduces the flanking bonus, which is incoherent and would make the panel impossible to explain. Note that the illustrative summary tables in the source design proposal were generated with supporters excluded and consequently disagree with the proposal's own worked example; the worked example and the prose are correct and this requirement follows them.
CB-990 Flanking MUST contribute shock only, and MUST NOT contribute power.
Rationale: this is the single most important structural constraint in §5. Surrounding a unit does not make your shells hit harder; it makes the unit decide it cannot stay. Keeping flanking out of the power calculation is what stops "adjacency bonus" from degenerating into "bring more units", and it is what makes positioning worth more than force — the design's central claim.
CB-1000 stanceShockToDefender and stanceShockToAttacker MUST come from CB-1520. For a
Reaction both MUST be 0, replaced by the fixed ambush term of CB-970.
CB-1010 A defender break MUST suppress an attacker falter.
Rationale: you are not thrown back from a position you have just taken. Without the suppression the same Clash could both take the ground and refuse it, which has no coherent consequence.
CB-1020 Neither break check MUST apply to a Bombardment.
Rationale: guns unstick, they do not take ground. A bombardment that could rout a defender would let artillery capture tiles at zero risk from beyond retaliation range, which is the single most degenerate outcome available in this design space.
CB-1030 Break MUST be a threshold comparison, never a probability roll. All uncertainty in the
break outcome MUST enter through Ld/La and therefore through the single draw.
Rationale: a second roll would double the entropy, halve the legibility, and destroy the exactness of the readout for nothing. The break is already uncertain, because the losses that drive it are.
5.9 Step 8 — Consequences
CB-1040 The outcomes MUST be exactly these five, evaluated in this order.
| # | Condition | Consequence |
|---|---|---|
| 1 | Defender's stack reduced to 0 strength throughout | Every reduced-to-zero unit destroyed; survivors remain; attacker MAY advance if the tile is empty |
| 2 | defenderBreaks and a legal retreat destination exists |
The whole defending stack vacates to that tile with +10 disorder each and 0 movement remaining; the lead attacker MAY advance |
| 3 | defenderBreaks and the defender's retreat class forbids retreat (CB-1330) |
Not destroyed. shaken set to 2. The tile does not change hands |
| 4 | defenderBreaks and no legal retreat destination exists |
Rout: the whole defending stack is destroyed |
| 5 | attackerFalters |
Losses stand; the attacker does not advance, gains +25 disorder, and may not attack again this turn (CB-250) |
CB-1050 When none of conditions 1–5 hold, losses MUST be applied and nothing MUST move. The lead attacker MAY initiate a further Assault if it has movement remaining.
CB-1060 The lead attacker MAY advance only when the target tile is empty of enemy units after the
consequence step, its Stance is Advance or Hold, it has at least one movement point remaining,
and the tile is legal for it to occupy under stacking and passability rules.
CB-1070 Advancing MUST cost one movement point.
CB-1080 Stance Entrench and Stance March MUST NOT advance.
Rationale: an entrenched unit that leaves its position to occupy the ground it has just cleared has
thrown away the position it spent turns preparing; the player who wants the tile sets Advance. A
marching column is not fighting for ground at all.
CB-1090 Supporters MUST NOT advance (CB-430). A Reaction MUST NOT produce an advance (CB-200).
CB-1100 A retreating stack MUST arrive with its movement allowance for the turn spent, MUST NOT act again that turn, and MUST retain its Posture and order stack intact per CM-220.
CB-1110 Retreat MUST move the entire defending stack to the same destination tile. Splitting a retreating stack across destinations MUST NOT occur.
CB-1120 A retreat destination MUST have stacking room for the entire retreating stack. Where no candidate does, the retreat MUST be treated as having no legal destination and outcome 4 applies.
Rationale: this makes crowding behind a line into a real vulnerability, which is the correct consequence of the design's stacking economics. It is also the sharpest edge in the document and the first thing to soften if playtest shows rout losses above the CB-2710 band.
CB-1130 shaken = 2 MUST subtract 20 from resolve while non-zero and MUST be decremented by 1 at
the end of each turn, so a garrison that breaks in place fights the remainder of this turn and the
whole of the next at reduced resolve.
Rationale: cities fall to strength loss, not to rout. A defender that cannot run has to be killed, and the shaken penalty is what makes the second and third assault of a siege progressively cheaper without ever letting a fortification be taken by morale alone.
CB-1140 A stack destroyed by rout MUST emit one combat event listing every destroyed unit and
MUST raise a Routed Attention Event for the losing player naming the tile and the unit count.
CB-1150 A faltered attacker MUST gain +25 disorder and MUST be barred from initiating further Assaults or Bombardments that turn.
CB-1160 After a sanctioned automated capture resulting from an advance, the capture obligations of CM-730 apply. This document MUST NOT duplicate them.
CB-1170 A unit that advances MUST have its digIn reset to 0.
CB-1180 All consequences MUST be applied before the Clash's combat event is emitted, and the event MUST contain the resulting state of every unit it touched.
5.10 Step 9 — Bookkeeping
CB-1190 Disorder MUST be applied after consequences, in exactly these amounts, and MUST be
clamped to [0, 100] after summation:
| Event | Disorder change |
|---|---|
| Initiated an Assault | lead attacker +6 + floor(La / 2) |
| Was assaulted | defender +floor(Ld / 2) |
| Was bombarded | defender +3 × Ld + 5 |
| Was engaged by a Reaction | mover +floor(Ld / 2) + 4 |
| Reacted | reactor +8 |
| Broke and retreated | broken unit +10 (whole stack) |
| Broke in place (outcome 3) | defender +10 |
| Attack faltered | lead attacker +25 |
| Supported an attack | supporter +10 |
| Disengaged | disengaging unit +15 |
CB-1200 The bombardment coefficient of 3 × MUST be the mechanism by which artillery removes resolve. It MUST NOT be applied to any other kind.
Rationale: this one row is the entire combined-arms argument for a third role. A bombardment that takes 7 strength and adds 26 disorder has removed 26 resolve, and resolve is what decides whether the tile changes hands. Guns do not kill; guns unstick. That is a mechanically distinct job no amount of extra armour can do, and it is expressed entirely through the two-track structure of CB-030 rather than by giving artillery a bigger number.
CB-1210 Experience MUST be awarded as:
xpGain = 2 + floor(damageDealt / 4) + (opposingSideBroke ? 10 : 0)
for the lead attacker and for the defender, and floor(xpGain / 2) for each supporter, where
damageDealt is Ld for the attacking side and La for the defender.
CB-1220 Total xp gained by a unit MUST be capped at 25 per turn from all sources.
Rationale: the cap is what stops a unit being farmed on a helpless target. Twenty-five per turn means the fastest possible path from Green to Elite is 28 turns of continuous heavy fighting, which no unit survives — so Elite is genuinely rare and is a story about a formation rather than a grind.
CB-1230 xp MUST be capped at 999 and MUST NOT decrease.
CB-1240 No xp MUST be awarded from a Disengagement, to either side.
Rationale: nobody learns anything from a contact broken before the exchange, and awarding it would make a screening force an experience farm.
CB-1250 digIn MUST be incremented by 1, to a maximum of 4, at the end of each turn in which the
unit began and ended the turn on the same tile in Stance Entrench and did not initiate a Clash.
CB-1260 digIn MUST be reset to 0 whenever the unit changes tile for any reason, including
retreat, disengagement, transport, and ReturnToPost.
CB-1270 A unit is considered to have fought this turn if it initiated, defended, supported, reacted to, was bombarded by, or disengaged from any engagement this turn. This flag MUST be cleared at the start of each turn.
CB-1280 The bookkeeping of §5.10 MUST be applied to every participant of every Clash, including participants that were destroyed, before the destroyed units are removed, so that the combat event reports final values.
CB-1290 Every quantity in §5.10 MUST be emitted in the combat event with its source named.
CB-1300 The engine MUST NOT apply any disorder, experience, or dig-in change not enumerated in §5.10.
6. Retreat, rout, and encirclement
CB-1310 A retreat destination MUST be selected deterministically from the tiles adjacent (Chebyshev 1) to the defender's tile that satisfy all of:
- passable to every unit of the retreating stack under its movement class;
- contains no unit hostile to the retreating player;
- is not the lead attacker's tile;
- has stacking room for the entire retreating stack;
- is legal for the retreating units' domain (a land unit may not retreat into open water).
CB-1320 Among the candidates the engine MUST select the lowest score under:
score = 4000 × (count of units hostile to the retreating player adjacent to the candidate)
− terrainCover(candidate)
− (a friendly unit is present on the candidate ? 500 : 0)
− (candidate is strictly closer to the nearest friendly city than the current tile ? 2000 : 0)
with ties broken by lowest tile index (row-major), consistent with 03-architecture.md's canonical tile ordering.
Rationale for the weights: avoiding further contact dominates everything, so 4000 per adjacent enemy sits an order of magnitude above the rest. Falling back toward home (2000) outranks falling back into cover (at most 600) because a unit that retreats deeper into a pocket to sit in a forest has made its situation worse; company (500) is the smallest term because it is a preference, not a survival requirement. Every weight is separated from the next by more than the maximum of the terms below it, so the ordering is a strict lexicographic preference expressed as one integer — which is what makes it explainable in the readout as a ranked list of reasons.
CB-1330 Every unit MUST have a retreatClass:
| Class | Behaviour on break |
|---|---|
Ground |
Retreats per CB-1310; destroyed if no candidate exists (rout) |
Detached |
Disengages immediately to its nearest reachable friendly basing site; destroyed if none is reachable within its remaining endurance |
CB-1340 A unit occupying a city or a works of level ≥ 1 MUST NOT retreat regardless of its
retreatClass, and MUST take outcome 3 of CB-1040 instead.
CB-1350 The retreat destination MUST be computed against the world as it stands at the instant of resolution, using true state.
CB-1360 The preview MUST compute the retreat destination from the viewing player's knowledge view, and MUST mark it provisional whenever any input to CB-1310 or CB-1320 is unobserved.
Rationale: the exactness guarantee of §16 is a guarantee about the resolution function given its inputs, not a claim that a player can see the whole board. Showing a retreat tile computed from partial knowledge without saying so would be the one lie the readout must never tell.
CB-1370 The predicted retreat destination MUST be displayed on the map in the attack preview before the player commits.
Rationale: this is what turns "cut their line of retreat" from a thing that happens to you into a thing you plan. The mechanic of CB-050 is worthless if the player cannot see the roads they need to take away.
CB-1380 A retreating stack MUST NOT pass through or over any intervening tile. Retreat is a single step to an adjacent tile.
CB-1390 Zone-of-control rules, where 01-game-rules.md defines them, MUST be applied as an additional exclusion in CB-1310 and MUST NOT alter the scoring of CB-1320.
CB-1400 A defender MUST be considered encircled for the purposes of the resolve term in CB-710 when the candidate set of CB-1310 is empty at the moment resolve is computed.
Rationale: encirclement is not a state to track, a flag to set, or a subsystem to write. It is the name of a condition the retreat rule already computes, and reusing that computation is what makes CB-050 true rather than merely claimed. The −20 resolve means a cut-off unit breaks sooner, and breaking with nowhere to go destroys it — so the same geometry both causes and punishes the break.
CB-1410 Encirclement MUST be visible before it is fatal: the client MUST mark any unit whose candidate set is empty, for both owners, whenever the marked unit is visible to the viewing player.
CB-1420 Rout destruction MUST NOT be reduced, split, or partially applied. If the stack cannot retreat, all of it is destroyed.
Rationale: this is the mechanic the source design was least sure of, and it is also the one that makes the whole manoeuvre game worthwhile. Four units vanishing in one Clash is enormous swing, and softening it pre-emptively would remove the reason to encircle anything. CB-2710 puts a measured band on it so that a tuning failure is a failing number rather than an argument.
CB-1430 A unit destroyed by rout MUST count as a combat loss for scoring and victory purposes on the same terms as any other destruction; 14-victory.md owns the terms.
7. Terrain
CB-1440 Every terrain type defined by 01-game-rules.md MUST carry a combat profile consisting
of: cover (per-mille), chaos (per-mille), and one roleTerrain per-mille value for each of the
six roles.
CB-1450 The roleTerrain table MUST be:
| Terrain family | Foot | Armor | Gun | Recon | Naval | Air |
|---|---|---|---|---|---|---|
| Open | 0 | +150 | 0 | +100 | — | 0 |
| Hills | +100 | −150 | +100 | 0 | — | 0 |
| Forest | +150 | −300 | −150 | −100 | — | −100 |
| Marsh | 0 | −400 | −250 | −150 | — | 0 |
| Mountain | +200 | −500 | −200 | −150 | — | −150 |
| Urban | +200 | −250 | −100 | −200 | — | −150 |
| Water | — | — | — | — | 0 | 0 |
Rationale: this one table is the whole combined-arms argument. Armour is 150 per-mille better than foot in the open and 700 worse in mountains. There is no terrain where a single role is correct, so a force that is all one thing has a map full of tiles it cannot profitably touch — and that is a structural incentive to mix rather than a bonus bolted on to reward mixing.
CB-1460 The cover and chaos table MUST be:
| Terrain family | Cover (defender force mod) | Chaos (volatility) |
|---|---|---|
| Open | 0 | −150 |
| Marsh | +250 | +250 |
| Hills | +300 | +50 |
| Forest | +400 | +200 |
| Urban | +500 | +350 |
| Mountain | +600 | +150 |
| Water | 0 | 0 |
Rationale: marsh is the deliberate oddity — only moderate cover, but the highest chaos short of a city. It is a bad place to be and a worse place to plan, which gives the map a terrain type whose danger is informational rather than numerical. Urban has both the second-highest cover and the highest chaos because city fighting should be the thing a careful player is most reluctant to gamble on, and that is a different feeling from "hard".
CB-1470 01-game-rules.md MUST assign every terrain type it defines to one of these families, or supply an explicit profile of its own. A terrain type with neither MUST use the Open profile, and CI MUST fail the content build with a named error rather than defaulting silently.
CB-1480 A terrain profile MUST NOT vary by attacker, by turn, by weather, or by any dynamic state. Where a scenario needs seasonal or weather effects, they MUST be expressed as a separate additive term in CB-550/CB-600 with its own requirement, not by mutating the profile.
Rationale: a static profile is what makes the terrain overlay of CB-2470 honest and what lets a player learn the map once. A mutating profile turns every learned fact into a maybe.
CB-1490 The engine MUST export defensiveValue(unitClass, terrain) = cover(terrain) + roleTerrain(role(unitClass), terrain) as an integer per-mille, higher being better. (Satisfies
CM-1410.)
CB-1500 The terrain overlay in the client MUST be computed from the same profile data the core resolves with. A separate presentation table MUST NOT exist.
8. Stance, dig-in, and works
CB-1510 Combat MUST read the unit's Stance as defined by 10-turn-model.md, with values
March, Advance, Hold, Entrench. Combat MUST NOT define its own tactical posture vocabulary
and MUST NOT modify a unit's Stance.
CB-1520 The Stance combat table MUST be:
| Stance | Attack force mod | Defence force mod | Resolve | Shock to defender | Shock to attacker | May advance |
|---|---|---|---|---|---|---|
Advance |
+250 | −200 | −10 | +6 | 0 | yes |
Hold |
0 | 0 | 0 | 0 | 0 | yes |
Entrench |
−400 | +300 | +15 | 0 | +10 | no |
March |
−250 | −300 | −5 | 0 | 0 | no |
Rationale for the March row, which the source design did not have: the turn model grants Initiative
and forfeits reaction capacity for March, and something has to make that a trade rather than a free
win. A column caught on the road is bad at fighting and much worse at being fought, which is what
makes reactive fire (§11) and screening forces mechanically real. The resolve penalty is only −5
because a unit that is merely moving has not lost its nerve; the punishment is positional, not moral.
CB-1530 A unit in Stance March MUST NOT initiate an Assault or a Bombardment. It MAY be the
defender of one, and MAY be the mover in a Reaction.
CB-1540 The Advance resolve penalty of −10 MUST be retained in combination with the strength
penalty of CB-120.
Rationale: together they produce a specific and intended behaviour — a unit below about 60 strength falters out of any marginal attack, while a good attack still goes in. The rule is therefore "worn units cannot make bad attacks", not "worn units cannot attack", and it is intentional culmination rather than an accident of two penalties meeting. It will nonetheless be felt as "my units stopped working" before it is understood as "rotate your line", so CB-2400 requires the falter risk to be named explicitly in the preview.
CB-1550 The defender's position term MUST be +100 × max(digIn, works), so that works sets a
floor on dig-in rather than adding to it.
CB-1560 digIn MUST accrue by CB-1250 and reset by CB-1260, with a maximum of 4 (+400).
CB-1570 works MUST be an integer 0–4 supplied per tile by 01-game-rules.md. A city MUST supply
its own works level.
CB-1580 Stance MUST be derivable from Posture by the rules of 13-command.md §4 and MUST be overridable per turn by the player (CM-560). Combat MUST accept whatever value it is given.
CB-1590 The preview MUST name the Stance of both sides and the numeric contribution of each.
9. Grade and supply
CB-1600 Grade MUST be derived from xp by these thresholds and MUST NOT be stored separately:
| Grade | xp threshold |
|---|---|
| Green | 0 |
| Trained | 100 |
| Veteran | 300 |
| Elite | 700 |
CB-1610 The grade table MUST be:
| Grade | Power | Resolve | Volatility |
|---|---|---|---|
| Green | +0 | +0 | 1000 |
| Trained | +80 | +5 | 850 |
| Veteran | +180 | +12 | 700 |
| Elite | +300 | +20 | 550 |
Rationale: Elite is +30% power, not +200%. Veterans should be reliably better, not individually decisive — an Elite unit that beat four Green ones would make combat a race to a few super-units and would make every loss catastrophic. The volatility drop from 1000 to 550 is arguably the larger effect and is certainly the one that matters to a planner: an Elite formation's outcomes cluster, so you can build an operation around it.
CB-1620 Combat MUST read a supply tier with four values: Cut, Strained, Adequate,
Full.
CB-1630 The supply tier MUST be derived from the supply state owned by 12-economy.md by exactly:
| Economy supply state | Additional condition | Combat supply tier |
|---|---|---|
| Isolated | — | Cut |
| Extended | — | Strained |
| Supplied | unit is not on a supply source tile | Adequate |
| Supplied | unit occupies a supply source tile (a supplying city or an emplaced depot) | Full |
Rationale: the economy owns the geography of supply and this document owns what supply does in a fight, so the mapping belongs here and the inputs belong there. The fourth tier is derived rather than requested because "standing on the depot" is a fact the economy already computes for its own purposes, and giving a unit a combat edge for fighting on top of its own supply head is both true to life and a real reason to place depots where the fighting will be.
CB-1640 12-economy.md MUST NOT define combat power multipliers for supply. The values below are the normative ones.
CB-1650 The supply table MUST be:
| Supply tier | Power | Resolve | Volatility |
|---|---|---|---|
Cut |
−400 | −15 | +300 |
Strained |
−150 | −5 | +100 |
Adequate |
0 | 0 | 0 |
Full |
+50 | 0 | −100 |
Rationale: −150 for Strained is within three per-mille of the ×7/8 the economy proposal reached
independently, so the two designs agree about extension. Cut is deliberately harsher than the
economy's ×3/4, at −400 plus −15 resolve plus +300 volatility, because cutting a road is meant to be
the strongest thing in the game, and the supply table of §23 shows it working: against a defender that no
assault can shift, severing supply produces certain break. That is the operational game this system
exists to create, and it should not be a marginal effect.
CB-1660 Supply MUST affect only power, resolve and volatility. It MUST NOT affect losses, break thresholds, or retreat directly.
CB-1670 The supply tier of both sides MUST be shown in the preview.
10. Bombardment
CB-1680 A Bombardment MUST be legal only when: the firing unit's class has bombardRange ≥ 1;
the target tile is within bombardRange (Chebyshev) of the firing unit; the firing unit has not
moved this turn; the firing unit has fired fewer than bombardShots times this turn; and the firing
unit has not supported this turn.
CB-1690 A Bombardment MUST forfeit all of the firing unit's remaining movement for the turn.
Rationale: guns either displace or fire. This is what makes the position of a battery a decision a turn in advance rather than a thing that resolves itself, and it is what stops artillery from being able to both keep pace with an advance and support it.
CB-1700 A Bombardment MUST use intensityOut = 22 and intensityBack = 0, and MUST perform no
break check for either side (CB-1020).
CB-1710 A Bombardment MUST NOT reduce a unit's strength below 1.
Rationale: artillery softens; it never takes ground and it never takes the last point. Without this floor a gun line kills at zero risk from beyond retaliation range, which is both degenerate and thematically wrong. It also means the last strength point of every defender must be paid for by somebody who walked into contact.
CB-1720 A Bombardment MUST apply +3 × Ld + 5 disorder to the target (CB-1190) and MUST NOT
apply disorder to the firing unit.
CB-1730 A Bombardment MUST target the defending stack's top defender under CB-400, and its Ld
MUST be allocated by carry-over under CB-920 subject to the floor of CB-1710 applying to each unit
individually.
CB-1740 Bombardment against a city or works MUST use the same procedure. Where
02-units-and-industry.md gives a class a siege property, its effect MUST be expressed as an
additional attackerEdgeMods term declared in that document and MUST NOT alter this procedure.
CB-1750 The equilibrium between bombardment and recovery MUST be treated as a designed quantity.
One bombardment against a defender in a city produces roughly +23 disorder, against that city's
−45 disorder recovery (CB-2070): two shots per turn roughly break even, and three or more make
progress. 02-units-and-industry.md MUST set bombardShots against this threshold — a class whose
bombardShots alone exceeds two can reduce a city single-handed, and a class with bombardShots 1
requires a gun line of three.
Rationale: artillery only works concentrated, and this is the arithmetic that makes it so. A gun
line is a thing you build, move and protect as a unit rather than a per-unit habit, and the threshold
falls directly out of the recovery table without any rule that mentions concentration. The reason the
threshold is stated in shots rather than in batteries is that bombardShots is the roster's dial:
whether concentration is required is decided there, and this requirement is what makes that decision
visible to whoever turns the dial.
11. Reactions
CB-1760 10-turn-model.md owns when a reaction occurs and who gets it: reaction capacity, eligibility, and the interruption of the mover. This document owns only what the reaction does.
CB-1770 A Reaction MUST resolve as a Clash with the reacting unit as the attacking side and the
moving unit as the defender, on the mover's halt tile, with intensityOut = 34 and
intensityBack = 14.
Rationale for the asymmetry: a reaction is a firefight begun by a unit that is in position against one that is deployed for movement. The reactor deals a real blow and takes a glancing one. Symmetric intensity would make overwatch a losing trade against any tougher unit — a dug-in rifle company would deal 13 and take 16 to an armoured battalion — and a mechanic that costs you more than it costs your enemy will simply never be used, which would waste the reaction rule the turn model paid for.
CB-1780 The mover MUST halt regardless of the Clash outcome; the halt is the turn model's, not this document's. The break check determines only whether the mover is additionally thrown back.
CB-1790 In a Reaction the reactor's Stance attack modifier MUST NOT apply. A flat +200 prepared fire modifier MUST apply in its place, and the combined-arms term MUST NOT apply.
Rationale: Entrench gives −400 attack, and a reaction is precisely the reward for having
entrenched. Applying the assault modifier to prepared fire would make the best-positioned unit the
worst reactor, which inverts the mechanic.
CB-1800 In a Reaction the mover's own Stance defence modifier MUST apply, so that March (−300)
is punished and Advance (−200) is punished less.
CB-1810 In a Reaction flankShock MUST be the fixed ambush term of 12 and the works and Stance
shock terms MUST be 0 (CB-1000).
CB-1820 A mover that breaks MUST retreat from its halt tile under §6, and MUST be destroyed if no candidate exists.
Rationale: overwatch on a chokepoint is lethal, and it is lethal by the same rule that makes encirclement lethal. No new rule is needed for "ambushed in a defile".
CB-1830 A reactor that falters MUST gain +25 disorder and MUST forfeit its remaining reaction capacity for the remainder of the Cascade. It MUST NOT move or retreat.
CB-1840 A Reaction MUST NOT be predictable in the attack preview, because reaction capacity is not observable. The preview MUST instead state, as a warning without a probability, when the target tile or the planned route is adjacent to a visible enemy that could hold reaction capacity.
Rationale: an honest "you may be intercepted here" is worth more than a fabricated percentage. This is the one place the readout deliberately declines to give a number, and saying why is part of the contract.
12. Disengagement
CB-1850 When an Assault would resolve against a unit whose order stack has an active Withdraw
Reflex (CM-1400), and that unit has not already disengaged this turn, the engine MUST resolve a
Disengagement instead of a Clash.
CB-1860 A Disengagement MUST consume no draw, MUST perform no break check, and MUST resolve as:
- select a retreat destination by §6;
- if one exists, the disengaging unit loses
floor(1000 / tough)strength, gains +15 disorder, vacates the tile, and its movement for the turn is spent; - the attacker takes no losses and MAY advance under CB-1060;
- if no destination exists, the unit is Caught and a normal Assault resolves with a −200 defender force modifier (CB-600).
CB-1870 Parting losses at the calibration tough values MUST therefore be: 16 for tough 60, 14
for 70, 10 for 100, 7 for 130.
CB-1880 The Caught modifier of −200 MUST apply only to the Clash that follows a failed disengagement, and MUST NOT persist.
CB-1890 A unit MUST disengage at most once per turn. A second attack on a unit that has already disengaged this turn MUST resolve as a normal Clash.
Rationale, and this settles the source design's open question on infinite screening: a screening unit trades strength and disorder for tempo, and both meters run against it. Disengaging counts as having fought (CB-1270), so the unit recovers no strength at end of turn and only half its disorder — +15 gained against −10 recovered is a net +5 disorder per turn, while strength falls by the parting loss with no recovery at all. A scout troop screening every turn is dead in about six turns. The once-per-turn cap closes the other half of the hole, which is one unit absorbing an unbounded number of attacks in a single turn by walking backwards.
CB-1900 A disengaging unit MUST NOT gain or grant experience (CB-1240).
CB-1910 The attacker's preview MUST show, when the target is under a Withdraw Reflex and that
fact is observable, that the attack will yield the tile without an exchange, together with the
parting loss the defender will take.
CB-1920 A Detached unit MUST NOT disengage under this section; CB-1330 governs it.
13. Stacks
CB-1930 stackExtra MUST be the count of units on the defending tile other than the defender,
with strength ≥ 1, and MUST contribute +100 × min(3, stackExtra) to the defender's force
modifier.
CB-1940 01-game-rules.md owns stacking capacity. The cap of 3 in CB-1930 MUST be independent of that capacity, so that raising the stacking limit does not raise the defensive ceiling.
Rationale: the reachable-maximum assertion of CB-630 must survive a stacking-rule change made by another document. Decoupling the two is what makes that possible without a cross-document renegotiation.
CB-1950 Only the defender MUST contribute defensive power (CB-400). Other stack members MUST NOT contribute power, resolve, volatility, or break terms.
CB-1960 Losses MUST be allocated to the stack by carry-over (CB-920).
CB-1970 A break MUST move or destroy the whole stack (CB-1110, CB-1420).
CB-1980 The attacking side MUST NOT stack: an Assault is one lead attacker plus at most two supporters, each on its own tile.
14. Cities, works, and sieges
CB-1990 A defender in a city or works MUST NOT retreat (CB-1340) and MUST take the shaken consequence of CB-1130 instead.
CB-2000 A city MUST therefore fall to strength loss, never to rout.
Rationale: cities are sieges, not skirmishes. They are where the game's big decisions land and they are the one place where fight-to-the-death is the rule — which is only tolerable because CB-040 makes it the exception everywhere else.
CB-2010 The +10 own-city resolve term (CB-710) and the +6 × works term MUST both apply, and
MUST stack additively with the Stance and grade terms.
CB-2020 A player MUST be able to order a garrison to abandon its place during the Orders phase. An abandoning unit MUST move out under normal movement at its activation, and the place MUST be left undefended.
Rationale, and this settles the source design's open question on city retreat: the pathology it worried about is a player feeding units into a hole they cannot leave, and the fix is to let them leave — deliberately, in advance, as a decision. Making it an automatic break-triggered behaviour instead would take the decision away and would let a fortification be lost to a morale check, which CB-2000 forbids. As an order it is legible, it is the player's, and it costs them the city they chose to give up.
CB-2030 Abandonment MUST NOT be reachable through any Sanction and MUST require an explicit player order, consistent with CM-410.
CB-2040 The following siege sequence MUST be reproduced exactly by any conforming implementation
and MUST be a golden-master test vector. Defender: Rifle Company (atk 30, def 44, tough 100,
role Foot), Trained, Adequate, Stance Entrench, in its own works-3 city on Urban terrain, alone.
Attacker for the assaults: Armoured Battalion (atk 62, def 38, tough 130, role Armor), Veteran,
Full, Stance Advance, strength 100, three flankers. Bombardment by Field Battery (atk 48,
tough 70, role Gun), Trained, Adequate, strength 100, at median draw.
| Defender state | A | D | Resolve | They lose (min/med/max) | Break |
|---|---|---|---|---|---|
| str 100, dis 0 | 75 | 117 | 88 | 16 / 23 / 31 | 0‰ |
| str 81, dis 69 | 75 | 94 | 10 | 18 / 26 / 34 | 1000‰ |
| str 59, dis 55 | 75 | 69 | 13 | 22 / 31 / 39 | 1000‰ |
| str 28, dis 80, shaken | 75 | 31 | 5 | 28 / 28 / 28 | 1000‰, destroyed |
Three sequential battery shots from strength 100 at the median draw: A 47 against D 117, 109 and
102, removing 6 strength and adding 23 disorder each; end of turn strength 82, disorder 69, city
recovery −45 leaves disorder 24.
CB-2050 The intended shape is two turns of a three-battery gun line plus two armoured assaults to take one entrenched company out of a level-3 fortification. CB-2710 measures deviation from it.
CB-2060 Combat MUST NOT transfer city ownership. It vacates or destroys the defenders; the advance of CB-1060 into an empty city is what 01-game-rules.md acts on.
15. Recovery
CB-2070 At the end of every turn, after all combat and before production, each unit MUST recover according to the base row that matches its situation:
| Situation | Disorder | Strength |
|---|---|---|
| In the field | −20 | +4 |
Stance Entrench, in the field |
−35 | +4 |
| In a friendly city | −45 | +12 |
| In a friendly city with a depot | −45 | +20 |
CB-2080 The base row MUST then be modified, in this order:
- if supply tier is
Strained, halve both values by floor division and set strength recovery to 0; - if supply tier is
Cut, set both to 0; - if the unit fought this turn (CB-1270), set strength recovery to 0 and halve the disorder recovery by floor division.
CB-2090 The order of operations in CB-2080 MUST be exactly as given. Halving twice MUST produce
floor(floor(x/2)/2).
CB-2100 Strength MUST be capped at 100 and disorder floored at 0 after recovery.
CB-2110 The recovery asymmetry MUST be preserved: a unit at 40 strength is 5 turns from full in a city, 3 in a city with a depot, and 15 in the field.
Rationale: rotation is a real decision with a real cost, and this is where it lives. Fifteen turns in the field is long enough that leaving a worn unit forward is a genuine sacrifice; five in a city is short enough that pulling it back is a plan rather than a write-off. It also gives cities a purpose beyond production — they are where armies are repaired — which is a second reason to want them and a second thing to lose when one falls.
CB-2120 Recovery MUST NOT be applied to a unit destroyed during the turn.
CB-2130 Attrition damage from being out of supply is owned by 12-economy.md and MUST be applied
after this section's recovery, so that a Cut unit neither recovers nor is double-counted.
16. The prediction contract
This section is the reason the rest of the document is shaped the way it is. Under Orders and the Cascade a player commits before seeing, so a prediction that can disagree with the resolution is worse than no prediction at all.
CB-2140 The engine MUST expose exactly one resolution function
resolveExchange(spec: ClashSpec, r: int) → Outcome
and the prediction function MUST be implemented by calling it. A second implementation of the arithmetic, in any form — an estimator, an approximation, a client-side copy, an AI-specific evaluator — MUST NOT exist.
Rationale: this is the requirement the whole subsystem is built to make satisfiable. Single-exchange resolution (CB-020), a self-contained Specification (CB-280) and monotonicity (CB-2150) exist so that an exact preview is a handful of calls to the real function rather than a model of it. The class of bug where the AI plays a different game from the player, or where the preview is right in the common case and wrong at the edges, is not mitigated here — it is made unrepresentable.
CB-2150 Every downstream quantity MUST be monotone in r, specifically: s, A_eff,
shareA, Ld and shockDefender are non-decreasing; D_eff, shareD, La and shockAttacker
are non-increasing.
Rationale, stated as a proof obligation rather than an observation: s is non-decreasing in r by
construction; A_eff is non-decreasing and D_eff non-increasing in s; shareA = floor(1000 · A_eff / (A_eff + D_eff)) is increasing in A_eff and decreasing in D_eff, so it is non-decreasing;
Ld and La are monotone transforms of shareA and shareD; min with a constant preserves
monotonicity, as does the max(1, …) floor. The property therefore holds for every intensity pair
and every clamp setting, not merely for the shipped constants — but CB-2670 requires the sweep
anyway, because a future non-monotone modifier would silently make every preview a lie.
CB-2160 The loss bands MUST be computed by evaluating resolveExchange at r = 0, r = 500
and r = 999, and taking minimum, median and maximum by value rather than by position.
CB-2170 The break threshold rDef MUST be the least r ∈ [0, 1000) for which
defenderBreaks holds, or 1000 if none does, found by binary search over the closed range.
P(defender breaks) MUST be reported as 1000 − rDef per-mille.
CB-2180 The falter threshold rAtt MUST be the least r ∈ [0, 1000) for which
shockAttacker < resolveAttacker, or 1000 if none does. P(attacker falters) MUST be reported as
min(rAtt, rDef) per-mille.
Rationale: the attacker breaks for r < rAtt because its shock is non-increasing, and CB-1010
suppresses the falter whenever the defender breaks, which happens for r ≥ rDef. The intersection is
exactly r < min(rAtt, rDef), so the answer is one subtraction and no further search.
CB-2190 The survival threshold rSurv MUST be the least r for which La < leadStrength.
P(attacker destroyed) MUST be rSurv per-mille.
CB-2200 winPermille MUST be defined as 1000 − max(rDef, rSurv), floored at 0: the count of
draws for which the defender breaks and the lead attacker survives.
CB-2210 capturePermille MUST be winPermille when CB-1060's positional preconditions hold for
the attacker, and 0 otherwise. (Satisfies the capture-probability requirement of CM-1370.)
CB-2220 The binary search MUST use exactly the bracket [0, 1000) and MUST terminate in at most
10 probes.
CB-2230 A full preview MUST cost at most 33 evaluations of resolveExchange: 3 for the
bands, 10 each for rDef, rAtt and rSurv.
CB-2240 Every reported probability MUST be an exact count of favourable draws out of 1000, expressed in per-mille. Monte Carlo sampling, expected-value approximation, and curve fitting MUST NOT appear anywhere in prediction.
Rationale: "roughly 60%" is the specific lie that makes players distrust odds displays. 597‰, and it is right, is a different kind of object.
CB-2250 The command layer's win-probability predictor (CM-150) MUST be winPermille from this
function, evaluated over the knowledge view.
CB-2260 The predictor MUST set estimated = true whenever the knowledge view lacks a current
observation of the target's strength, and in that case MUST return 400‰, per CM-160.
Rationale: this document owes the command layer a conservative single number under uncertainty, and owes the player a band and a pivot for the same situation (§17). Those are two projections of the same computation, not two predictors, and the difference is what the caller can act on: automation needs a threshold test, a person needs to know which unknown is doing the damage.
CB-2270 The actual outcome of a resolution MUST always lie within the displayed loss bands, by construction. A violation MUST be a build-failing CI assertion, not a playtest observation (CB-2660).
CB-2280 The preview MUST be a pure function of the Clash Specification. It MUST NOT consume the PRNG, mutate state, emit events, or depend on how many previews have already been computed.
CB-2290 The AI MUST use this preview function and MUST NOT be given a cheaper approximation.
Rationale: at the cost bounds of §20 there is no efficiency argument for a separate estimator, so the only thing a separate estimator could buy is a divergence between what the AI believes and what the game does. That divergence is a whole class of bug and it is being designed out.
17. Prediction under fog
CB-2300 Combat prediction MUST be computed over the viewing player's knowledge view and MUST NOT read true state for anything the viewing player has not observed, consistent with CM-130.
CB-2310 01-game-rules.md MUST supply, per observed enemy unit, an observation level with exactly three values, and this document requires the following fields at each:
| Level | Name | Fields available |
|---|---|---|
| L1 | Sighted |
Owner, unit class, tile, terrain, stack count |
| L2 | Adjacent |
L1 plus Stance, works level, supply tier, and strength to within ±15 |
| L3 | Identified |
L2 plus exact strength, disorder, grade, digIn, shaken |
CB-2320 A Recon-role unit adjacent to an enemy MUST grant Identified on that enemy. Any unit
that fought a given enemy during the previous Cascade MUST hold Identified on it until the end of
the current turn.
Rationale: reconnaissance's job in this system is not to add a bonus, it is to collapse a band into a number. The table in CB-2360 shows a prediction that spans the entire 0–1000‰ range at L1 and resolves to a single value at L3, so scouting is the difference between knowing and not knowing rather than a few percentage points of edge.
CB-2330 When an input is unobserved, the preview MUST evaluate over the full domain of that input
and report the resulting range of each output, together with a provisional flag naming every
unobserved field.
CB-2340 The preview MUST perform pivot analysis over each unobserved field with an enumerable
domain (grade 4 values, stance 4, supplyTier 4, digIn 5, works 5). For each such field it
MUST determine whether the break verdict is constant across the domain; where it is not, it MUST
report the threshold value at which the verdict changes.
CB-2350 The preview MUST display at most two pivots, ordered by the span of breakPermille
each induces, largest first.
Rationale: a readout that says "0 to 1000 per-mille" is useless. A readout that says "they break unless they are Veteran or better — grade unobserved, a Recon unit adjacent would tell you" names the risk, names the threshold, and names the fix, and it is a sentence a player can act on. It costs four more preview calls over the unobserved dimension, which at the bounds of §20 is free.
CB-2360 The following pivot table MUST be reproduced exactly and MUST be a golden-master test
vector. Attack: Armoured Battalion (Veteran, Full, strength 92, Stance Advance) with a Field
Battery supporting (Trained, Full, strength 100), against a Rifle Company at strength 78, disorder
15, Stance Entrench, digIn 2, Adequate, in Forest, one flanker. A = 97 throughout.
| Defender grade | D | Resolve | They break | They lose |
|---|---|---|---|---|
| Green | 74 | 29 | 1000‰ | 26 / 34 / 41 |
| Trained | 79 | 34 | 1000‰ | 25 / 33 / 39 |
| Veteran | 85 | 41 | 885‰ | 25 / 31 / 38 |
| Elite | 93 | 49 | 125‰ | 24 / 30 / 36 |
CB-2370 The preview MUST make explicit that the attacker's own losses are well-known even
when the outcome is not, because La depends on the defender's power and not on its resolve.
Rationale: the cost of attacking is roughly knowable from a distance while the result is not, and that is exactly the right asymmetry for a game about committing forces. The panel should say so rather than leaving the player to infer it from two bands of different widths.
CB-2380 A preview MUST NOT be cached across turns and MUST be recomputed whenever the knowledge view changes.
18. The readout
CB-2390 The attack preview MUST present its content in this order, and 04-ui-ux.md MUST implement that ordering:
- The verdict, in plain language, first — "They break — certain."
- Both loss bands as min ── median ── max, with the resulting strength values.
- Odds and reliability as two separate figures.
- Every contributing line item, with its source named.
- The consequence: where they will retreat to, whether they are cut off, whether you take the tile.
- The alternative: a one-click preview of the same attack without each supporter.
CB-2400 A single expected value MUST NOT be shown in place of a band, and the falter probability MUST always be shown even when it is 0‰.
Rationale: a single expected value is the specific lie that makes players distrust odds displays, and a falter risk that appears only when non-zero is a risk players never learn exists until it happens to them. CB-1540's culmination behaviour is only fair if the panel names it every time.
CB-2410 Because support and flanking are automatic, the panel MUST enumerate every unit it counted and every modifier it applied.
Rationale: automatic contributions that are not itemised are indistinguishable from magic. The panel is where a player learns the rules, and a rule that fires without appearing on it will be learned as superstition or not at all.
CB-2420 The panel MUST name the retreat destination and MUST state explicitly when the defender has none.
CB-2430 A hostile-tile overlay MUST be available, colouring each enemy-occupied tile adjacent
to the player's units by the breakPermille of the best available attack, computed with the same
function.
CB-2440 After every resolution the client MUST show a reconciliation line stating the prediction and the actual result — "predicted 26–41, actual 35".
Rationale: this builds trust in the readout faster than any tutorial, because it is evidence rather than a claim, and it makes CB-2270's invariant visible to the person it protects.
CB-2450 The core MUST emit a combat event for every resolution containing: the Clash
Specification, r, every intermediate quantity of §5, the outcome, and the resulting state of every
participant.
CB-2460 The combat event MUST be sufficient to reconstruct the resolution without access to the world state at the time of resolution.
CB-2470 The client MUST NOT compute any combat number itself. Every figure it displays MUST come from a core export.
CB-2480 Every per-mille figure shown to a player MUST be shown in per-mille or as a rounded percentage derived from it, and MUST NOT be re-derived from a percentage.
19. Determinism and arithmetic
CB-2490 Combat MUST be the only subsystem that consumes randomness, consistent with CM-110.
CB-2500 Two conforming implementations given the same Clash Specification and the same r MUST
produce byte-identical outcomes on every platform.
CB-2510 Every intermediate product MUST fit within a signed 64-bit integer, and every intermediate in §5 MUST be below 2⁴⁷ in magnitude, so that an IEEE-754-double implementation is exact.
Rationale: the largest intermediate is a Step-1 contribution numerator at 255 × 100 × 1550 × 1000 ≈ 3.95 × 10¹⁰, about 2³⁵·². The 2⁴⁷ bound leaves eleven bits of headroom for a roster that raises the
stat caps, and it is the assertion that makes "a TypeScript number is fine here" a checked claim
rather than a hope.
CB-2520 Iteration over stacks, adjacency, supporters and flankers MUST be in a canonical order:
tiles by lowest tile index, units by lowest creationSequence, consistent with CM-170.
CB-2530 A combatLogicVersion MUST be recorded in the game record. Any change to a constant, a
table, a formula, or an ordering in this document MUST bump it.
CB-2540 Combat MUST NOT read wall-clock time, entity allocation order, hash-map iteration order, or object identity.
CB-2550 A Clash MUST NOT depend on the number of Clashes previously resolved this turn other than
through attackOrdinal (CB-830) and through the state changes of CB-270.
CB-2560 The seeded hash function MUST be the one 03-architecture.md defines, used in its counter-based form.
20. Performance
CB-2570 One resolution MUST be O(1) in the number of units in the game and MUST perform no allocation on the hot path.
CB-2580 A full exact preview MUST cost at most 33 resolutions (CB-2230); a preview with pivot analysis over one unobserved enumerable field MUST cost at most 33 × 5 = 165.
CB-2590 The engine MUST sustain at least 500 resolutions per Cascade and 100,000 previews per second on the reference hardware of 03-architecture.md, with no Monte Carlo anywhere and no loop whose length depends on the units involved.
Rationale: the second figure is what makes CB-2290 affordable. An AI that can evaluate a hundred thousand candidate attacks in well under a second can use the player's exact preview rather than an approximation, and the hostile-tile overlay of CB-2430 can be repainted every frame.
CB-2600 Worst-case Cascade combat time MUST be flat with respect to force composition, terrain, and odds.
CB-2610 Preview computation MUST be safe to run concurrently and off the simulation thread, being a pure function (CB-2280).
21. Rules options
CB-2620 Combat rules options MUST be fixed at setup, MUST be part of the game record, and MUST
contribute to combatLogicVersion resolution.
CB-2630 The option volatilityFloor MUST offer exactly two values: Measured (250, the
default) and Chaotic (600).
Rationale, and this addresses the source design's open question on whether one draw is enough drama: the design's position is that the surprise should come from not knowing what you are attacking rather than from the resolution, and §17 is the argument for it. But it is a taste question that only playing answers, and raising the floor is a one-constant experiment that changes nothing structural — the preview stays exact, the bands simply get wider. Shipping both and measuring which is preferred is cheaper than arguing about it.
CB-2640 No rules option MAY alter the shape of the procedure, only the value of a named constant.
CB-2650 The client MUST display the active options in the attack preview header whenever any is not at its default.
22. CI invariants and playtest metrics
CB-2660 CI MUST assert, over the golden-master corpus, that every resolved outcome lies within the loss band the preview displayed for it. A violation MUST be a build failure.
CB-2670 CI MUST re-run the monotonicity sweep of CB-2150 whenever any constant in this document changes. The reference sweep covers every terrain, grade, supply tier, Stance, three strength levels, two disorder levels, two dig-in levels and all pairings of the calibration cast, over all 1000 draws: 86,016,000 evaluations, zero violations at the constants specified here.
CB-2680 CI MUST assert that the maximum reachable additive defender total is at most +1600 and the maximum reachable attacker total at most +510, against the shipped tables (CB-630).
CB-2690 CI MUST verify the binary-search prediction against a brute-force oracle over all 1000 draws for a spanning corpus of Specifications. The reference corpus is 5,376 cases: zero mismatches.
CB-2700 CI MUST assert that no combat code path calls a floating-point operation, consistent with the determinism lint rules of 03-architecture.md.
CB-2710 The automated playtest harness MUST measure the following, with the stated targets, so that a balance regression is a failing number rather than an opinion.
| Risk | Metric | Target | Response if it fails |
|---|---|---|---|
| Encirclement swing dominates | Share of total unit losses from rout | 20–35% | Reduce CB-1420 to the top two units of the stack |
| Fronts freeze under entrenchment | Share of turns on a contested front with no tile changing hands | < 40% | Reduce the Entrench defence term or raise the bombardment coefficient |
| Artillery dominance | Ratio of Bombardments to Assaults | ≤ 2:1 | Lower intensityOut for Bombardment or raise the recovery floor |
| Artillery uselessness | Same ratio | ≥ 0.5:1 | Raise the bombardment disorder coefficient from 3× |
| Disorder death spiral | Share of unit-turns above 70 disorder | < 10% | Raise field disorder recovery from −20 |
| Combat too grindy | Mean turns to resolve a contested tile | 2–4 | Raise intensityOut for Assault from 60 |
| A role is never worth building | Build share by role | No role below 15% | Retune CB-1450 |
| Fog band unusable | Share of previews where pivot analysis names a single dominant unknown | > 70% | Show two pivots (already permitted by CB-2350) |
| Screening is free | Mean turns a screening unit survives continuous disengagement | 5–8 | Raise the disengagement disorder from +15 |
CB-2720 Preview trust MUST NOT appear in the playtest table as a tuning metric. It is an invariant asserted by CB-2660.
CB-2730 The reference worked example of §23 MUST be a golden-master test vector.
CB-2740 Every table in this document MUST be generated from the same constant definitions the engine compiles, and CI MUST assert that the document's tables match the shipped constants.
Rationale: hand-edited numbers in a specification drift from the code within one release, and this document's entire value is that its numbers are the real ones.
23. Worked example, normative as a test vector
The situation. Belvoir Wood, a Forest tile. The 3rd Rifles hold it: Green, strength 78,
disorder 15, Stance Entrench, digIn 2, supply Adequate, alone on the tile. Attacking are the
2nd Armoured Battalion (Veteran, strength 92, supply Full, Stance Advance) from the west, and
the 1st Field Battery (Trained, strength 100, supply Full) adjacent to the north, joining as a
supporter. Both attackers are adjacent to the target, so flankers = 1.
Step 1 — contributions.
| Working | Result | |
|---|---|---|
| 2nd Armoured | 62 × 92 × (1000 + 180 + 50 − 300) × 1000 / 10⁸ |
53 |
| 1st Field Battery (supporter, factor 400) | 48 × 100 × (1000 + 80 + 50 − 150) × 400 / 10⁸ |
18 |
| A_raw | 71 | |
| 3rd Rifles | 44 × 78 × (1000 + 0 + 0 + 150) × 1000 / 10⁸ |
39 |
Step 2 — force modifiers. Attacker: Advance +250, combined arms (Armor + Gun) +120 → +370;
A = floor(71 × 1370 / 1000) = 97. Defender: Forest cover +400, Entrench +300, position +200 →
+900; D = floor(39 × 1900 / 1000) = 74. Odds 97 : 74.
Step 3 — volatility. Attacker 700 − 100 = 600; defender 1000 + 0 = 1000; mean 800; Forest chaos +200 → 1000. Reliability ±250‰.
Step 4 — resolve. Defender 40 + 0 + 15 − 15 − floor(22/2) = 29. Attacker
40 + 12 − 10 − floor(8/2) = 38.
Step 5 — the draw. r = 617.
Step 6 — the exchange. s = floor(117 × 1000 / 2000) = 58; A_eff = 102; D_eff = 69;
shareA = floor(102000 / 171) = 596; shareD = 404; Ld = floor(60 × 596 × 100 / 100000) = 35;
La = floor(60 × 404 × 100 / 130000) = 18.
Step 7 — the break checks. Defender shock 35 + 8 + 6 = 49 ≥ 29 → the 3rd Rifles break.
Attacker shock 18 + 10 = 28 < 38, and suppressed regardless by CB-1010.
Step 8 — consequences. 3rd Rifles: 78 → 43 strength, disorder 15 → 42, retreat to the lowest-scoring adjacent tile. 2nd Armoured: 92 → 74, disorder +15, advances into Belvoir Wood if it has a movement point. 1st Field Battery: disorder +10, support spent.
What the player was shown before committing — every figure below is the exact output of the preview function, and the actual result above falls inside every band:
┌─ ASSAULT ─ 2nd Armoured Battalion → Belvoir Wood (Forest) ──────────────────┐
│ YOUR FORCE attack power 97 │
│ 2nd Armoured Advance 92 Veteran 53 base 62 │
│ +180 veteran +50 full supply −300 armour in forest │
│ 1st Field Bty support 100 Trained 18 base 48 × 400‰ │
│ +80 trained +50 full supply −150 guns in forest │
│ force +250 advance +120 combined arms (armour + guns) │
│ │
│ THEIR FORCE defence power 74 │
│ 3rd Rifles Entrench 78 Green 39 base 44 │
│ +150 foot in forest │
│ position +400 forest cover +300 entrench +200 dug in ×2 │
│ │
│ ODDS 97 : 74 RELIABILITY ±250‰ (broken ground, green) │
│ │
│ THEY LOSE 26 ── 34 ── 41 → 78 drops to 37–52 │
│ YOU LOSE 14 ── 19 ── 25 → 92 drops to 67–78 │
│ │
│ ▸ THEY BREAK — certain (1000‰). Resolve 29 against shock 40–55. │
│ They fall back to Ford Seven. They are not cut off. │
│ ▸ Your attack falters: never (0‰). │
│ ▸ You take Belvoir Wood if you have a movement point left. You do. │
│ │
│ [ Attack ] [ Attack without 1st Field Battery ] [ Cancel ] │
└─────────────────────────────────────────────────────────────────────────────┘
The same attack against a fresh defender — strength 100, disorder 0, everything else identical:
| Worn (78 / 15) | Fresh (100 / 0) | |
|---|---|---|
| Defence power | 74 | 95 |
| They lose | 26 / 34 / 41 | 22 / 30 / 37 |
| You lose | 14 / 19 / 25 | 17 / 22 / 28 |
| Their resolve | 29 | 55 |
| They break | 1000‰ | 0‰ |
The identical attack is decisive against one and pointless against the other. What changed was not the
odds — 1.31:1 against 1.02:1, a modest shift — but the resolve, and resolve is driven by condition.
So change the condition first. The battery bombards instead of supporting: A 47 against D 95,
taking 4 / 7 / 10 strength and adding 17 / 26 / 35 disorder. Seven strength is almost nothing;
26 disorder is 26 points of resolve. The armour then assaults alone at A 66 against D 89 — worse
odds than the attack that achieved nothing — and the defender, now at resolve 26, breaks 882‰ of
the time. The armour pays a median 26 strength to do it, more than the 22 it would have lost failing.
Winning is more expensive than losing. Good.
Three terrains and two Stances, lone Veteran Armoured Battalion in Advance at full strength and
Full supply against a Trained Rifle Company at full strength and Adequate supply, no supporters,
no flankers:
| Terrain | Their Stance | A | D | Reliability | They lose | You lose | Resolve | They break | You falter |
|---|---|---|---|---|---|---|---|---|---|
| Open | Hold |
106 | 47 | ±143‰ | 37/41/45 | 11/14/17 | 45 | 858‰ | 0‰ |
| Open | Entrench |
106 | 61 | ±143‰ | 33/38/41 | 13/16/20 | 60 | 0‰ | 0‰ |
| Forest | Hold |
71 | 75 | ±231‰ | 22/29/36 | 18/23/29 | 45 | 0‰ | 0‰ |
| Forest | Entrench |
71 | 91 | ±231‰ | 19/26/33 | 20/25/31 | 60 | 0‰ | 0‰ |
| Mountain | Hold |
56 | 89 | ±218‰ | 17/23/29 | 23/28/33 | 45 | 0‰ | 0‰ |
| Mountain | Entrench |
56 | 106 | ±218‰ | 15/20/27 | 25/30/34 | 60 | 0‰ | 297‰ |
A lone tank breaks infantry caught in the open 858 times in a thousand. It cannot break infantry that
has dug in anywhere, including in the open. In mountains it has a 297‰ chance of being thrown back
with nothing to show for 30 strength. The same six cases with a Field Battery supporting and two more
units adjacent (flankers = 3):
| Terrain | Their Stance | A | D | They lose | You lose | They break |
|---|---|---|---|---|---|---|
| Open | Hold |
145 | 47 | 42/45/48 | 9/11/13 | 1000‰ |
| Open | Entrench |
145 | 61 | 38/42/45 | 11/13/16 | 1000‰ |
| Forest | Hold |
102 | 75 | 27/34/41 | 14/19/25 | 1000‰ |
| Forest | Entrench |
102 | 91 | 24/31/38 | 16/21/27 | 625‰ |
| Mountain | Hold |
84 | 89 | 22/29/35 | 18/23/28 | 1000‰ |
| Mountain | Entrench |
84 | 106 | 20/26/33 | 20/25/30 | 239‰ |
A battery and two adjacent units turn 0‰ into certainty in three of the six cases, and 0‰ into a
real chance in the other two, while barely moving the odds — Forest Entrench goes from 71:75 to
102:91, a shift of a third of a point of odds, and from never to 625‰. Positioning is worth more
than force. The hard cell remains entrenched infantry in
mountains at 239‰ — a bad bet with the whole apparatus applied. That cell is not a bug; it is the
answer to "how do you take a mountain?", and the answer is that you do not assault it. Same defender
at digIn 2, same supported attack, varying only supply:
| Their supply | D | They lose | Their resolve | They break |
|---|---|---|---|---|
Full |
121 | 18/24/30 | 60 | 66‰ |
Adequate |
117 | 18/25/31 | 60 | 127‰ |
Strained |
102 | 20/27/34 | 55 | 653‰ |
Cut, and encircled |
79 | 23/30/38 | 25 | 1000‰ |
Cutting the road does more than three extra battalions, and it emerges from the interaction of supply, resolve and the break check rather than from any rule that mentions "siege".
24. Contracts on other documents
Obligations this specification places on subsystems it does not own, recorded so that a change on either side is detectable.
CB-2750 01-game-rules.md MUST provide: a terrain combat profile for every terrain type (CB-1440,
CB-1470); Chebyshev adjacency and a canonical row-major tile index; stacking capacity per tile;
passability per movement class; city ownership and works level per tile; the observation-level ladder
of CB-2310; and, optionally, crossesRiverEdge and elevation (CB-580). It MUST NOT define combat
resolution, terrain defence multipliers, or retreat behaviour.
CB-2760 02-units-and-industry.md MUST provide, per class: atk, def, tough, role,
retreatClass, bombardRange, bombardShots (CB-150). It MUST NOT define a combat resolution model
of its own, MUST NOT express unit durability as hit points (CB-110 makes strength normalised and
tough the durability lever), and MUST NOT define terrain, entrenchment, or suppression multipliers —
CB-140 replaces suppression with disorder.
CB-2770 10-turn-model.md MUST provide: a Stance per unit with the four values of CB-1510; a totally-ordered creation sequence number; a stable per-unit per-turn attack ordinal keyed positionally rather than by stream cursor (CB-810 depends on this, and it is a hard requirement, not a preference); reaction eligibility and capacity, and the halt of a moving unit when a reaction fires (CB-1780); and the guarantee that combat state changes are visible to units activating later in the same Cascade (CB-270).
CB-2780 12-economy.md MUST provide: a per-unit supply state in its three-value vocabulary and a per-unit flag for occupying a supply source, from which CB-1630 derives the combat supply tier; depot locations for the recovery table of CB-2070; and out-of-supply attrition applied after recovery (CB-2130). It MUST NOT define combat power multipliers for supply (CB-1640) and MUST NOT define an independent repair rate — strength recovery is CB-2070.
CB-2790 13-command.md's CM-150, CM-160, CM-1370 and CM-1410 are satisfied by CB-2250, CB-2260, CB-2210 and CB-1490 respectively. CM-2160's requirement that co-ordinated attacks key off adjacency and timing rather than Formation membership is satisfied by CB-420.
CB-2800 13-command.md MUST provide the Withdraw Reflex state that §12 reads, and the Risk
Sanction that pushes it. This document MUST NOT decide when a unit chooses to disengage.
CB-2810 14-victory.md MUST own how combat losses, city captures and rout destruction score.
CB-2820 03-architecture.md MUST provide the counter-based coreHash of CB-800, the canonical
state hash that CB-180 contributes to, the determinism lint rules that CB-2700 asserts against, and
the golden-master corpus infrastructure that CB-2660, CB-2690 and CB-2730 use.
CB-2830 04-ui-ux.md MUST own the presentation of the attack preview (CB-2390), the condition badge (CB-190), the hostile-tile overlay (CB-2430), the encirclement marking (CB-1410) and the reconciliation line (CB-2440), implementing the content requirements this document states.
Open questions
Is the whole-stack rout the right severity? CB-1420 destroys every unit on a tile that breaks with nowhere to go, which can remove three units in one Clash. It is the mechanic that makes the entire manoeuvre game worthwhile and it is also the largest single swing in the design. CB-2710 bands it at 20–35% of total losses; if the measured share sits above that, the named response is to destroy only the top two defenders and let the rest survive at heavy loss. What is not settled is whether that response would leave encirclement worth attempting at all.
Should a stack that cannot fit its retreat destination be destroyed? CB-1120 says yes, which makes crowding behind a thin line a genuine vulnerability and is the sharpest edge in the document. The alternative — the units that fit retreat and the remainder are destroyed — is more forgiving and much harder to preview honestly, because the preview would have to name which units die. The current rule is chosen partly because it is explainable.
Is two supporters the right number? CB-370 allows two, which makes a three-role combined-arms force reachable and therefore makes the +260 term of CB-560 attainable. One supporter would make three roles impossible and would turn combined arms into a two-role decision, which is simpler and possibly better. There is no data either way; the metric that would settle it is the build share by role in CB-2710.
Does the "not yet activated" support rule read as arbitrary? CB-370's rationale argues that it correctly rewards a battery that stayed put, because staying put lowers Initiative and lowers Initiative means activating later and activating later means being available to support. That is an elegant chain and it is also four inferential steps long. If players experience it as "my gun sometimes cannot support and I cannot tell why", the rule is wrong however good the reasoning is, and the fix is to make support available regardless of activation at the cost of a larger disorder charge.
Air and naval combat are only partially specified here. CB-1330's
Detachedretreat class and theNaval/Airrows of CB-1450 make the procedure applicable to them, but interception, basing, endurance, submerged concealment and shore bombardment are not designed in this document and the intensity constants have not been calibrated for them. The assault intensity of 60 is almost certainly wrong for an air strike, which should be sharper and shorter.Is
toughthis document's stat or the roster's? CB-160 keeps it here as a pure combat divisor. It might be better expressed as a unit size that also drives production cost, transport slots and supply draw, in which case it belongs to 02-units-and-industry.md and this document should read it rather than define its meaning.Does the once-per-turn disengagement cap create a new exploit? CB-1890 stops one unit absorbing unlimited attacks in a turn, but it also means a screening unit's second attacker gets a Caught Clash at −200 defence, which is usually fatal. A player who screens with a single unit against two attackers therefore loses it outright, which may make screening a two-unit minimum in practice. That might be correct; it was not designed.
Should reactive fire be previewable at all? CB-1840 declines to give a probability because reaction capacity is unobservable, and shows a warning instead. The opposing case is that a player who knows an enemy is in Stance
Holdcan infer reaction capacity perfectly well, so the warning is coy rather than honest, and the preview should show the reaction as a conditional outcome. The counter-argument is that inferring capacity requires knowing the enemy's Stance, which is only available at observation level L2.