Split the cluttered progression_design.md and content_design.md into
separated documents by role:
- docs/balancing/rules.md - design rules and principles (moved from
progression_design.md, which is removed)
- docs/balancing/targets.md - the chosen base numbers: run shape,
factory curve, threat ladder (achieved
values adopted), combat anchors, pacing
anchors
- docs/balancing/derived.md - current tuned state of all derived
numbers, mirroring the configs
- docs/balancing/process.md - pass order, tuning discipline learned in
arena rounds 1-5, tools, next-round
checklist
- docs/balancing/history.md - chronological record: decisions, numbers
pass, calculator bugs, arena rounds,
pacing pass
- docs/balancing/README.md - index, status, open action items (moved
from progression_design.md)
content_design.md slims back down to actual content: footprint gating,
hull grids, gating matrix (module names updated to railguns), and the
production tree structure with its fiction - numbers, anchors, arena
logs, and pacing all moved to docs/balancing/. Stale references in the
config comments updated to the new locations.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyCu8vwChKMbLJQ3xosYEN
99 lines
5.0 KiB
Markdown
99 lines
5.0 KiB
Markdown
# Balancing Process
|
||
|
||
How balancing is done in this project: the pass order, the tuning
|
||
discipline, and the tools. Refer to this when starting the next
|
||
balancing round.
|
||
|
||
## The pass order
|
||
|
||
Each pass depends on the ones before it; a change in an earlier pass
|
||
invalidates the later ones (but not vice versa). Redo from the earliest
|
||
pass whose inputs changed.
|
||
|
||
1. **Targets** (`targets.md`) — choose the root numbers: run shape,
|
||
factory curve, threat-cost ladder, fleet size, block roots, combat
|
||
anchors, pacing anchors. These are design decisions, not
|
||
measurements. Everything else is derived from them.
|
||
2. **Tree structure** (`../content_design.md`) — items, chains,
|
||
what-consumes-what, per the production tree rules (one input per
|
||
phase transition, generic parts, archetypes, refactorability).
|
||
Structure only, no quantities.
|
||
3. **Numbers** (`derived.md`, recipes/materials in the configs) —
|
||
quantities and durations so every fitted ship sums to its ladder
|
||
value, the ratio curve is realized, and the belt/buffer guardrails
|
||
hold. Verified computationally by `tools/threat_report.py`.
|
||
4. **Calculator/tooling parity** — the game's `ThreatCostCalculator`
|
||
and `tools/threat_report.py` must produce identical values; the
|
||
Python tool is the design reference. Any semantic change to
|
||
REQ-THREAT-* needs both updated plus tests.
|
||
5. **Combat stats** (arena-driven) — derive stats from the combat
|
||
anchors, then iterate against the arena suite
|
||
(`bin/balancing/data/balancing.toml`) until equal-threat matchups are
|
||
near-draws. Threat costs are stat-independent, so arena ship counts
|
||
stay valid across stat changes.
|
||
6. **Pacing** — unlock ladder, `unlock_requires` edges, threat rate,
|
||
block/artifact/expansion values, per the pacing anchors.
|
||
|
||
Then: **full-game playtests**, which are the only check for the pacing
|
||
pass and feed back into targets.
|
||
|
||
## Tuning discipline (learned in arena rounds 1–5)
|
||
|
||
- **Change anchors, not symptoms.** When a class of results is off,
|
||
adjust the anchor that explains all of them (e.g. the concentration
|
||
tax) rather than individual stats.
|
||
- **Fewest knobs per round.** Attribution dies when many knobs move at
|
||
once. Prefer one anchor change plus its mechanical compensations.
|
||
- **Shared vs. local knobs.** Guns and module stats are shared across
|
||
many hulls — changing them moves many matchups. Per-hull HP moves
|
||
exactly one matchup; it is the designated per-ship trim knob on top of
|
||
the HP-per-threat prior.
|
||
- **Mind the ride-alongs.** A module buff lands on every default loadout
|
||
containing it (e.g. an armor buff strengthens the destroyer swarm that
|
||
opposes the dreadnought). Compute the net effect per matchup before
|
||
choosing step sizes.
|
||
- **Two-round signal policy.** Single arena runs re-roll by ~±10% EHP
|
||
margin; a margin inside ±20% counts as converged for v1. Only act on
|
||
signals that persist across two rounds.
|
||
- **Arena geometry is part of the fixture.** Lane width/height changes
|
||
the results (full engagement vs. fleets slipping past); margins are
|
||
only comparable within the same geometry.
|
||
- **Accept mechanical texture.** Not every deviation is a bug: a margin
|
||
that survives a stat change is mechanical (usually range/kiting under
|
||
the orbit AI) and may be desirable doctrine texture. Document the
|
||
acceptance in `targets.md` instead of chasing it.
|
||
- **Range is the strongest stat** under the orbit AI — free approach
|
||
fire. Price range modifiers conservatively; station dominance is
|
||
controlled via range, not HP.
|
||
|
||
## Tools
|
||
|
||
- `tools/threat_report.py` — item threats, module contributions,
|
||
hull/fitted ship threats, producer:consumer ratios, belt feasibility;
|
||
reads the real configs. The design reference for threat semantics.
|
||
- `tools/verify_recipes.py` — recipe tree closure, visuals coverage,
|
||
orphans, reprocessing-only items.
|
||
- `tools/verify_layouts.py` — module footprint gating matrix per hull.
|
||
- **Balancing tool** (`balancing` target) — parallel arena simulation of
|
||
`bin/balancing/data/balancing.toml`; logs winner, surviving counts,
|
||
team EHP %, and fight duration per arena. The suite covers: class
|
||
mirrors (expect near-mutual annihilation, symmetric winners),
|
||
equal-threat cross-tier matchups (expect near-draws — power-per-threat
|
||
made empirical), a 2:1 decisiveness check, doctrine matchups
|
||
(armored-vs-glass, repair-escort), and station assault.
|
||
|
||
## Checklist for the next balancing round
|
||
|
||
1. Pull; run `verify_recipes.py`, `verify_layouts.py`,
|
||
`threat_report.py`; compare against the tables in `derived.md`.
|
||
2. If recipes/materials changed: re-check fitted threats vs. the ladder
|
||
in `targets.md`; update arena suite ship counts if fitted values
|
||
moved.
|
||
3. Run the arena suite; read EHP margins and durations against the
|
||
expectations noted in `balancing.toml` and the anchors.
|
||
4. Apply changes per the tuning discipline (two-round signals only);
|
||
record the round and its knob changes in `history.md`.
|
||
5. Update `derived.md` where values moved; if an anchor moved, update
|
||
`targets.md` and state why.
|
||
6. Commit and push (the review workflow reads the remote).
|