105 lines
3.8 KiB
TypeScript
105 lines
3.8 KiB
TypeScript
import { content } from "@deadhead/content";
|
||
import { describe, expect, it } from "vitest";
|
||
import { tierFor } from "../src/board";
|
||
import { contractPayout, createRng, economicDistance, type PayoutInput } from "../src/index";
|
||
|
||
/** Plain distance, so each factor's effect is exact; the curve is tested below. */
|
||
const config = {
|
||
...content.config,
|
||
economicDistance: { ...content.config.economicDistance, exponent: 1 },
|
||
};
|
||
const standard = content.cargoType("standard")!;
|
||
const dangerous = content.cargoType("dangerous")!;
|
||
|
||
const base: PayoutInput = {
|
||
cargoType: standard,
|
||
payMultiplier: 1,
|
||
tier: 0,
|
||
massTonnes: 10,
|
||
distanceMillionKm: 100,
|
||
firstContact: false,
|
||
priority: false,
|
||
};
|
||
|
||
describe("contract payout", () => {
|
||
it("matches a hand calculation", () => {
|
||
// Standard rate (credits/t/M km) × 10 t × 100M km.
|
||
expect(contractPayout(base, config)).toBe(standard.basePayPerTonnePerMillionKm * 10 * 100);
|
||
});
|
||
|
||
it("scales linearly with mass and distance", () => {
|
||
const payout = contractPayout(base, config);
|
||
expect(contractPayout({ ...base, massTonnes: 30 }, config)).toBe(3 * payout);
|
||
expect(contractPayout({ ...base, distanceMillionKm: 250 }, config)).toBe(2.5 * payout);
|
||
});
|
||
|
||
it("pays more for Dangerous than Standard on the same mass and route", () => {
|
||
expect(contractPayout({ ...base, cargoType: dangerous, tier: 1 }, config)).toBeGreaterThan(
|
||
contractPayout(base, config),
|
||
);
|
||
});
|
||
|
||
it("pays more at a higher tier", () => {
|
||
const tier1 = contractPayout({ ...base, cargoType: dangerous, tier: 1 }, config);
|
||
const tier3 = contractPayout({ ...base, cargoType: dangerous, tier: 3 }, config);
|
||
expect(tier3).toBeGreaterThan(tier1);
|
||
});
|
||
|
||
it("pays more for a first-contact contract", () => {
|
||
expect(contractPayout({ ...base, firstContact: true }, config)).toBe(
|
||
contractPayout(base, config) * config.firstContactPayoutMultiplier,
|
||
);
|
||
});
|
||
|
||
it("rounds to whole credits", () => {
|
||
expect(Number.isInteger(contractPayout({ ...base, distanceMillionKm: 0.384 }, config))).toBe(
|
||
true,
|
||
);
|
||
});
|
||
});
|
||
|
||
describe("payout on economic distance (decision 0.5.84)", () => {
|
||
const real = content.config;
|
||
it("pays a Moon-length leg exactly as before, and long legs on the gentler curve", () => {
|
||
const moon = { ...base, distanceMillionKm: real.economicDistance.referenceMillionKm };
|
||
expect(contractPayout(moon, real)).toBe(contractPayout(moon, config));
|
||
const mars = { ...base, distanceMillionKm: 130 };
|
||
expect(contractPayout(mars, real)).toBeLessThan(contractPayout(mars, config));
|
||
expect(contractPayout(mars, real)).toBe(
|
||
Math.round(standard.basePayPerTonnePerMillionKm * 10 * economicDistance(130, real)),
|
||
);
|
||
});
|
||
});
|
||
|
||
describe("contracts carry goods (CR-007)", () => {
|
||
const rng = createRng(42);
|
||
|
||
it("takes the tier of a High-Value or Dangerous contract from its good", () => {
|
||
for (const good of content.goods.filter(
|
||
(g) => g.cargoClassId === "high-value" || g.cargoClassId === "dangerous",
|
||
)) {
|
||
expect(tierFor(rng, good, content)).toBe(good.tier);
|
||
}
|
||
});
|
||
|
||
it("keeps the random 1–5 tier for Heavy goods and tier 0 for Standard", () => {
|
||
const parts = content.good("machine-parts")!;
|
||
const seen = new Set<number>();
|
||
for (let i = 0; i < 200; i++) seen.add(tierFor(rng, parts, content));
|
||
expect([...seen].sort()).toEqual([1, 2, 3, 4, 5]);
|
||
expect(tierFor(rng, content.good("mail")!, content)).toBe(0);
|
||
});
|
||
|
||
it("doubles the payout when the good's pay multiplier doubles", () => {
|
||
expect(contractPayout({ ...base, payMultiplier: 2 }, config)).toBe(
|
||
2 * contractPayout(base, config),
|
||
);
|
||
});
|
||
|
||
it("pays a Priority contract exactly the bonus times an identical normal one", () => {
|
||
expect(contractPayout({ ...base, priority: true }, config)).toBe(
|
||
contractPayout(base, config) * config.priorityPayoutMultiplier,
|
||
);
|
||
});
|
||
});
|