rg-stats
Version:
A library for calculating various rhythm game stats.
338 lines (324 loc) • 7.01 kB
text/typescript
import t from "tap";
import { isAprx } from "../test-utils/approx";
import { TestCase } from "../test-utils/test-case";
import { ThrowsToSnapshot } from "../test-utils/throw-snapshot";
import { calculate, inverse } from "./jubility";
const testCases = [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
];
t.test("Jubility Tests", (t) => {
function MakeTestCase(rate: number, level: number, jubility: number): TestCase {
return (t) =>
t.equal(
// note: score doesn't actually matter, it's just there to check
// that your score isn't < 700k.
calculate(700_001, rate, level),
jubility,
`${rate}% on a level ${level} chart should be worth ${jubility} jubility.`
);
}
// This is just one big table stolen from bemaniwiki.
for (const testCase of testCases) {
MakeTestCase(testCase[0], testCase[1], testCase[2])(t);
}
t.equal(calculate(699_999, 100, 10), 0, "Should return 0 for scores < 700k no matter what.");
t.end();
});
t.test("Jubility Validation Tests", (t) => {
ThrowsToSnapshot(
t,
() => calculate(1_000_001, 100, 10.5),
"Should throw if score is > 1million."
);
ThrowsToSnapshot(
t,
() => calculate(1_000_000, 120.1, 10.5),
"Should throw if music rate is > 120."
);
ThrowsToSnapshot(t, () => calculate(-1, 100, 10.5), "Should throw if score is negative.");
ThrowsToSnapshot(
t,
() => calculate(1_000_000, -1, 10.5),
"Should throw if music rate is negative."
);
ThrowsToSnapshot(
t,
() => calculate(1_000_000, -1, -1),
"Should throw if chart level is negative."
);
t.end();
});
t.test("Inverse Jubility Tests", (t) => {
function MakeTestCase(rate: number, level: number, jubility: number): TestCase {
return (t) =>
isAprx(
t,
// We ceil this because the bemaniwiki test dataset is not very
// accurate. All our data should be pretty close, though.
Math.ceil(inverse(jubility, level)),
rate,
`Inverse ${jubility} on a level ${level} chart should be equal to ${rate} music rate.`,
1
);
}
for (const testCase of testCases) {
MakeTestCase(testCase[0], testCase[1], testCase[2])(t);
}
ThrowsToSnapshot(
t,
() => inverse(9000, 1),
"Should throw if the requested jubility is not possible on a chart of that level."
);
t.end();
});