Add buy/sell tools, library docs, and permit/HQ fixes
- tools/buy.py: cash-aware shopping list (construction gap vs empire state's built buildings, plus N days of NET operating stock via simulate() so self-produced inputs net against consumption), priced at real order-book fill cost, checked against cash/reserve, flags multi-trip, binary-searches an affordable size when short - tools/sell.py: where to post an ask vs hitting the bids now, with expected clear time from 30-day traded-volume percentiles - docs/library.md: module map and patterns for using puga.* directly instead of the CLI, plus the "simulate() only knows what the plan lists" gotcha that a first version of buy.py hit - tools/simulate.py: fix --no-hq being a no-op; tools/state.py: permits_total_override for when the in-game HQ screen disagrees with FIO's MaximumPermits - docs/mechanics.md: HQ display vs PRUNplanner's HQ flag, workforce arrival sources - CLAUDE.md, docs/roadmap.md updated Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
spec = importlib.util.spec_from_file_location("buy", Path(__file__).resolve().parent.parent / "tools" / "buy.py")
|
||||
buy = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(buy)
|
||||
|
||||
BUILDINGS = [
|
||||
{"building_ticker": "HWP", "costs": [{"material_ticker": "BBH", "material_amount": 4}, {"material_ticker": "LTA", "material_amount": 2}], "area_cost": 25},
|
||||
{"building_ticker": "HB2", "costs": [{"material_ticker": "BTA", "material_amount": 2}], "area_cost": 12},
|
||||
]
|
||||
PLAN = {"plan_data": {"buildings": [{"name": "HWP", "amount": 1}], "infrastructure": [{"building": "HB2", "amount": 1}]}}
|
||||
|
||||
|
||||
def test_construction_items_include_mcg_and_full_amount_when_nothing_built():
|
||||
need = buy.construction_items(PLAN, BUILDINGS, built={})
|
||||
assert need == {"BBH": 4, "LTA": 2, "MCG": 100 + 48, "BTA": 2}
|
||||
|
||||
|
||||
def test_construction_items_gap_only_counts_unbuilt_units():
|
||||
need = buy.construction_items(PLAN, BUILDINGS, built={"HWP": 1, "HB2": 1})
|
||||
assert need == {} # already fully built: nothing to buy
|
||||
|
||||
|
||||
def test_construction_items_partial_gap():
|
||||
plan = {"plan_data": {"buildings": [{"name": "HWP", "amount": 3}], "infrastructure": []}}
|
||||
need = buy.construction_items(plan, BUILDINGS, built={"HWP": 1})
|
||||
assert need == {"BBH": 8, "LTA": 4, "MCG": 200} # 2 more HWP needed, not 3
|
||||
|
||||
|
||||
def test_stock_items_only_keeps_net_consumption():
|
||||
flows = {"C": {"inp": 11.13, "out": 0}, "AL": {"inp": 42.78, "out": 44.57}, "BHP": {"inp": 0, "out": 14.26}}
|
||||
got = buy.stock_items(flows, days=2)
|
||||
assert got == {"C": 22.26} # AL nets to +1.78/day (own smelters cover the HWP): not bought
|
||||
|
||||
|
||||
def test_net_of_on_hand_floors_at_zero():
|
||||
got = buy.net_of_on_hand({"C": 20, "O": 5}, {"C": 30, "O": 2})
|
||||
assert got == {"C": 0.0, "O": 3.0}
|
||||
Reference in New Issue
Block a user