import { describe, expect, it } from "vitest"; import { content, type FixedPosition } from "../src/index"; import { offsetPosition } from "../src/data/locations"; // Vite reads the lockfile as text at test time. import lockfile from "../../../pnpm-lock.yaml?raw"; function point(p: FixedPosition) { const a = (p.angleDegrees * Math.PI) / 180; return { x: p.radiusMillionKm * Math.cos(a), y: p.radiusMillionKm * Math.sin(a) }; } function distance(fromId: string, toId: string): number { const a = point(content.location(fromId)!.position); const b = point(content.location(toId)!.position); return Math.hypot(b.x - a.x, b.y - a.y); } describe("fixed map (Step 15.1, decision 0.5.93)", () => { it("gives every location a fixed position", () => { for (const location of content.locations) { expect(location.position.radiusMillionKm, location.id).toBeGreaterThan(0); expect(Number.isFinite(location.position.angleDegrees), location.id).toBe(true); } }); it("defines the Moon as an offset from Earth", () => { const moon = content.location("moon")!; expect(moon.orbitParent).toEqual({ locationId: "earth", offsetMillionKm: 0.384 }); expect(moon.position).toEqual(offsetPosition(content.location("earth")!.position, 0.384)); expect(distance("earth", "moon")).toBeCloseTo(0.384, 6); }); it("puts every child body at its parent plus its offset", () => { for (const location of content.locations.filter((l) => l.orbitParent)) { const { locationId, offsetMillionKm } = location.orbitParent!; const parent = content.location(locationId)!; expect(location.position, location.id).toEqual( offsetPosition(parent.position, offsetMillionKm), ); } }); it.each([ ["earth", "mars", 201], ["mars", "ceres-station", 202], ["earth", "ceres-station", 402], ["ceres-station", "jupiter-cloud-platform", 393], ["mars", "jupiter-cloud-platform", 592], ["earth", "jupiter-cloud-platform", 792], ])("puts %s–%s at about %i M km (CR-015 §2, within 2 %)", (from, to, expected) => { const d = distance(from, to); expect(Math.abs(d - expected) / expected).toBeLessThan(0.02); expect(distance(to, from)).toBeCloseTo(d, 9); }); it("no longer depends on astronomy-engine", () => { expect(lockfile).not.toContain("astronomy-engine"); }); }); describe("new locations (Step 16.1, decision 0.5.100)", () => { it.each([ ["earth", "venus-cloud-city", 200], ["moon", "venus-cloud-city", 200], ["venus-cloud-city", "mars", 332], ["venus-cloud-city", "ceres-station", 520], ["venus-cloud-city", "jupiter-cloud-platform", 885], ["ceres-station", "io-sulfur-works", 393], ["ceres-station", "europa-ice-station", 393], ])("puts %s–%s at about %i M km (CR-016 §2, within 2 %)", (from, to, expected) => { expect(Math.abs(distance(from, to) - expected) / expected).toBeLessThan(0.02); }); it("keeps the Jupiter system within 1.1M km of the platform", () => { const system = ["jupiter-cloud-platform", "io-sulfur-works", "europa-ice-station"]; for (const a of system) for (const b of system) expect(distance(a, b)).toBeLessThan(1.1); }); it("rides Io and Europa with Jupiter, at their offsets", () => { expect(content.location("io-sulfur-works")!.orbitParent).toEqual({ locationId: "jupiter-cloud-platform", offsetMillionKm: 0.42, }); expect(content.location("europa-ice-station")!.orbitParent).toEqual({ locationId: "jupiter-cloud-platform", offsetMillionKm: 0.67, }); }); it("starts all three undiscovered, selling fuel, landable without Hardened ships", () => { for (const id of ["venus-cloud-city", "io-sulfur-works", "europa-ice-station"]) { const location = content.location(id)!; expect(location.startsDiscovered, id).toBe(false); expect(location.sellsFuel, id).toBe(true); expect(location.landingRequirement, id).not.toBe("hardened"); } expect(content.locations.filter((l) => l.startsDiscovered).map((l) => l.id)).toEqual([ "earth", "moon", ]); }); });