import { afterAll, describe, expect, it } from "vitest"; import { mkdirSync, writeFileSync } from "node:fs"; import { BASE_CLASS_IDS, COA_CLASS_IDS, ROM_CLASS_IDS } from "../app/characterCatalog"; import { loadCanonicalBenchmarkBuilds } from "./benchmark/benchmarkBuilds"; import { runDungeonBossBenchmark } from "./benchmark/dungeonBossBenchmark"; import { BENCHMARK_POLICY_ID } from "./benchmark/benchmarkController"; import { requireDungeonDefinition } from "./dungeonRegistry"; import { finalDungeonBoss } from "./dungeonRunStore"; const dungeon = requireDungeonDefinition("ragefire-chasm"); const final = finalDungeonBoss(dungeon)!; const ordinary = dungeon.bosses.find((boss) => boss.id !== final.id)!; const samples: unknown[] = []; afterAll(() => { if (process.env.BOSS_BALANCE_REPORT !== "1") return; mkdirSync("artifacts/boss-difficulty", { recursive: true }); writeFileSync("artifacts/boss-difficulty/wow-rom-coa-balance.json", `${JSON.stringify({ difficulty: 1, dungeon: dungeon.id, durationLimitSeconds: 60, targets: { ordinary: 25, final: 45 }, policy: BENCHMARK_POLICY_ID, gear: "resolved starter heirlooms", samples, }, null, 2)}\n`); }); describe("default boss difficulty across WoW, RoM, and CoA classes", () => { for (const level of [8, 20, 40, 80]) { it(`benchmarks every class-role build at level ${level}`, async () => { const builds = await loadCanonicalBenchmarkBuilds(level, [...BASE_CLASS_IDS, ...ROM_CLASS_IDS, ...COA_CLASS_IDS]); const tooFast: string[] = []; for (const build of builds) { // Let the test worker deliver progress between CPU-heavy simulations. await new Promise((resolve) => setTimeout(resolve, 0)); for (const seed of ["class-balance-1", "class-balance-2", "class-balance-3"]) { const boss = await runDungeonBossBenchmark(build, ordinary.id, seed); const last = await runDungeonBossBenchmark(build, final.id, seed); samples.push({ level, classId: build.classId, source: build.source, role: build.role, orientation: build.orientation, seed, boss, final: last }); if ((boss.seconds ?? boss.elapsedSeconds) < 25 || (last.seconds ?? last.elapsedSeconds) < 45) { tooFast.push(`${build.id}: ${boss.seconds}/${last.seconds}`); } } } expect(tooFast).toEqual([]); }, 180_000); } for (const level of [8, 80]) { it(`records healer outcomes with the final boss attacking at level ${level}`, async () => { const builds = (await loadCanonicalBenchmarkBuilds(level, [...BASE_CLASS_IDS, ...ROM_CLASS_IDS, ...COA_CLASS_IDS])) .filter((build) => build.role === "healer"); for (const build of builds) { const last = await runDungeonBossBenchmark(build, final.id, "class-balance-2", true); samples.push({ level, classId: build.classId, source: build.source, role: build.role, mechanics: true, final: last }); expect(last.attacks, build.id).toBeGreaterThan(5); expect(last.incomingDamage, build.id).toBeGreaterThan(0); } }, 60_000); } });