import { content } from "@deadhead/content"; import { describe, expect, it } from "vitest"; import { completability, generateContractBoard, isCompletable, type BoardInput, type FleetMember, } from "../src/index"; const { config } = content; const loc = (id: string) => content.location(id)!; const allIds = new Set(content.locations.map((l) => l.id)); const maxed = Object.fromEntries(content.skills.map((k) => [k.id, k.maxLevel])); const avatarCourier: FleetMember = { modelId: "courier", skills: {} }; /** Assorted fleets: lone ships, mixed crews, upgrades, skills. */ const FLEETS: FleetMember[][] = [ [avatarCourier], [avatarCourier, { modelId: "clipper", skills: { "expensive-cargo": 1 } }], [{ modelId: "lighter", skills: { "heavy-cargo": 2 }, upgrades: { "cargo-bay": 2 } }], [{ modelId: "freighter", skills: { "long-haul": 2, "dangerous-cargo": 1 } }], [{ modelId: "bulk-hauler", skills: maxed }, avatarCourier], ]; describe("boards keep only completable jobs (Step 19.2, decision 0.5.114)", () => { it("never offers a job the fleet can't complete, over 1,000 boards", () => { let boards = 0; const origins = ["earth", "moon", "mars", "ceres-station", "jupiter-cloud-platform"]; for (const [f, fleet] of FLEETS.entries()) { for (const origin of origins) { for (let periodIndex = 0; periodIndex < 40; periodIndex++) { const discoveredIds = periodIndex % 2 ? allIds : new Set(["earth", "moon", "mars"]); const input: BoardInput = { location: loc(origin), discoveredIds, periodIndex, worldSeed: 1000 + f, fleet, }; for (const c of generateContractBoard(input, content)) { const { completable, failures } = completability(c, fleet, discoveredIds, content); expect(completable, `${c.id} ${JSON.stringify(failures)}`).toBe(true); } boards++; } } } expect(boards).toBeGreaterThanOrEqual(1000); }); it("gives a level-0 avatar in a Courier at Earth full boards of Standard Moon jobs it fits", () => { for (let periodIndex = 0; periodIndex < 200; periodIndex++) { const board = generateContractBoard( { location: loc("earth"), discoveredIds: allIds, periodIndex, worldSeed: 7, fleet: [avatarCourier], }, content, ); expect(board).toHaveLength(config.contractBoardSize); for (const c of board) { expect(c.cargoTypeId).toBe("standard"); expect(c.massTonnes).toBeLessThanOrEqual(content.shipModel("courier")!.cargoCapacityTonnes); expect(c.destinationId).toBe("moon"); } } }); it("stops at the attempt limit with a smaller board when the fleet can do almost nothing", () => { // From Mars a Courier reaches nowhere: nothing is completable. const nothing = generateContractBoard( { location: loc("mars"), discoveredIds: allIds, periodIndex: 3, worldSeed: 7, fleet: [avatarCourier], }, content, ); expect(nothing).toEqual([]); // Only Earth known: a Courier can take nothing but the odd first contact to the // Moon, which random candidates hit only sometimes, so some boards come up short. const known = new Set(["earth"]); let short = 0; for (let periodIndex = 0; periodIndex < 50; periodIndex++) { const board = generateContractBoard( { location: loc("earth"), discoveredIds: known, periodIndex, worldSeed: 7, fleet: [avatarCourier], }, content, ); expect(board.length).toBeLessThanOrEqual(config.contractBoardSize); if (board.length < config.contractBoardSize) short++; for (const c of board) { expect(c.destinationId).toBe("moon"); expect(isCompletable(c, [avatarCourier], known, content)).toBe(true); } } expect(short).toBeGreaterThan(0); }); it("gives identical boards for identical inputs", () => { for (const fleet of FLEETS) { const input: BoardInput = { location: loc("earth"), discoveredIds: allIds, periodIndex: 12, worldSeed: 99, fleet, }; expect(generateContractBoard(input, content)).toEqual(generateContractBoard(input, content)); } }); it("filters first contacts the same way", () => { // Only Earth and the Moon known: a Clipper may explore Mars or Venus, a Courier nowhere. const known = new Set(["earth", "moon"]); let clipperFirstContacts = 0; for (let periodIndex = 0; periodIndex < 100; periodIndex++) { const input = { location: loc("earth"), discoveredIds: known, periodIndex, worldSeed: 5 }; const courierBoard = generateContractBoard({ ...input, fleet: [avatarCourier] }, content); expect(courierBoard.some((c) => c.firstContact)).toBe(false); const clipper: FleetMember = { modelId: "clipper", skills: { "long-haul": 1 } }; for (const c of generateContractBoard({ ...input, fleet: [clipper] }, content)) { if (c.firstContact) { clipperFirstContacts++; expect(isCompletable(c, [clipper], known, content)).toBe(true); } } } expect(clipperFirstContacts).toBeGreaterThan(0); }); });