deadhead-space-freight-tycoon/packages/sim/test/tutorial-xp.test.ts

75 lines
2.5 KiB
TypeScript

import { content } from "@deadhead/content";
import { describe, expect, it } from "vitest";
import {
createDispatchRecord,
deliveryXp,
deliveryXpFor,
generateTutorialBoard,
pilotLevel,
routeDistance,
skillPointsAvailable,
type Contract,
} from "../src/index";
const { config } = content;
const loc = (id: string) => content.location(id)!;
const now = Date.parse("2100-01-01T00:30:00Z");
const pilot = {
id: "avatar",
isAvatar: true,
assignedShipId: null,
inFlight: false,
skills: {},
restEndsAt: null,
};
const loaner = { id: "loaner", modelId: "company-courier", locationId: "earth", inFlight: false };
/** The XP a contract would pay on a tutorial run. */
function tutorialXp(contract: Contract): number {
const record = createDispatchRecord(
{
id: "t",
ship: { ...loaner, locationId: contract.originId },
pilot,
contract,
tutorial: true,
now,
worldSeed: 1,
},
content,
);
return deliveryXpFor(record, contract.tier, content);
}
describe("tutorial XP (Step 17.2, decision 0.5.107)", () => {
it("grants exactly the multiplier times a normal run's XP for the same route and cargo", () => {
const [contract] = generateTutorialBoard(
{ loanerLocationId: "earth", runsCompleted: 0, periodIndex: 0 },
content,
);
const normal = deliveryXp(routeDistance(loc("earth"), loc("moon")), contract!.tier, config);
expect(tutorialXp(contract!)).toBe(normal * config.tutorialXpMultiplier);
expect(config.tutorialXpMultiplier).toBeGreaterThanOrEqual(2);
});
it("brings the avatar to level 1 with a skill point after five runs of the lowest-XP jobs", () => {
// Every board period gives a different board; take the worst case of many.
for (let periodIndex = 0; periodIndex < 50; periodIndex++) {
let xp = 0;
let at: "earth" | "moon" = "earth";
for (let run = 0; run < config.tutorialRunsBeforeDeal; run++) {
const board = generateTutorialBoard(
{ loanerLocationId: at, runsCompleted: run, periodIndex: periodIndex + run },
content,
);
// Run 2 offers the scripted "Unmarked crates" job; the cheapest choice is still taken.
xp += Math.min(...board.map(tutorialXp));
at = at === "earth" ? "moon" : "earth";
}
const level = pilotLevel(xp, config);
expect(level, `period ${periodIndex}: ${xp} XP`).toBeGreaterThanOrEqual(1);
expect(skillPointsAvailable(level, 0, config)).toBeGreaterThanOrEqual(1);
}
});
});