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`).