71 lines
2.7 KiB
TypeScript
71 lines
2.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
cargoTypes,
|
|
gameConfig,
|
|
gameConfigSchema,
|
|
locations,
|
|
shipModels,
|
|
skills,
|
|
} from "../src/index";
|
|
|
|
// The Moon sits a fixed 0.384M km from Earth (Step 3.3).
|
|
const EARTH_MOON_MILLION_KM = 0.384;
|
|
|
|
describe("game config", () => {
|
|
it("passes the schema", () => {
|
|
expect(() => gameConfigSchema.parse(gameConfig)).not.toThrow();
|
|
});
|
|
|
|
it("uses 1 real minute = 1 game hour", () => {
|
|
expect(gameConfig.timeCompression).toBe(60);
|
|
});
|
|
|
|
it("has one Long Haul cut-off per Long Haul level", () => {
|
|
const longHaul = skills.find((s) => s.id === "long-haul")!;
|
|
expect(gameConfig.longHaulCutoffsMillionKm).toHaveLength(longHaul.maxLevel);
|
|
});
|
|
|
|
it("covers fuel for at least three Courier runs from Earth after the deal (no soft-lock)", () => {
|
|
// Amended Step 2.5 (CR-001). Assume the player always flies the lowest-paying
|
|
// (light) tutorial contract; Phase 12 replaces these with measured numbers.
|
|
const courier = shipModels.find((s) => s.id === "courier")!;
|
|
const earth = locations.find((l) => l.id === "earth")!;
|
|
const standard = cargoTypes.find((c) => c.id === "standard")!;
|
|
|
|
const lightTutorialPayout =
|
|
standard.basePayPerTonnePerMillionKm *
|
|
gameConfig.tutorialMassTonnes.light *
|
|
EARTH_MOON_MILLION_KM *
|
|
gameConfig.tutorialPayoutShare;
|
|
const earned =
|
|
lightTutorialPayout * gameConfig.tutorialRunsBeforeDeal + gameConfig.startingCredits;
|
|
const fuelFromEarth =
|
|
EARTH_MOON_MILLION_KM * courier.fuelCostPerMillionKm * earth.launchCostMultiplier;
|
|
expect(earned).toBeGreaterThanOrEqual(3 * fuelFromEarth);
|
|
});
|
|
|
|
it("sizes the tutorial board and loaner for the three mass tiers", () => {
|
|
expect(gameConfig.tutorialBoardSize).toBe(3);
|
|
const loaner = shipModels.find((s) => s.id === gameConfig.tutorialLoanerShipModelId)!;
|
|
expect(loaner.purchasable).toBe(false);
|
|
expect(gameConfig.tutorialMassTonnes.full).toBeLessThanOrEqual(loaner.cargoCapacityTonnes);
|
|
});
|
|
|
|
it("lends the Courier price plus an Earth bay lease in the deal", () => {
|
|
const courier = shipModels.find((s) => s.id === "courier")!;
|
|
const earth = locations.find((l) => l.id === "earth")!;
|
|
expect(gameConfig.dealLoanAmount).toBeGreaterThan(0);
|
|
expect(gameConfig.dealLoanAmount).toBe(courier.price + earth.bayLeasePrice);
|
|
});
|
|
|
|
it("has a contract mass band each purchasable ship can carry", () => {
|
|
for (const ship of shipModels.filter((s) => s.purchasable)) {
|
|
expect(
|
|
gameConfig.contractMassBands.some((band) => band.max <= ship.cargoCapacityTonnes),
|
|
).toBe(true);
|
|
}
|
|
const total = gameConfig.contractMassBands.reduce((sum, b) => sum + b.count, 0);
|
|
expect(total).toBe(gameConfig.contractBoardSize);
|
|
});
|
|
});
|