#!/usr/bin/env python3 """Simplest possible ship-trip cost: give it a ship, a start and a stop, done - it works out jumps/charges/sliders/landing fuel itself. For when you don't know (and shouldn't need to know) the physics breakdown - if you already have jump counts/slider values/gateway fees in hand, use the lower-level tools/shipcost.py instead. tools/shiptrip.py AVI-07JFB ZV-307 ZV-759c # Antares -> Deimos tools/shiptrip.py AVI-07JFC "Antares Station" Nike # names/planets/systems all work Defaults to max sliders (v0=v1=1.0, fastest) since that's the common case; pass --v0/--v1 to model a slower/cheaper trip. Covers direct-jump routes (puga/route.py's FTL jump graph) and the handful of gateway chains with a measured LOCK fee (puga.shipping.KNOWN_GATEWAY_ROUTES) - for any other gateway route, the LOCK fee genuinely cannot be looked up (no FIO/PRUNplanner endpoint has it), and this tool tells you exactly where to read it live and what to run afterward. """ import argparse, math, sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from puga import config, fio, market, shipping def check_ship(reg: str): """Warn (don't fail) if the given ship's live specs don't match what this model is calibrated against - see shipping.CALIBRATED_SPEC.""" try: ships = fio.own(f"/ship/ships/{fio.me()}") except Exception: return None ship = next((s for s in ships if s.get("Registration") == reg), None) if ship is None: print(f"warning: {reg!r} isn't a ship on this account (known: " + ", ".join(s.get("Registration", "?") for s in ships) + ") - proceeding anyway", file=sys.stderr) return None spec = shipping.CALIBRATED_SPEC def differs(k, v): return v != spec[k] if isinstance(spec[k], str) else not math.isclose(v or 0, spec[k], rel_tol=1e-6) mismatches = [k for k, v in {"blueprint": ship.get("BlueprintNaturalId"), "acceleration": ship.get("Acceleration"), "thrust": ship.get("Thrust"), "reactor_power": ship.get("ReactorPower"), "emitter_power": ship.get("EmitterPower")}.items() if differs(k, v)] if mismatches: print(f"warning: {reg} differs from the calibrated hull/reactor on {', '.join(mismatches)} - " f"this model (puga/shipping.py) has NOT been validated for this ship, numbers may be wrong", file=sys.stderr) return ship def main(): ap = argparse.ArgumentParser() ap.add_argument("ship", help="ship registration, e.g. AVI-07JFB") ap.add_argument("src", help="start: planet natural id, system natural id, or name") ap.add_argument("dst", help="stop: planet natural id, system natural id, or name") ap.add_argument("--v0", type=float, default=1.0, help="fuel-usage slider, 0..1 (default 1.0 = fastest)") ap.add_argument("--v1", type=float, default=1.0, help="reactor-usage slider, 0..1 (default 1.0 = fastest)") ap.add_argument("--cx", default=config.DEFAULT_CX) a = ap.parse_args() check_ship(a.ship) snap = market.snapshot() sf_px = (snap.get(("SF", a.cx)) or market.Quote("SF", a.cx)).ask or 0 ff_px = (snap.get(("FF", a.cx)) or market.Quote("FF", a.cx)).ask or 0 result = shipping.leg_cost(a.src, a.dst, sf_px, ff_px, a.v0, a.v1) if result is None: sys.exit(f"no priceable route between {a.src} and {a.dst} - not a direct-jump route, and not a measured " f"gateway chain (puga.shipping.KNOWN_GATEWAY_ROUTES). The LOCK fee can't be looked up (no " f"FIO/PRUNplanner endpoint has it) - open the Ship Flight Control screen in APEX for this exact " f"route, read each LOCK segment's fee, then run:\n" f" tools/shipcost.py --v0 {a.v0} --gateways N --lock-fee AMOUNT:CURRENCY [--lock-fee ...]") lnd = result["landing_fuel_included"] print(f"{a.src} -> {a.dst}" + (" (landing fuel not measured for this destination - not included)" if lnd is None else "")) print(f"STL fuel: {result['stl_fuel']:.1f} units -> {result['stl_fuel']*sf_px:,.0f} AIC") print(f"FTL fuel: {result['ftl_fuel']:.1f} units -> {result['ftl_fuel']*ff_px:,.0f} AIC") if result["non_aic_fees"]: print("Gateway LOCK fees (non-AIC): " + ", ".join(f"{amt:,.0f} {cur}" for amt, cur in result["non_aic_fees"])) print(f"\nTotal (AIC): {result['aic']:,.0f}" + (" + non-AIC fees above" if result["non_aic_fees"] else "")) if __name__ == "__main__": main()