diff --git a/README.md b/README.md index d13be4f..749d59a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,23 @@ # Navigation computer of the SS Probably Fine Computes flight plans for the *SS Probably Fine*: route, distance, -travel time, and the amout of fuel required. +travel time, and the amount of fuel required. ```console $ python3 nav.py mars ``` Requires Python 3.9 or newer; no further dependencies. + +All routes start at Earth. Getting back is your own problem. + +## Contributing + +Use Conventional Commits. Do not push broken code. +(Yes, Dr. Honk, this means you.) + +## Maintenance + +All maintenance is recorded in `maintenance.md`. + +To list all known destinations, run `python3 nav.py --list`. diff --git a/crew.md b/crew.md index 21d4cb4..d2e0c7e 100644 --- a/crew.md +++ b/crew.md @@ -3,5 +3,10 @@ | Name | Role | |------|------| | Captain Vega | Captain | -| Dr. Honk | Ship's doctor | +| Dr. Honk | Chief medical officer | | Lt. Byte | Chief engineer | +| Cmdr. Nova | Pilot | +| Ensign Pixel | Navigator | +| Quackers, the debugging duck | Morale officer | +| `coffeed` | Beverage officer | +| Mission control | Remote support (does not accept pushes) | diff --git a/maintenance.md b/maintenance.md index 2e27487..93f3284 100644 --- a/maintenance.md +++ b/maintenance.md @@ -1,3 +1,64 @@ # Maintenance log Newest entries at the bottom. +- 2026-09-01: Escape pod batteries charged. +- 2026-09-02: Antenna realigned. +- 2026-09-02: Fire extinguishers checked. +- 2026-09-04: Gyroscopes recalibrated. +- 2026-09-04: Escape pod batteries charged. +- 2026-09-04: Oxygen scrubbers replaced. +- 2026-09-05: Water recycling filters replaced. +- 2026-09-05: Antenna realigned. +- 2026-09-06: Fuel lines checked for leaks. +- 2026-09-07: Solar panels cleaned. +- 2026-09-07: Coffee machine descaled. +- 2026-09-07: Hull inspection, no leaks. +- 2026-09-09: Antenna realigned. +- 2026-09-09: Solar panels cleaned. +- 2026-09-10: Escape pod batteries charged. +- 2026-09-10: Debugging duck accounted for. +- 2026-09-11: Comms backup tested. +- 2026-09-14: Water recycling filters replaced. +- 2026-09-15: Airlock seals checked. +- 2026-09-15: Water recycling filters replaced. +- 2026-09-16: Coffee machine descaled. +- 2026-09-16: Antenna realigned. +- 2026-09-17: Cabin pressure sensors tested. +- 2026-09-18: Radiation shielding inspected. +- 2026-09-18: Debugging duck accounted for. +- 2026-09-18: Fire extinguishers checked. +- 2026-09-20: Fire extinguishers checked. +- 2026-09-20: Oxygen scrubbers replaced. +- 2026-09-21: Solar panels cleaned. +- 2026-09-21: Gyroscopes recalibrated. +- 2026-09-22: Fuel lines checked for leaks. +- 2026-09-23: Escape pod batteries charged. +- 2026-09-24: Galley fridge defrosted. +- 2026-09-27: Escape pod batteries charged. +- 2026-09-27: Comms backup tested. +- 2026-09-28: Gyroscopes recalibrated. +- 2026-09-28: Hull inspection, no leaks. +- 2026-09-30: Navigation lights checked. +- 2026-10-01: Cabin pressure sensors tested. +- 2026-10-02: Escape pod batteries charged. +- 2026-10-03: Navigation lights checked. +- 2026-10-05: Fuel lines checked for leaks. +- 2026-10-05: Fire extinguishers checked. +- 2026-10-05: Hull inspection, no leaks. +- 2026-10-06: Water recycling filters replaced. +- 2026-10-06: Debugging duck accounted for. +- 2026-10-07: Oxygen scrubbers replaced. +- 2026-10-07: Coffee machine descaled. +- 2026-10-08: Cabin pressure sensors tested. +- 2026-10-08: Thrusters test-fired. +- 2026-10-09: Galley fridge defrosted. +- 2026-10-10: Airlock seals checked. +- 2026-10-10: Galley fridge defrosted. +- 2026-10-11: Thrusters test-fired. +- 2026-10-11: Cabin pressure sensors tested. +- 2026-10-11: Antenna realigned. +- 2026-10-12: Thrusters test-fired. +- 2026-10-13: Fuel lines checked for leaks. +- 2026-10-14: Fuel lines checked for leaks. +- 2026-10-14: Hull inspection, no leaks. +- 2026-10-15: Escape pod batteries charged. diff --git a/nav.py b/nav.py index ca03c84..6adcb9b 100644 --- a/nav.py +++ b/nav.py @@ -2,21 +2,52 @@ """Navigation computer of the SS Probably Fine. Usage: python3 nav.py DESTINATION + python3 nav.py --list + python3 nav.py --all """ import sys +from collections.abc import Iterator # Distances between neighboring waypoints, in million km. DISTANCES = { ("earth", "moon"): 0.4, ("earth", "gateway"): 0.4, ("gateway", "mars"): 225.0, + ("gateway", "venus"): 41.0, + ("mars", "phobos"): 0.01, + ("mars", "deimos"): 0.02, + ("venus", "mercury"): 48.0, + ("mars", "ceres"): 265.0, + ("gateway", "milliways"): 42.0, + ("ceres", "vesta"): 96.0, + ("ceres", "europa"): 399.0, + ("europa", "io"): 0.25, + ("europa", "ganymede"): 0.4, + ("ganymede", "callisto"): 0.8, + ("europa", "titan"): 655.0, + ("titan", "enceladus"): 1.0, + ("titan", "pluto"): 4387.0, } # Every route starts at Earth. ROUTES = { "moon": ["earth", "moon"], "mars": ["earth", "gateway", "mars"], + "venus": ["earth", "gateway", "venus"], + "phobos": ["earth", "gateway", "mars", "phobos"], + "deimos": ["earth", "gateway", "mars", "deimos"], + "mercury": ["earth", "gateway", "venus", "mercury"], + "ceres": ["earth", "gateway", "mars", "ceres"], + "milliways": ["earth", "gateway", "milliways"], + "vesta": ["earth", "gateway", "mars", "ceres", "vesta"], + "europa": ["earth", "gateway", "mars", "ceres", "europa"], + "io": ["earth", "gateway", "mars", "ceres", "europa", "io"], + "ganymede": ["earth", "gateway", "mars", "ceres", "europa", "ganymede"], + "callisto": ["earth", "gateway", "mars", "ceres", "europa", "ganymede", "callisto"], + "titan": ["earth", "gateway", "mars", "ceres", "europa", "titan"], + "enceladus": ["earth", "gateway", "mars", "ceres", "europa", "titan", "enceladus"], + "pluto": ["earth", "gateway", "mars", "ceres", "europa", "titan", "pluto"], } NAMES = { @@ -24,6 +55,20 @@ NAMES = { "moon": "Moon", "gateway": "Lunar Gateway", "mars": "Mars", + "venus": "Venus", + "phobos": "Phobos", + "deimos": "Deimos", + "mercury": "Mercury", + "ceres": "Ceres", + "milliways": "Restaurant at the End of the Universe", + "vesta": "Vesta", + "europa": "Europa", + "io": "Io", + "ganymede": "Ganymede", + "callisto": "Callisto", + "titan": "Titan", + "enceladus": "Enceladus", + "pluto": "Pluto", } CRUISE_SPEED = 0.87 # million km per day @@ -31,17 +76,21 @@ FUEL_PER_MKM = 0.142 # tonnes of fuel per million km LAUNCH_FUEL = 380.0 # tonnes of fuel to escape Earth's gravity -def distance(a, b): +def distance(a: str, b: str) -> float: + """Return the distance between two neighboring waypoints.""" if (a, b) in DISTANCES: return DISTANCES[(a, b)] return DISTANCES[(b, a)] -def legs(route): - return [(route[i], route[i + 1]) for i in range(len(route) - 1)] +def legs(route: list[str]) -> Iterator[tuple[str, str]]: + """Yield the legs (pairs of consecutive waypoints) of a route.""" + for i in range(1, len(route) - 1): + yield route[i], route[i + 1] -def plan(destination): +def plot_course(destination: str) -> tuple[list[str], float, float, float]: + """Return route, distance, travel time (days) and fuel (t).""" route = ROUTES[destination] total = 0.0 fuel = 0.0 @@ -54,14 +103,38 @@ def plan(destination): return route, total, total / CRUISE_SPEED, fuel -def main(): - destination = sys.argv[1] - route, total, days, fuel = plan(destination) +def asteroid_route(belt: str) -> list[str]: + """Return a route through the asteroid belt (currently: via Ceres).""" + return ROUTES["ceres"] + [belt] + + +def print_plan(destination: str) -> None: + """Print the flight plan for a destination.""" + route, total, days, fuel = plot_course(destination) print("Route: " + " -> ".join(NAMES[w] for w in route)) print(f"Distance: {total:.1f} million km") print(f"Travel time: {round(days)} days") print(f"Fuel required: {round(fuel)} t") +def main() -> None: + if len(sys.argv) != 2: + sys.exit(__doc__) + if sys.argv[1] == "--list": + for key in sorted(ROUTES): + print(f"{key:12} {NAMES[key]}") + return + if sys.argv[1] == "--all": + for key in sorted(ROUTES): + print_plan(key) + print() + return + destination = sys.argv[1].lower() + if destination not in ROUTES: + known = ", ".join(sorted(ROUTES)) + sys.exit(f"Unknown destination: {destination} (known: {known})") + print_plan(destination) + + if __name__ == "__main__": main()