Write the v2 production tree into the config files
recipes.toml: full v2 rewrite - iron/copper/quartz mining, smelting incl. the value-losing scrap sink, reprocessing (4 scrap / 4 s, voidsteel at 20% of the full pool), tiered intermediates, hulls, module prefabs, and the three drop-only shortcut recipes. modules.toml: lasers renamed to railguns (implementation unchanged), prefab materials and numbers-pass production times; combat stats stay placeholders for the arena pass. ships.toml: hull-item materials, numbers-pass base production times, and geometry-validated default_modules loadouts for every ship (waves now spawn armed). visuals.toml: item entries for the new palette (quartz, silicon, copper_coil, control_chip, capacitor_bank, hardened_steel, ceramic_plate, voidsteel, voidsteel_plate, railgun modules); retired titanium/alloy/laser items removed. world.toml: scrap_per_threat = 0.25 per the numbers pass. threat_report.py: commit an item's threat only once every eligible recipe is computable - the previous first-resolved-wins behavior let shortcut recipes underprice items (same flaw exists in ThreatCostCalculator, recorded as action item 9). Verified: all default_modules placements valid, verify_recipes and verify_layouts pass, and the report reproduces the numbers-pass tables exactly (fitted: 10.5/47/99/233.5/354.5/722.5/1491.5/1436.5). Doc tables updated for the three geometry-corrected loadouts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DyCu8vwChKMbLJQ3xosYEN
This commit is contained in:
@@ -86,23 +86,30 @@ def resolve_items(recipes, scrap_threat):
|
||||
|
||||
item_threat = {"scrap": scrap_threat}
|
||||
|
||||
def resolve_pass():
|
||||
# All eligible (recipe, output) pairs per item: the max rule requires
|
||||
# committing an item only once EVERY eligible recipe for it is
|
||||
# computable — otherwise a shallow shortcut recipe that resolves one
|
||||
# pass earlier than the base path would win and underprice the item.
|
||||
recipes_per_item = {}
|
||||
for recipe in non_repro:
|
||||
for output in recipe.get("outputs", []):
|
||||
if eligible(recipe, output):
|
||||
recipes_per_item.setdefault(output["item"], []).append(
|
||||
(recipe, output))
|
||||
|
||||
def resolve_pass(require_all_recipes=True):
|
||||
progress = False
|
||||
best = {}
|
||||
for recipe in non_repro:
|
||||
for output in recipe.get("outputs", []):
|
||||
if output["item"] in item_threat:
|
||||
continue
|
||||
if not eligible(recipe, output):
|
||||
continue
|
||||
threat = recipe_threat_per_unit(recipe, output, item_threat)
|
||||
if threat is None:
|
||||
continue
|
||||
key = output["item"]
|
||||
if key not in best or threat > best[key]:
|
||||
best[key] = threat
|
||||
# A full sweep before committing keeps the max rule correct when
|
||||
# several recipes for the same item resolve in the same pass.
|
||||
for item, pairs in recipes_per_item.items():
|
||||
if item in item_threat:
|
||||
continue
|
||||
threats = [recipe_threat_per_unit(r, o, item_threat)
|
||||
for r, o in pairs]
|
||||
if require_all_recipes and any(t is None for t in threats):
|
||||
continue
|
||||
threats = [t for t in threats if t is not None]
|
||||
if threats:
|
||||
best[item] = max(threats)
|
||||
for item, threat in best.items():
|
||||
item_threat[item] = threat
|
||||
progress = True
|
||||
@@ -139,6 +146,13 @@ def resolve_items(recipes, scrap_threat):
|
||||
progress = resolve_pass()
|
||||
progress = repro_pass() or progress
|
||||
|
||||
# Deadlock guard: if recipe cycles keep items waiting on each other,
|
||||
# fall back to committing with the computable subset of recipes.
|
||||
progress = True
|
||||
while progress:
|
||||
progress = resolve_pass(require_all_recipes=False)
|
||||
progress = repro_pass() or progress
|
||||
|
||||
return item_threat
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user