Add tools/planet.py: rank planets for a resource with real founding cost
Ranks every planet with a given extraction factor using FIO's bulk planet data, pricing founding cost straight from FIO's own BuildRequirements (real environment-extras materials for that specific planet, not guessed temperature/pressure thresholds - PRUNplanner's own calculations don't model those either). Takes --from an existing base to report jump distance via tools/route.py, since pure-factor ranking alone is misleading: the best-factor planets found for FEO were all 500-2600+ ly away, confirming Vulcan (merely decent factor, same system as Deimos) was still the right practical pick. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rank planets for a resource, with founding cost (environment extras straight from FIO's
|
||||
BuildRequirements - real game data, not guessed thresholds) and optional jump distance from
|
||||
a base you already have.
|
||||
|
||||
tools/planet.py FEO --top 10
|
||||
tools/planet.py LST --min-factor 0.5 --from ZV-759c --top 5
|
||||
"""
|
||||
import argparse, sys
|
||||
from pathlib import Path
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from puga import config, market, planet, route
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("resource", help="raw resource ticker, e.g. LST, FEO, SEA")
|
||||
ap.add_argument("--min-factor", type=float, default=0.0)
|
||||
ap.add_argument("--top", type=int, default=10)
|
||||
ap.add_argument("--from", dest="from_", help="planet/system to measure jump distance from (e.g. an existing base)")
|
||||
ap.add_argument("--cx", default=config.DEFAULT_CX)
|
||||
a = ap.parse_args()
|
||||
|
||||
rows = planet.candidates(a.resource, a.min_factor)
|
||||
if not rows:
|
||||
print(f"no planets found with {a.resource.upper()} (factor >= {a.min_factor})")
|
||||
return
|
||||
rows = rows[:a.top]
|
||||
|
||||
src = route.resolve_system(a.from_) if a.from_ else None
|
||||
graph = route.load_graph() if src else None
|
||||
|
||||
for p in rows:
|
||||
env_cost = 0.0
|
||||
env_items = []
|
||||
for br in p["build_requirements"]:
|
||||
w = market.walk(br["MaterialTicker"], a.cx, br["MaterialAmount"], "buy")
|
||||
env_cost += w["total"]
|
||||
env_items.append(f"{br['MaterialAmount']}{br['MaterialTicker']}")
|
||||
dist = ""
|
||||
if src:
|
||||
r = route.shortest_path(src, p["system_id"], graph=graph) if p["system_id"] != src else {"jumps": 0, "distance_ly": 0.0}
|
||||
dist = f" {r['jumps']} jumps ({r['distance_ly']:.0f} ly)" if r else " unreachable"
|
||||
print(f"{p['natural_id']:8} {p['name']:16} factor {p['factor']:.3f} T {p['temperature']:6.1f}C P {p['pressure']:.2f}bar "
|
||||
f"G {p['gravity']:.2f} fert {p['fertility']:5.2f} faction {p['faction'] or '-':4} env-cost {env_cost:8,.0f} AIC ({', '.join(env_items) or 'none'}){dist}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user