import { content } from "@deadhead/content"; import { describe, expect, it } from "vitest"; import { autoRepairBooks, boardPeriodMs, canReachHq, chooseAction, defaultOrders, fuelPriceAt, independentOptions, MS_PER_MINUTE, nearestReachableHq, repairRecommended, type Contract, type IndependentInput, } from "../src/index"; const { config } = content; const clipper = content.shipModel("clipper")!; const PERIOD = boardPeriodMs(config); const PERIOD_START = 1000 * PERIOD; const AFTER_LOOK = PERIOD_START + (config.independentPilots.firstLookRealMinutes + 1) * MS_PER_MINUTE; const EARTH_PRICE = fuelPriceAt(content.location("earth")!, config); const DISCOVERED = new Set(["earth", "moon", "mars", "ceres-station", "jupiter-cloud-platform"]); const job = (id: string, destinationId: string): Contract => ({ id, originId: "earth", destinationId, cargoTypeId: "standard", goodId: "consumer-goods", shipper: "Pacific Orbital Logistics", priority: false, deliveryWindowGameHours: null, tier: 0, massTonnes: 10, payout: 30_000, requiredSkill: null, requiredLongHaulLevel: 0, offeredAt: PERIOD_START, expiresAt: PERIOD_START + PERIOD, firstContact: false, }); function input(board: Contract[], over: Partial = {}): IndependentInput { return { ship: { id: "s", modelId: "clipper", locationId: "earth", inFlight: false, tank: { fuelFu: clipper.tankCapacityFu, avgPrice: EARTH_PRICE }, }, pilot: { id: "p", isAvatar: false, assignedShipId: "s", inFlight: false, skills: { "long-haul": 3 }, restEndsAt: null, }, orders: defaultOrders("earth", content), board, now: AFTER_LOOK, credits: 1_000_000, spendingFloor: config.independentPilots.defaultSpendingFloor, discoveredIds: DISCOVERED, emptyPeriods: 0, ...over, }; } // Mars is one leg from Earth for a Clipper (CR-015); Ceres isn't. const mars = { hqId: "mars" }; describe("service routing (Step 27.2, decisions 0.5.153, 0.5.154)", () => { it("can reach Mars but not Ceres from Earth in a Clipper", () => { expect(canReachHq(input([]).ship, "mars", DISCOVERED, content)).toBe(true); expect(canReachHq(input([]).ship, "ceres-station", DISCOVERED, content)).toBe(false); }); it("with a booking at Mars, considers only jobs that end at Mars", () => { const board = [job("a", "moon"), job("b", "ceres-station"), job("c", "mars")]; const options = independentOptions(input(board, { booking: mars }), content); expect(options.map((o) => o.contract.destinationId)).toEqual(["mars"]); expect(chooseAction(input(board, { booking: mars }), content)).toMatchObject({ kind: "take", contractId: "c", }); }); it("deadheads straight to Mars when no job goes there", () => { const board = [job("a", "moon")]; expect(chooseAction(input(board, { booking: mars }), content)).toMatchObject({ kind: "deadhead-service", destinationId: "mars", }); }); it("waits at the booked HQ for the work to start", () => { const at = input([job("a", "moon")], { booking: mars }); const there = { ...at, ship: { ...at.ship, locationId: "mars" } }; expect(chooseAction(there, content)).toEqual({ kind: "wait" }); }); it("works as usual when the booked HQ is out of reach", () => { // A Courier can't reach Jupiter from Earth. const courier = content.shipModel("courier")!; const base = input([job("a", "moon")], { booking: { hqId: "jupiter-cloud-platform" } }); const small = { ...base, ship: { ...base.ship, modelId: "courier", tank: { fuelFu: courier.tankCapacityFu, avgPrice: 5 }, }, }; expect(canReachHq(small.ship, "jupiter-cloud-platform", DISCOVERED, content)).toBe(false); expect(chooseAction(small, content)).toMatchObject({ kind: "take", contractId: "a" }); }); it("finds the nearest reachable HQ", () => { const ship = input([]).ship; expect(nearestReachableHq(ship, ["mars", "moon"], DISCOVERED, content)).toBe("moon"); expect(nearestReachableHq(ship, ["earth", "moon"], DISCOVERED, content)).toBe("earth"); const courier = { ...ship, modelId: "courier", tank: { fuelFu: 40, avgPrice: 5 } }; expect(nearestReachableHq(courier, ["jupiter-cloud-platform"], DISCOVERED, content)).toBeNull(); }); }); describe("auto-repair (Step 27.2, decision 0.5.157)", () => { const on = { on: true, thresholdPct: 40 }; const drop = { hullBefore: 45, hullAfter: 35, autoRepair: on, booking: null }; it("books a repair when an Independent crew's hull drops below the threshold", () => { expect(autoRepairBooks({ ...drop, pilotMode: "independent" })).toBe(true); }); it("doesn't with a Manual crew, with auto-repair off, or without crossing the threshold", () => { expect(autoRepairBooks({ ...drop, pilotMode: "manual" })).toBe(false); expect( autoRepairBooks({ ...drop, pilotMode: "independent", autoRepair: { ...on, on: false } }), ).toBe(false); expect(autoRepairBooks({ ...drop, pilotMode: "independent", hullBefore: 38 })).toBe(false); expect(autoRepairBooks({ ...drop, pilotMode: "independent", hullAfter: 41 })).toBe(false); }); it("adds the repair to an upgrade booking, but not a second repair", () => { expect(autoRepairBooks({ ...drop, pilotMode: "independent", booking: { repair: false } })).toBe( true, ); expect(autoRepairBooks({ ...drop, pilotMode: "independent", booking: { repair: true } })).toBe( false, ); }); it("recommends a repair for a Manual crew below the threshold", () => { const base = { hullPct: 35, autoRepair: on, booking: null }; expect(repairRecommended({ ...base, pilotMode: "manual" })).toBe(true); expect(repairRecommended({ ...base, pilotMode: "independent" })).toBe(false); expect(repairRecommended({ ...base, pilotMode: "manual", hullPct: 50 })).toBe(false); expect(repairRecommended({ ...base, pilotMode: "manual", booking: { repair: true } })).toBe( false, ); }); });