Release Healer Man 0.1.16

This commit is contained in:
phenom committed 2026-09-05 20:42:38 -04:00
1 parent f49e54f35f
commit b57d2b2839
89 files changed
+6055 -17

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
ARENA_RATING_BANDS,
arenaAiProfile,
arenaDampening,
arenaDrDuration,
arenaMatchTolerance,
calculateArenaRating,
shouldArenaBotChooseCorrectly,
validateArenaComposition,
} from "./arena-rules.mjs";
function seededRandom(seed) {
let value = seed >>> 0;
return () => {
value = (value * 1664525 + 1013904223) >>> 0;
return value / 0x100000000;
};
}
test("arena Elo uses band K values, draws are neutral, and clamps at both ends", () => {
assert.deepEqual(calculateArenaRating({ rating: 1000, opponentRating: 1000, score: 1 }), {
before: 1000, expected: 0.5, delta: 20, after: 1020,
});
assert.equal(calculateArenaRating({ rating: 2499, opponentRating: 2500, score: 1 }).after, 2500);
assert.equal(calculateArenaRating({ rating: 0, opponentRating: 0, score: 0 }).after, 0);
assert.equal(calculateArenaRating({ rating: 1600, opponentRating: 1900, score: 0.5 }).delta, 0);
});
test("arena composition requires exactly one healer and rejects tanks", () => {
assert.equal(validateArenaComposition("2v2", ["healer", "damage"]).ok, true);
assert.equal(validateArenaComposition("3v3", ["healer", "damage", "damage"]).ok, true);
assert.equal(validateArenaComposition("3v3", ["healer"], { allowPartial: true }).missing.damage, 2);
assert.equal(validateArenaComposition("2v2", ["tank", "damage"]).code, "invalid_arena_role");
assert.equal(validateArenaComposition("2v2", ["damage", "damage"]).code, "invalid_composition");
});
test("difficulty, dampening, matchmaking, and DR follow the rated rules", () => {
const gladiator = arenaAiProfile(2500);
assert.equal(gladiator.healerInterruptChance, 0.85);
assert.equal(gladiator.minimumErrorChance, 0.08);
assert.equal(shouldArenaBotChooseCorrectly(1, gladiator.minimumErrorChance, () => 0.93), false);
assert.equal(arenaDampening(0), 0.1);
assert.equal(arenaDampening(540_000), 1);
assert.equal(arenaMatchTolerance(0), 100);
assert.equal(arenaMatchTolerance(30_000), 400);
assert.equal(arenaDrDuration(8000, 2, 0).durationMs, 2000);
assert.equal(arenaDrDuration(8000, 3, 15_000).durationMs, 8000);
});
test("every AI band honors its settings and seeded minimum-error floor", () => {
for (const [index, band] of ARENA_RATING_BANDS.entries()) {
const profile = arenaAiProfile(band.min);
assert.equal(profile.title, band.title);
assert.equal(profile.reactionMinSeconds, band.reaction[0]);
assert.equal(profile.reactionMaxSeconds, band.reaction[1]);
assert.equal(profile.healerInterruptChance, band.healerInterrupt);
assert.equal(profile.coordinatedCcChance, band.coordinatedCc);
assert.equal(profile.correctDefensiveChance, band.defensive);
assert.equal(profile.minimumErrorChance, band.error);
const random = seededRandom(index + 1);
const decisions = Array.from({ length: 4000 }, () => shouldArenaBotChooseCorrectly(1, profile.minimumErrorChance, random));
const errorRate = decisions.filter((decision) => !decision).length / decisions.length;
assert.ok(errorRate >= profile.minimumErrorChance - 0.025, `${band.title} fell below its minimum-error floor`);
assert.ok(errorRate <= profile.minimumErrorChance + 0.025, `${band.title} exceeded its seeded error tolerance`);
}
});