Add first numbers pass for the v2 production tree

content_design.md: full recipe quantities and durations computed with
a recursive threat calculator - economy constants (scrap_per_threat
0.25, reprocessing 4 scrap / 4 s, voidsteel at threat 100), per-item
threat values, module contributions, and fitted ship threats vs the
ladder (96-124%, smooth ~x2-per-class curve). Checks recorded: ratio
curve realized (t1 1:1, t2 2:3, t3 strange), belt feasibility under
the single-belt cap, block at 4 threat implying ~18 blocks average
building cost for the 4-minute doubling target, and a small-end
deviation note recommending ladder adjustment over chain thinning.

progression_design.md: two new action items - amend REQ-THREAT-ITEM to
exclude scrap-consuming recipes from item threat (the max rule would
otherwise inflate basic materials via the scrap smelting recipe), and
port the calculator to tools/threat_report.py.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyCu8vwChKMbLJQ3xosYEN
This commit is contained in:
2026-07-03 15:05:40 +02:00
parent 72b38df0dd
commit fe7703d5c3
2 changed files with 108 additions and 0 deletions

View File

@@ -339,6 +339,101 @@ first-pass tree. `verify_recipes.py` must be re-run once this lands in
recipes.toml; the numbers pass should add a threat-report tool that
prints per-item threat values and producer:consumer ratio tables.
### Numbers — first pass
Computed with a recursive threat calculator (to be ported to
`tools/threat_report.py`, see the action items in
`progression_design.md`); quantities and durations tuned so fitted
ships land on the threat-cost ladder and the ratio curve is realized.
**Economy constants:** `scrap_per_threat = 0.25` (1 scrap per 4 threat
destroyed — a cruiser kill drops ~59 scrap). Reprocessing: 4 scrap per
cycle, 4 s, full-pool weights iron_ingot 30 / copper_ingot 30 /
silicon 20 / voidsteel 20 → threat(voidsteel) = (4·4 + 4)/0.2 = 100.
Scrap smelting: 1 scrap → 1 iron_ingot, 1 s — deliberately
value-losing (4 threat of scrap becomes a 2-threat ingot); reprocessing
is the value-preserving path.
**Recipes** (dur in seconds; threat is per output item):
| item | recipe | dur | out | threat |
|---|---|---|---|---|
| iron_ore / copper_ore | miner | 1 | 1 | 1 |
| quartz | miner (deposit) | 2 | 1 | 2 |
| iron_ingot | 1 iron_ore | 1 | 1 | 2 |
| copper_ingot | 1 copper_ore | 1 | 1 | 2 |
| silicon | 1 quartz | 2 | 1 | 4 |
| steel_plate | 2 iron_ingot | 3 | 1 | 7 |
| copper_wire | 1 copper_ingot | 1 | 2 | 1.5 |
| copper_coil | 2 copper_wire | 1.5 | 1 | 4.5 |
| building_block | 2 steel_plate | 2 | 4 | 4 |
| control_chip | 1 silicon + 2 copper_wire | 5 | 1 | 12 |
| capacitor_bank | 2 copper_coil + 1 silicon | 5 | 1 | 18 |
| hardened_steel | 3 steel_plate | 12 | 1 | 33 |
| ceramic_plate | 2 quartz | 4 | 1 | 8 |
| drive_unit | 2 steel_plate + 2 copper_coil + 1 control_chip | 8 | 1 | 43 |
| voidsteel_plate | 1 voidsteel + 1 hardened_steel | 8 | 1 | 141 |
| capital_core | 2 voidsteel + 1 capacitor_bank + 1 control_chip | 10 | 1 | 240 |
**Module prefabs** (contribution = item threat + module production
time):
| module | recipe | dur | mod. time | contribution |
|---|---|---|---|---|
| railgun_s | 1 copper_coil | 1 | 1 | 6.5 |
| salvager | 1 steel_plate + 2 copper_wire | 2 | 1 | 13 |
| repair_tool | 1 steel_plate + 2 copper_wire | 2 | 1 | 13 |
| armor_plates | 4 steel_plate | 3 | 1 | 32 |
| maneuvering_thrusters | 1 steel_plate + 1 copper_coil | 2 | 1 | 14.5 |
| sensor_booster | 2 copper_wire + 1 copper_coil | 2 | 1 | 10.5 |
| afterburner | 2 copper_coil + 1 steel_plate | 3 | 1 | 20 |
| weapon_stabilizer | 1 steel_plate + 1 copper_coil | 2 | 1 | 14.5 |
| weapon_primer | 1 capacitor_bank + 1 copper_coil | 4 | 2 | 28.5 |
| weapon_upgrade | 1 control_chip + 1 copper_coil | 4 | 2 | 22.5 |
| railgun_m | 1 capacitor_bank + 2 steel_plate + 1 copper_coil | 4 | 3 | 43.5 |
| drone_bay | 1 control_chip + 2 steel_plate + 1 copper_coil | 4 | 3 | 37.5 |
| railgun_l | 1 capacitor_bank + 2 hardened_steel + 1 ceramic_plate | 6 | 4 | 102 |
| drone_hangar | 1 voidsteel_plate + 2 control_chip + 1 drive_unit | 10 | 6 | 224 |
**Ships** (fitted = hull item + ship base time + typical loadout; the
typical loadouts double as the `default_modules` for enemy waves):
| ship | hull recipe | dur | base | typical loadout | fitted (target) |
|---|---|---|---|---|---|
| drone | 1 iron_ingot | 1 | 1 | railgun_s | 10 (10) |
| frigate | 2 steel_plate + 1 copper_wire | 2 | 2 | 2× railgun_s, maneuvering_thrusters | 47 (40) |
| destroyer | 3 steel_plate + 2 copper_coil | 4 | 3 | 3× railgun_s, armor_plates, sensor_booster | 99 (80) |
| cruiser | 2 hardened_steel + 2 control_chip | 6 | 4 | 2× railgun_m, armor_plates, maneuvering_thrusters | 234 (200) |
| battlecruiser | 3 hardened_steel + 2 control_chip + 1 drive_unit | 8 | 5 | 3× railgun_m, weapon_primer, armor_plates | 370 (350) |
| battleship | 3 voidsteel_plate + 1 drive_unit + 2 control_chip | 10 | 6 | railgun_l, 2× railgun_m, weapon_stabilizer, armor_plates, sensor_booster | 752 (700) |
| dreadnought | 5 voidsteel_plate + 1 capital_core + 2 drive_unit | 12 | 8 | 3× railgun_l, weapon_primer, weapon_upgrade, 2× armor_plates | 1472 (1500) |
| carrier | 5 voidsteel_plate + 1 capital_core + 2 drive_unit | 12 | 8 | drone_hangar, 2× railgun_m, 2× armor_plates, sensor_booster | 1436 (1500) |
**Checks:**
- *Ratio curve realized:* t1 all 1:1 (miner:smelter); t2 clean 2:3
(ingot→plate, wire→coil); t3 strange — 2:5 (silicon→chip), 3:5
(coil→capacitor), 3:4 (plate→hardened, plate→drive); t4 inverted 3:2
(hardened→voidsteel_plate).
- *Belt feasibility:* worst input demand is 1.33 items/s (wire→coil,
plate→armor) — under the ~2/s single-belt cap everywhere; no
accidental multi-belt recipes.
- *Block economy:* building_block = 4 threat ⇒ for the 4-minute
doubling at 30% capacity, the average building must cost ≈ 18 blocks
(0.3 × 240 / 4). buildings.toml costs should be set around that mean
(belts cheap, producers ~2030).
- *Small-end deviation:* frigatecruiser land 624% hot because the
fixed chain overhead dominates small hulls. Recommendation: accept
and adjust the ladder targets to the achieved values (the ~×2-per-
class curve shape is preserved) rather than thinning the early
chains below readability.
- *Rule bug discovered:* the scrap→iron_ingot smelter recipe combined
with REQ-THREAT-ITEM's max-across-recipes rule would set
threat(iron_ingot) to the scrap path (1 + 4 = 5, or more at other
scrap values) instead of 2, inflating every downstream item. Fix
required: exclude scrap-consuming recipes from item threat
computation (see progression_design.md action items).
## Balancing targets (first pass, July 2026)
The six root numbers for the balancing pass. Every derived value (threat

View File

@@ -373,3 +373,16 @@ in `requirements.md` and the git history). Still open:
one matching deposit tile). Touches REQ-BLD-MINER ("every asteroid
tile is equivalent" no longer holds), REQ-GW-ASTEROID-EXPAND /
REQ-EXP-*, `world.toml`, and `visuals.toml`.
6. **Exclude scrap-consuming recipes from item threat.** Amend
REQ-THREAT-ITEM: recipes that take scrap as an input do not
participate in an item's threat computation (neither as its value
nor in the max-across-recipes rule). Otherwise the scrap→ingot
smelter recipe inflates the basic materials' threat via the max
rule, poisoning every downstream value. Scrap conversion is already
accounted for by the REQ-THREAT-SCRAP constant.
7. **Threat report tool.** Port the numbers-pass calculator to
`tools/threat_report.py`: per-item threat values, module
contributions, fitted ship threats vs. the ladder targets,
producer:consumer ratio tables, and belt-feasibility checks, read
from the real config files. Re-run after any recipe or material change, like
`verify_recipes.py`.