deadhead-space-freight-tycoon/packages/sim/test/fuel-share.test.ts

64 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { content } from "@deadhead/content";
import { beforeAll, describe, expect, it } from "vitest";
import { formatFuelShares, fuelShares, type FuelShare } from "../balance/fuel-share";
import { checkRoute, fuelPriceAt, legFuel, routeDistance } from "../src/index";
const loc = (id: string) => content.location(id)!;
let rows: FuelShare[];
beforeAll(() => {
rows = fuelShares(content);
});
describe("fuel-to-payout check (CR-018 Step 18.1, decision 0.5.110)", () => {
it("reports every purchasable ship on every route it can fly", () => {
const models = content.shipModels.filter((m) => m.purchasable);
expect(new Set(rows.map((r) => r.modelId))).toEqual(new Set(models.map((m) => m.id)));
for (const row of rows) {
const model = content.shipModel(row.modelId)!;
expect(checkRoute(model, loc(row.originId), loc(row.destinationId), content.config).ok).toBe(
true,
);
expect(row.contracts).toBeGreaterThan(0);
}
// Every Earth–Moon leg is flown by every purchasable ship, and boards always offer it.
for (const model of models) {
expect(
rows.some(
(r) => r.modelId === model.id && r.originId === "earth" && r.destinationId === "moon",
),
).toBe(true);
}
});
it("prices a leg's fuel at the origin and divides by the median fitting payout", () => {
const row = rows.find(
(r) => r.modelId === "courier" && r.originId === "earth" && r.destinationId === "moon",
)!;
const courier = content.shipModel("courier")!;
const fuel =
legFuel(courier, loc("earth"), routeDistance(loc("earth"), loc("moon")), content.config) *
fuelPriceAt(loc("earth"), content.config);
expect(row.fuelCost).toBeCloseTo(fuel, 9);
expect(row.share).toBeCloseTo(row.fuelCost / row.medianPayout, 12);
expect(row.medianPayout).toBeGreaterThan(0);
});
it("keeps fuel at most 25% of the median payout on every route but the Clipper's Mars → Earth (Step 18.2)", () => {
// One price can't put every row in 15–25% (user decision, CR-018): only the top is gated.
// Few Mars → Earth jobs are small enough for the Clipper's 30 t hold, so their median is low.
const over = rows
.filter((r) => r.share > 0.25)
.map((r) => `${r.modelId} ${r.originId} → ${r.destinationId}`);
expect(over).toEqual(["clipper mars → earth"]);
});
it("reports the share for each ship and route as a table", () => {
const table = formatFuelShares(rows).split("\n");
expect(table).toHaveLength(rows.length + 1);
expect(
table.find((line) => line.startsWith("courier") && line.includes("earth → moon")),
).toMatch(/\d+\.\d%$/);
});
});