Files
dota_factory/docs/balancing/process.md
Malte Langkabel b4fa3e6dff Restructure balancing docs into docs/balancing/
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
2026-07-06 17:15:08 +02:00

99 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 15)
- **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).