Add runway tool with auto-detected inbound ships; fix multi-base state tagging

- tools/runway.py: days of runway per material before a base stalls, from simulate()'s real
  net consumption, base storage, plus (by default) any ship currently flying to that planet
- tools/state.py: tag each STORE entry with its planet (FIO AddressableId -> site SiteId) and
  each ship with its live flight origin/destination (keyed by ShipId, not StlFuelStoreId).
  Needed now that a 2nd base exists - storage/ship data was previously unscoped, which would
  have silently mixed bases' inventories together
- tests, docs updated (docs/library.md notes the multi-base tagging as a reusable pattern)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-28 21:56:18 +02:00
co-authored by Claude Sonnet 5
parent 58db08b921
commit b24f132210
6 changed files with 155 additions and 2 deletions
+11
View File
@@ -118,6 +118,17 @@ profit/day is for its WHOLE plan, not the marginal addition - for "is this new b
also simulate the base without the addition and diff, same as `tools/simulate.py`'s built-vs-planned
new_capex logic but applied to profit instead of capex.
## Multi-base state: storage and ships are now tagged by location
Once a 2nd base exists, `empire/state/company.yaml`'s `storage` list has multiple `STORE` entries
(one per base) and must be filtered by `planet` (a field `tools/state.py` now adds, from FIO's
`AddressableId` matching a site's `SiteId`) - summing all `STORE` entries blindly (as `tools/buy.py`
did briefly) mixes bases' inventories together. Similarly each entry in `ships` now carries a
`flight` dict (`origin`, `destination`, `eta_ms`) from live `/ship/flights`, keyed to the ship via
`ShipId` (not `StlFuelStoreId`, despite that looking like a plausible match - verified 2026-09-28).
`tools/runway.py`'s `inbound_ships()`/`on_hand()` are the reference implementation for scoping to
one base correctly; reuse them rather than re-deriving this.
## Conventions to keep
- Cache TTLs matter: market data is cached ~15 min, static game data ~24h (`puga/cache.py`).
+2
View File
@@ -20,6 +20,8 @@ Legend: [ ] todo, [x] done. Build order matters; each step is usable on its own.
13. [x] `tools/buy.py`: cash-aware shopping list (construction gap vs empire state's built count, plus N days of net operating stock via simulate(), priced at real fill cost, checked against cash/reserve).
14. [x] `tools/sell.py`: ask-post-price + expected-clear-time advisor from 30d traded-volume percentiles, vs hitting the bids now.
15. [x] `tools/runway.py`: days-of-runway per material from simulate()'s net consumption, base storage plus auto-detected inbound ship cargo (tools/state.py now tags each ship with its live flight origin/destination, and each STORE with its planet, needed once a 2nd base existed).
12. [ ] `tools/portfolio.py`: choose the set of 1-building opportunities for one base that maximises total profit given shared whole-building housing (HB1/HB2/...), capex, permits and (for demolish-later) 60-day value decay. Persistence check exists (`tools/persistence.py`); wire it into the ranking.
## Open questions