Add planet environment-extra materials to capex calcs; buildcost.py tool
Every building needs a planet-hazard material bundle on top of its normal BuildingCosts (SEA for low pressure, HSE for high pressure, MGC for low gravity, BL for high gravity, INS for cold, TSH for heat - area-scaled where applicable), confirmed against live in-game build panels on Nike. FIO's own BuildRequirements field is an unreliable superset across hazard types, not a direct per-building list - don't use it directly. - puga/planet.py::environment_extras(): the real threshold table. - tools/buildcost.py: full construction material list for one building, optionally on a given planet. - Wired into whatif.py/buy.py (via simulate(planet=...)) and scan.py's own bcost(), so their capex numbers stop silently omitting this. - docs/mechanics.md: the rule, plus a separate forum-sourced note on the CX order-cancellation penalty. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+28
-3
@@ -1,8 +1,33 @@
|
||||
"""Rank planets by extraction factor for a resource, with founding cost (environment extras
|
||||
from FIO's own BuildRequirements - authoritative, not guessed from temperature/pressure
|
||||
thresholds, which PRUNplanner's own calculations don't model either)."""
|
||||
"""Rank planets by extraction factor for a resource. Also holds environment_extras(): FIO's own
|
||||
BuildRequirements field is an unreliable superset across hazard types (confirmed wrong in-game,
|
||||
see docs/mechanics.md) - the real per-building material is computed from the planet's own
|
||||
pressure/gravity/temperature/surface against the Prosperous Universe Handbook's threshold table,
|
||||
which PRUNplanner's own calculations don't model either."""
|
||||
from . import fio
|
||||
|
||||
# (condition check, ticker, qty per unit area, flat qty) - handbook building-costs table
|
||||
ENV_RULES = [
|
||||
(lambda p: (p.get("pressure") or 0) < 0.25, "SEA", 1.0, 0.0),
|
||||
(lambda p: (p.get("pressure") or 0) > 2.0, "HSE", 0.0, 1.0),
|
||||
(lambda p: 0 < (p.get("gravity") or 1) < 0.25, "MGC", 0.0, 1.0),
|
||||
(lambda p: (p.get("gravity") or 0) > 2.5, "BL", 0.0, 1.0),
|
||||
(lambda p: (p.get("temperature") if p.get("temperature") is not None else 20) < -25, "INS", 10.0, 0.0),
|
||||
(lambda p: (p.get("temperature") or 0) > 75, "TSH", 0.0, 1.0),
|
||||
]
|
||||
|
||||
|
||||
def environment_extras(planet: dict, area: float) -> dict[str, float]:
|
||||
"""Extra construction/repair materials a building needs beyond its normal BuildingCosts and
|
||||
the universal MCG area cost (already handled elsewhere, e.g. simulate()'s mcg_per_area) - one
|
||||
row per hazard the planet actually has (pressure/gravity/temperature; rocky-vs-gaseous MCG/AEF
|
||||
swap not covered here). planet needs lowercase pressure/gravity/temperature keys, e.g. from
|
||||
prunplanner._g('/data/planet/{id}/') or fio.planets_full() remapped to lowercase."""
|
||||
out: dict[str, float] = {}
|
||||
for cond, tk, per_area, flat in ENV_RULES:
|
||||
if cond(planet):
|
||||
out[tk] = out.get(tk, 0.0) + per_area * area + flat
|
||||
return out
|
||||
|
||||
|
||||
def candidates(resource_ticker: str, min_factor: float = 0.0):
|
||||
"""All planets with this resource, sorted by factor descending. Each: PlanetNaturalId,
|
||||
|
||||
+7
-3
@@ -2,6 +2,7 @@
|
||||
Input is a PRUNplanner plan JSON (as stored by the API, or built by tools/plan_push.build_payload). Pure given the data passed in.
|
||||
Validated against a screenshot of the real simulator (tests/test_simulate.py)."""
|
||||
from . import econ
|
||||
from . import planet as planet_mod
|
||||
|
||||
TIERS = econ.TIERS
|
||||
FARMS = {"FRM", "ORC"}
|
||||
@@ -10,8 +11,8 @@ DEGRADATION_DAYS = 180 # usePlanCalculation.ts: degradation/day = const
|
||||
|
||||
def simulate(plan: dict, recipes: list[dict], buildings: list[dict], resources: list[dict], fertility: float,
|
||||
price, faction: str | None = None, permits: tuple[float, float] = (1, 2), mcg_per_area: float = 4.0,
|
||||
built: dict[str, int] | None = None) -> dict:
|
||||
"""price(ticker, side) -> AIC per unit or None; side is "buy" (net consumed) or "sell" (net produced), like PRUNplanner BUY/SELL exchange preferences; construction uses "buy". resources: planet resources [{material_ticker, resource_type, daily_extraction}]."""
|
||||
built: dict[str, int] | None = None, planet: dict | None = None) -> dict:
|
||||
"""price(ticker, side) -> AIC per unit or None; side is "buy" (net consumed) or "sell" (net produced), like PRUNplanner BUY/SELL exchange preferences; construction uses "buy". resources: planet resources [{material_ticker, resource_type, daily_extraction}]. planet: optional, adds environment-extra construction materials via planet.environment_extras() (SEA/HSE/MGC/BL/INS/TSH - needs lowercase pressure/gravity/temperature keys)."""
|
||||
d = plan["plan_data"]
|
||||
bl = {b["building_ticker"]: b for b in buildings}
|
||||
rec = {r["recipe_id"]: r for r in recipes}
|
||||
@@ -69,7 +70,10 @@ def simulate(plan: dict, recipes: list[dict], buildings: list[dict], resources:
|
||||
|
||||
def unit_cost(tk):
|
||||
info = bl[tk]
|
||||
return sum(c["material_amount"] * (price(c["material_ticker"], "buy") or 0) for c in info["costs"]) + mcg_per_area * info["area_cost"] * (price("MCG", "buy") or 0)
|
||||
cost = sum(c["material_amount"] * (price(c["material_ticker"], "buy") or 0) for c in info["costs"]) + mcg_per_area * info["area_cost"] * (price("MCG", "buy") or 0)
|
||||
if planet is not None:
|
||||
cost += sum(qty * (price(etk, "buy") or 0) for etk, qty in planet_mod.environment_extras(planet, info["area_cost"]).items())
|
||||
return cost
|
||||
|
||||
# Verified vs simulator screenshots: degradation = production buildings' construction cost / 180 (infrastructure excluded);
|
||||
# plan cost and area also include one core module (CM), added automatically.
|
||||
|
||||
Reference in New Issue
Block a user