rg-stats
Version:
A library for calculating various rhythm game stats.
857 lines (817 loc) • 32.9 kB
text/typescript
import t from "tap";
import { TestCase } from "../test-utils/test-case";
import { ThrowsToSnapshot } from "../test-utils/throw-snapshot";
import {
calculateVF4,
calculateVF5,
calculateVF6,
inverseVF4,
inverseVF5,
inverseVF6,
SDVXLamps,
} from "./volforce";
t.test("VF4 Tests", (t) => {
function VF4TestCase(level: number, expectedVF4: number, score: number) {
return { level, expectedVF4, score };
}
// As always, a bunch of test data ripped from bemaniwiki.
const VF4ExampleData = [
VF4TestCase(1, 42, 9_000_000),
VF4TestCase(1, 43, 9_100_000),
VF4TestCase(1, 43, 9_200_000),
VF4TestCase(1, 44, 9_300_000),
VF4TestCase(1, 45, 9_400_000),
VF4TestCase(1, 46, 9_500_000),
VF4TestCase(1, 46, 9_600_000),
VF4TestCase(1, 47, 9_700_000),
VF4TestCase(1, 47, 9_750_000),
VF4TestCase(1, 48, 9_800_000),
VF4TestCase(1, 48, 9_850_000),
VF4TestCase(1, 49, 9_900_000),
VF4TestCase(1, 49, 9_950_000),
VF4TestCase(1, 49, 9_990_000),
VF4TestCase(1, 50, 10_000_000),
VF4TestCase(2, 64, 9_000_000),
VF4TestCase(2, 64, 9_100_000),
VF4TestCase(2, 65, 9_200_000),
VF4TestCase(2, 66, 9_300_000),
VF4TestCase(2, 67, 9_400_000),
VF4TestCase(2, 69, 9_500_000),
VF4TestCase(2, 69, 9_600_000),
VF4TestCase(2, 71, 9_700_000),
VF4TestCase(2, 71, 9_750_000),
VF4TestCase(2, 72, 9_800_000),
VF4TestCase(2, 73, 9_850_000),
VF4TestCase(2, 74, 9_900_000),
VF4TestCase(2, 74, 9_950_000),
VF4TestCase(2, 74, 9_990_000),
VF4TestCase(2, 75, 10_000_000),
VF4TestCase(3, 85, 9_000_000),
VF4TestCase(3, 86, 9_100_000),
VF4TestCase(3, 87, 9_200_000),
VF4TestCase(3, 89, 9_300_000),
VF4TestCase(3, 90, 9_400_000),
VF4TestCase(3, 92, 9_500_000),
VF4TestCase(3, 93, 9_600_000),
VF4TestCase(3, 95, 9_700_000),
VF4TestCase(3, 95, 9_750_000),
VF4TestCase(3, 97, 9_800_000),
VF4TestCase(3, 97, 9_850_000),
VF4TestCase(3, 99, 9_900_000),
VF4TestCase(3, 99, 9_950_000),
VF4TestCase(3, 99, 9_990_000),
VF4TestCase(3, 100, 10_000_000),
VF4TestCase(4, 106, 9_000_000),
VF4TestCase(4, 108, 9_100_000),
VF4TestCase(4, 109, 9_200_000),
VF4TestCase(4, 111, 9_300_000),
VF4TestCase(4, 112, 9_400_000),
VF4TestCase(4, 115, 9_500_000),
VF4TestCase(4, 116, 9_600_000),
VF4TestCase(4, 118, 9_700_000),
VF4TestCase(4, 119, 9_750_000),
VF4TestCase(4, 121, 9_800_000),
VF4TestCase(4, 121, 9_850_000),
VF4TestCase(4, 123, 9_900_000),
VF4TestCase(4, 124, 9_950_000),
VF4TestCase(4, 124, 9_990_000),
VF4TestCase(4, 125, 10_000_000),
VF4TestCase(5, 128, 9_000_000),
VF4TestCase(5, 129, 9_100_000),
VF4TestCase(5, 131, 9_200_000),
VF4TestCase(5, 133, 9_300_000),
VF4TestCase(5, 135, 9_400_000),
VF4TestCase(5, 138, 9_500_000),
VF4TestCase(5, 139, 9_600_000),
VF4TestCase(5, 142, 9_700_000),
VF4TestCase(5, 143, 9_750_000),
VF4TestCase(5, 145, 9_800_000),
VF4TestCase(5, 146, 9_850_000),
VF4TestCase(5, 148, 9_900_000),
VF4TestCase(5, 149, 9_950_000),
VF4TestCase(5, 149, 9_990_000),
VF4TestCase(5, 150, 10_000_000),
VF4TestCase(6, 149, 9_000_000),
VF4TestCase(6, 151, 9_100_000),
VF4TestCase(6, 152, 9_200_000),
VF4TestCase(6, 156, 9_300_000),
VF4TestCase(6, 157, 9_400_000),
VF4TestCase(6, 161, 9_500_000),
VF4TestCase(6, 162, 9_600_000),
VF4TestCase(6, 166, 9_700_000),
VF4TestCase(6, 167, 9_750_000),
VF4TestCase(6, 169, 9_800_000),
VF4TestCase(6, 170, 9_850_000),
VF4TestCase(6, 173, 9_900_000),
VF4TestCase(6, 174, 9_950_000),
VF4TestCase(6, 174, 9_990_000),
VF4TestCase(6, 175, 10_000_000),
VF4TestCase(7, 171, 9_000_000),
VF4TestCase(7, 172, 9_100_000),
VF4TestCase(7, 174, 9_200_000),
VF4TestCase(7, 178, 9_300_000),
VF4TestCase(7, 180, 9_400_000),
VF4TestCase(7, 184, 9_500_000),
VF4TestCase(7, 186, 9_600_000),
VF4TestCase(7, 190, 9_700_000),
VF4TestCase(7, 191, 9_750_000),
VF4TestCase(7, 194, 9_800_000),
VF4TestCase(7, 195, 9_850_000),
VF4TestCase(7, 198, 9_900_000),
VF4TestCase(7, 199, 9_950_000),
VF4TestCase(7, 199, 9_990_000),
VF4TestCase(7, 200, 10_000_000),
VF4TestCase(8, 192, 9_000_000),
VF4TestCase(8, 194, 9_100_000),
VF4TestCase(8, 196, 9_200_000),
VF4TestCase(8, 200, 9_300_000),
VF4TestCase(8, 203, 9_400_000),
VF4TestCase(8, 207, 9_500_000),
VF4TestCase(8, 209, 9_600_000),
VF4TestCase(8, 213, 9_700_000),
VF4TestCase(8, 214, 9_750_000),
VF4TestCase(8, 218, 9_800_000),
VF4TestCase(8, 219, 9_850_000),
VF4TestCase(8, 222, 9_900_000),
VF4TestCase(8, 223, 9_950_000),
VF4TestCase(8, 224, 9_990_000),
VF4TestCase(8, 225, 10_000_000),
VF4TestCase(9, 213, 9_000_000),
VF4TestCase(9, 216, 9_100_000),
VF4TestCase(9, 218, 9_200_000),
VF4TestCase(9, 223, 9_300_000),
VF4TestCase(9, 225, 9_400_000),
VF4TestCase(9, 230, 9_500_000),
VF4TestCase(9, 232, 9_600_000),
VF4TestCase(9, 237, 9_700_000),
VF4TestCase(9, 238, 9_750_000),
VF4TestCase(9, 242, 9_800_000),
VF4TestCase(9, 243, 9_850_000),
VF4TestCase(9, 247, 9_900_000),
VF4TestCase(9, 248, 9_950_000),
VF4TestCase(9, 249, 9_990_000),
VF4TestCase(9, 250, 10_000_000),
VF4TestCase(10, 235, 9_000_000),
VF4TestCase(10, 237, 9_100_000),
VF4TestCase(10, 240, 9_200_000),
VF4TestCase(10, 245, 9_300_000),
VF4TestCase(10, 248, 9_400_000),
VF4TestCase(10, 253, 9_500_000),
VF4TestCase(10, 256, 9_600_000),
VF4TestCase(10, 261, 9_700_000),
VF4TestCase(10, 262, 9_750_000),
VF4TestCase(10, 266, 9_800_000),
VF4TestCase(10, 268, 9_850_000),
VF4TestCase(10, 272, 9_900_000),
VF4TestCase(10, 273, 9_950_000),
VF4TestCase(10, 274, 9_990_000),
VF4TestCase(10, 275, 10_000_000),
VF4TestCase(11, 256, 9_000_000),
VF4TestCase(11, 259, 9_100_000),
VF4TestCase(11, 262, 9_200_000),
VF4TestCase(11, 267, 9_300_000),
VF4TestCase(11, 270, 9_400_000),
VF4TestCase(11, 276, 9_500_000),
VF4TestCase(11, 279, 9_600_000),
VF4TestCase(11, 285, 9_700_000),
VF4TestCase(11, 286, 9_750_000),
VF4TestCase(11, 291, 9_800_000),
VF4TestCase(11, 292, 9_850_000),
VF4TestCase(11, 297, 9_900_000),
VF4TestCase(11, 298, 9_950_000),
VF4TestCase(11, 299, 9_990_000),
VF4TestCase(11, 300, 10_000_000),
VF4TestCase(12, 277, 9_000_000),
VF4TestCase(12, 280, 9_100_000),
VF4TestCase(12, 284, 9_200_000),
VF4TestCase(12, 290, 9_300_000),
VF4TestCase(12, 293, 9_400_000),
VF4TestCase(12, 299, 9_500_000),
VF4TestCase(12, 302, 9_600_000),
VF4TestCase(12, 308, 9_700_000),
VF4TestCase(12, 310, 9_750_000),
VF4TestCase(12, 315, 9_800_000),
VF4TestCase(12, 316, 9_850_000),
VF4TestCase(12, 321, 9_900_000),
VF4TestCase(12, 323, 9_950_000),
VF4TestCase(12, 324, 9_990_000),
VF4TestCase(12, 325, 10_000_000),
VF4TestCase(13, 299, 9_000_000),
VF4TestCase(13, 302, 9_100_000),
VF4TestCase(13, 305, 9_200_000),
VF4TestCase(13, 312, 9_300_000),
VF4TestCase(13, 315, 9_400_000),
VF4TestCase(13, 322, 9_500_000),
VF4TestCase(13, 325, 9_600_000),
VF4TestCase(13, 332, 9_700_000),
VF4TestCase(13, 334, 9_750_000),
VF4TestCase(13, 339, 9_800_000),
VF4TestCase(13, 341, 9_850_000),
VF4TestCase(13, 346, 9_900_000),
VF4TestCase(13, 348, 9_950_000),
VF4TestCase(13, 349, 9_990_000),
VF4TestCase(13, 350, 10_000_000),
VF4TestCase(14, 320, 9_000_000),
VF4TestCase(14, 324, 9_100_000),
VF4TestCase(14, 327, 9_200_000),
VF4TestCase(14, 334, 9_300_000),
VF4TestCase(14, 338, 9_400_000),
VF4TestCase(14, 345, 9_500_000),
VF4TestCase(14, 349, 9_600_000),
VF4TestCase(14, 356, 9_700_000),
VF4TestCase(14, 358, 9_750_000),
VF4TestCase(14, 363, 9_800_000),
VF4TestCase(14, 365, 9_850_000),
VF4TestCase(14, 371, 9_900_000),
VF4TestCase(14, 373, 9_950_000),
VF4TestCase(14, 374, 9_990_000),
VF4TestCase(14, 375, 10_000_000),
VF4TestCase(15, 342, 9_000_000),
VF4TestCase(15, 345, 9_100_000),
VF4TestCase(15, 349, 9_200_000),
VF4TestCase(15, 357, 9_300_000),
VF4TestCase(15, 360, 9_400_000),
VF4TestCase(15, 368, 9_500_000),
VF4TestCase(15, 372, 9_600_000),
VF4TestCase(15, 380, 9_700_000),
VF4TestCase(15, 382, 9_750_000),
VF4TestCase(15, 388, 9_800_000),
VF4TestCase(15, 390, 9_850_000),
VF4TestCase(15, 396, 9_900_000),
VF4TestCase(15, 398, 9_950_000),
VF4TestCase(15, 399, 9_990_000),
VF4TestCase(15, 400, 10_000_000),
VF4TestCase(16, 363, 9_000_000),
VF4TestCase(16, 367, 9_100_000),
VF4TestCase(16, 371, 9_200_000),
VF4TestCase(16, 379, 9_300_000),
VF4TestCase(16, 383, 9_400_000),
VF4TestCase(16, 391, 9_500_000),
VF4TestCase(16, 395, 9_600_000),
VF4TestCase(16, 404, 9_700_000),
VF4TestCase(16, 406, 9_750_000),
VF4TestCase(16, 412, 9_800_000),
VF4TestCase(16, 414, 9_850_000),
VF4TestCase(16, 420, 9_900_000),
VF4TestCase(16, 422, 9_950_000),
VF4TestCase(16, 424, 9_990_000),
VF4TestCase(16, 425, 10_000_000),
VF4TestCase(17, 384, 9_000_000),
VF4TestCase(17, 389, 9_100_000),
VF4TestCase(17, 393, 9_200_000),
VF4TestCase(17, 401, 9_300_000),
VF4TestCase(17, 406, 9_400_000),
VF4TestCase(17, 414, 9_500_000),
VF4TestCase(17, 419, 9_600_000),
VF4TestCase(17, 427, 9_700_000),
VF4TestCase(17, 429, 9_750_000),
VF4TestCase(17, 436, 9_800_000),
VF4TestCase(17, 438, 9_850_000),
VF4TestCase(17, 445, 9_900_000),
VF4TestCase(17, 447, 9_950_000),
VF4TestCase(17, 449, 9_990_000),
VF4TestCase(17, 450, 10_000_000),
VF4TestCase(18, 406, 9_000_000),
VF4TestCase(18, 410, 9_100_000),
VF4TestCase(18, 415, 9_200_000),
VF4TestCase(18, 424, 9_300_000),
VF4TestCase(18, 428, 9_400_000),
VF4TestCase(18, 437, 9_500_000),
VF4TestCase(18, 442, 9_600_000),
VF4TestCase(18, 451, 9_700_000),
VF4TestCase(18, 453, 9_750_000),
VF4TestCase(18, 460, 9_800_000),
VF4TestCase(18, 463, 9_850_000),
VF4TestCase(18, 470, 9_900_000),
VF4TestCase(18, 472, 9_950_000),
VF4TestCase(18, 474, 9_990_000),
VF4TestCase(18, 475, 10_000_000),
VF4TestCase(19, 427, 9_000_000),
VF4TestCase(19, 432, 9_100_000),
VF4TestCase(19, 437, 9_200_000),
VF4TestCase(19, 446, 9_300_000),
VF4TestCase(19, 451, 9_400_000),
VF4TestCase(19, 460, 9_500_000),
VF4TestCase(19, 465, 9_600_000),
VF4TestCase(19, 475, 9_700_000),
VF4TestCase(19, 477, 9_750_000),
VF4TestCase(19, 485, 9_800_000),
VF4TestCase(19, 487, 9_850_000),
VF4TestCase(19, 495, 9_900_000),
VF4TestCase(19, 497, 9_950_000),
VF4TestCase(19, 499, 9_990_000),
VF4TestCase(19, 500, 10_000_000),
VF4TestCase(20, 448, 9_000_000),
VF4TestCase(20, 453, 9_100_000),
VF4TestCase(20, 458, 9_200_000),
VF4TestCase(20, 468, 9_300_000),
VF4TestCase(20, 473, 9_400_000),
VF4TestCase(20, 483, 9_500_000),
VF4TestCase(20, 488, 9_600_000),
VF4TestCase(20, 499, 9_700_000),
VF4TestCase(20, 501, 9_750_000),
VF4TestCase(20, 509, 9_800_000),
VF4TestCase(20, 511, 9_850_000),
VF4TestCase(20, 519, 9_900_000),
VF4TestCase(20, 522, 9_950_000),
VF4TestCase(20, 524, 9_990_000),
VF4TestCase(20, 525, 10_000_000),
];
for (const data of VF4ExampleData) {
t.equal(
calculateVF4(data.score, data.level),
data.expectedVF4,
`${data.score} on a level ${data.level} chart should be worth ${data.expectedVF4} VF4.`
);
}
t.end();
});
t.test("VF4 Validation Tests", (t) => {
ThrowsToSnapshot(
t,
() => calculateVF4(10_000_001, 20),
"Should throw if score is greater than 10 million."
);
ThrowsToSnapshot(t, () => calculateVF4(-1, 20), "Should throw if score is negative.");
ThrowsToSnapshot(
t,
() => inverseVF4(9_000, 20),
"Should throw if the VF4 requested as an inversion is impossible."
);
t.end();
});
t.test("InverseVF4 Tests", (t) => {
// The test data I've nabbed from bemaniwiki picks cutoff points rather than
// the smallest possible value needed to get the vf4.
// I've reprocessed all of this data, though. Should be good.
const testData = [
{ vf4: 42, level: 1, expectedScore: 8936170 },
{ vf4: 43, level: 1, expectedScore: 9052632 },
{ vf4: 44, level: 1, expectedScore: 9263158 },
{ vf4: 45, level: 1, expectedScore: 9375000 },
{ vf4: 46, level: 1, expectedScore: 9500000 },
{ vf4: 47, level: 1, expectedScore: 9690722 },
{ vf4: 48, level: 1, expectedScore: 9795918 },
{ vf4: 49, level: 1, expectedScore: 9898990 },
{ vf4: 50, level: 1, expectedScore: 10000000 },
{ vf4: 64, level: 2, expectedScore: 9000000 },
{ vf4: 65, level: 2, expectedScore: 9122807 },
{ vf4: 66, level: 2, expectedScore: 9263158 },
{ vf4: 67, level: 2, expectedScore: 9305556 },
{ vf4: 69, level: 2, expectedScore: 9500000 },
{ vf4: 71, level: 2, expectedScore: 9700000 },
{ vf4: 71, level: 2, expectedScore: 9700000 },
{ vf4: 72, level: 2, expectedScore: 9795918 },
{ vf4: 73, level: 2, expectedScore: 9831650 },
{ vf4: 74, level: 2, expectedScore: 9900000 },
{ vf4: 75, level: 2, expectedScore: 10000000 },
{ vf4: 85, level: 3, expectedScore: 9000000 },
{ vf4: 86, level: 3, expectedScore: 9052632 },
{ vf4: 87, level: 3, expectedScore: 9157895 },
{ vf4: 89, level: 3, expectedScore: 9300000 },
{ vf4: 90, level: 3, expectedScore: 9375000 },
{ vf4: 92, level: 3, expectedScore: 9500000 },
{ vf4: 93, level: 3, expectedScore: 9587629 },
{ vf4: 95, level: 3, expectedScore: 9700000 },
{ vf4: 97, level: 3, expectedScore: 9800000 },
{ vf4: 97, level: 3, expectedScore: 9800000 },
{ vf4: 99, level: 3, expectedScore: 9900000 },
{ vf4: 100, level: 3, expectedScore: 10000000 },
{ vf4: 106, level: 4, expectedScore: 9000000 },
{ vf4: 108, level: 4, expectedScore: 9094737 },
{ vf4: 109, level: 4, expectedScore: 9178947 },
{ vf4: 111, level: 4, expectedScore: 9300000 },
{ vf4: 112, level: 4, expectedScore: 9333333 },
{ vf4: 115, level: 4, expectedScore: 9500000 },
{ vf4: 116, level: 4, expectedScore: 9567010 },
{ vf4: 118, level: 4, expectedScore: 9700000 },
{ vf4: 119, level: 4, expectedScore: 9714286 },
{ vf4: 121, level: 4, expectedScore: 9800000 },
{ vf4: 121, level: 4, expectedScore: 9800000 },
{ vf4: 123, level: 4, expectedScore: 9900000 },
{ vf4: 124, level: 4, expectedScore: 9920000 },
{ vf4: 125, level: 4, expectedScore: 10000000 },
{ vf4: 128, level: 5, expectedScore: 9000000 },
{ vf4: 129, level: 5, expectedScore: 9052632 },
{ vf4: 131, level: 5, expectedScore: 9192982 },
{ vf4: 133, level: 5, expectedScore: 9300000 },
{ vf4: 135, level: 5, expectedScore: 9375000 },
{ vf4: 138, level: 5, expectedScore: 9500000 },
{ vf4: 139, level: 5, expectedScore: 9553265 },
{ vf4: 142, level: 5, expectedScore: 9700000 },
{ vf4: 143, level: 5, expectedScore: 9727891 },
{ vf4: 145, level: 5, expectedScore: 9800000 },
{ vf4: 146, level: 5, expectedScore: 9831650 },
{ vf4: 148, level: 5, expectedScore: 9900000 },
{ vf4: 149, level: 5, expectedScore: 9933333 },
{ vf4: 150, level: 5, expectedScore: 10000000 },
{ vf4: 149, level: 6, expectedScore: 9000000 },
{ vf4: 151, level: 6, expectedScore: 9082707 },
{ vf4: 152, level: 6, expectedScore: 9142857 },
{ vf4: 156, level: 6, expectedScore: 9300000 },
{ vf4: 157, level: 6, expectedScore: 9345238 },
{ vf4: 161, level: 6, expectedScore: 9500000 },
{ vf4: 162, level: 6, expectedScore: 9543446 },
{ vf4: 166, level: 6, expectedScore: 9700000 },
{ vf4: 167, level: 6, expectedScore: 9737609 },
{ vf4: 169, level: 6, expectedScore: 9800000 },
{ vf4: 170, level: 6, expectedScore: 9812410 },
{ vf4: 173, level: 6, expectedScore: 9900000 },
{ vf4: 174, level: 6, expectedScore: 9942857 },
{ vf4: 175, level: 6, expectedScore: 10000000 },
{ vf4: 171, level: 7, expectedScore: 9000000 },
{ vf4: 172, level: 7, expectedScore: 9052632 },
{ vf4: 174, level: 7, expectedScore: 9157895 },
{ vf4: 178, level: 7, expectedScore: 9300000 },
{ vf4: 180, level: 7, expectedScore: 9375000 },
{ vf4: 184, level: 7, expectedScore: 9500000 },
{ vf4: 186, level: 7, expectedScore: 9587629 },
{ vf4: 190, level: 7, expectedScore: 9700000 },
{ vf4: 191, level: 7, expectedScore: 9744898 },
{ vf4: 194, level: 7, expectedScore: 9800000 },
{ vf4: 195, level: 7, expectedScore: 9848485 },
{ vf4: 198, level: 7, expectedScore: 9900000 },
{ vf4: 199, level: 7, expectedScore: 9950000 },
{ vf4: 200, level: 7, expectedScore: 10000000 },
{ vf4: 192, level: 8, expectedScore: 9000000 },
{ vf4: 194, level: 8, expectedScore: 9076023 },
{ vf4: 196, level: 8, expectedScore: 9169591 },
{ vf4: 200, level: 8, expectedScore: 9300000 },
{ vf4: 203, level: 8, expectedScore: 9398148 },
{ vf4: 207, level: 8, expectedScore: 9500000 },
{ vf4: 209, level: 8, expectedScore: 9576174 },
{ vf4: 213, level: 8, expectedScore: 9700000 },
{ vf4: 214, level: 8, expectedScore: 9705215 },
{ vf4: 218, level: 8, expectedScore: 9800000 },
{ vf4: 219, level: 8, expectedScore: 9831650 },
{ vf4: 222, level: 8, expectedScore: 9900000 },
{ vf4: 223, level: 8, expectedScore: 9911111 },
{ vf4: 224, level: 8, expectedScore: 9955556 },
{ vf4: 225, level: 8, expectedScore: 10000000 },
{ vf4: 213, level: 9, expectedScore: 9000000 },
{ vf4: 216, level: 9, expectedScore: 9094737 },
{ vf4: 218, level: 9, expectedScore: 9178947 },
{ vf4: 223, level: 9, expectedScore: 9300000 },
{ vf4: 225, level: 9, expectedScore: 9375000 },
{ vf4: 230, level: 9, expectedScore: 9500000 },
{ vf4: 232, level: 9, expectedScore: 9567010 },
{ vf4: 237, level: 9, expectedScore: 9700000 },
{ vf4: 238, level: 9, expectedScore: 9714286 },
{ vf4: 242, level: 9, expectedScore: 9800000 },
{ vf4: 243, level: 9, expectedScore: 9818182 },
{ vf4: 247, level: 9, expectedScore: 9900000 },
{ vf4: 248, level: 9, expectedScore: 9920000 },
{ vf4: 249, level: 9, expectedScore: 9960000 },
{ vf4: 250, level: 9, expectedScore: 10000000 },
{ vf4: 235, level: 10, expectedScore: 9000000 },
{ vf4: 237, level: 10, expectedScore: 9071770 },
{ vf4: 240, level: 10, expectedScore: 9186603 },
{ vf4: 245, level: 10, expectedScore: 9300000 },
{ vf4: 248, level: 10, expectedScore: 9393939 },
{ vf4: 253, level: 10, expectedScore: 9500000 },
{ vf4: 256, level: 10, expectedScore: 9597001 },
{ vf4: 261, level: 10, expectedScore: 9700000 },
{ vf4: 262, level: 10, expectedScore: 9721707 },
{ vf4: 266, level: 10, expectedScore: 9800000 },
{ vf4: 268, level: 10, expectedScore: 9843893 },
{ vf4: 272, level: 10, expectedScore: 9900000 },
{ vf4: 273, level: 10, expectedScore: 9927273 },
{ vf4: 274, level: 10, expectedScore: 9963636 },
{ vf4: 275, level: 10, expectedScore: 10000000 },
{ vf4: 256, level: 11, expectedScore: 9000000 },
{ vf4: 259, level: 11, expectedScore: 9087719 },
{ vf4: 262, level: 11, expectedScore: 9192982 },
{ vf4: 267, level: 11, expectedScore: 9300000 },
{ vf4: 270, level: 11, expectedScore: 9375000 },
{ vf4: 276, level: 11, expectedScore: 9500000 },
{ vf4: 279, level: 11, expectedScore: 9587629 },
{ vf4: 285, level: 11, expectedScore: 9700000 },
{ vf4: 286, level: 11, expectedScore: 9727891 },
{ vf4: 291, level: 11, expectedScore: 9800000 },
{ vf4: 292, level: 11, expectedScore: 9831650 },
{ vf4: 297, level: 11, expectedScore: 9900000 },
{ vf4: 298, level: 11, expectedScore: 9933333 },
{ vf4: 299, level: 11, expectedScore: 9966667 },
{ vf4: 300, level: 11, expectedScore: 10000000 },
{ vf4: 277, level: 12, expectedScore: 9000000 },
{ vf4: 280, level: 12, expectedScore: 9068826 },
{ vf4: 284, level: 12, expectedScore: 9198381 },
{ vf4: 290, level: 12, expectedScore: 9300000 },
{ vf4: 293, level: 12, expectedScore: 9391026 },
{ vf4: 299, level: 12, expectedScore: 9500000 },
{ vf4: 302, level: 12, expectedScore: 9579699 },
{ vf4: 308, level: 12, expectedScore: 9700000 },
{ vf4: 310, level: 12, expectedScore: 9733124 },
{ vf4: 315, level: 12, expectedScore: 9800000 },
{ vf4: 316, level: 12, expectedScore: 9821290 },
{ vf4: 321, level: 12, expectedScore: 9900000 },
{ vf4: 323, level: 12, expectedScore: 9938462 },
{ vf4: 324, level: 12, expectedScore: 9969231 },
{ vf4: 325, level: 12, expectedScore: 10000000 },
{ vf4: 299, level: 13, expectedScore: 9000000 },
{ vf4: 302, level: 13, expectedScore: 9082707 },
{ vf4: 305, level: 13, expectedScore: 9172932 },
{ vf4: 312, level: 13, expectedScore: 9300000 },
{ vf4: 315, level: 13, expectedScore: 9375000 },
{ vf4: 322, level: 13, expectedScore: 9500000 },
{ vf4: 325, level: 13, expectedScore: 9572901 },
{ vf4: 332, level: 13, expectedScore: 9700000 },
{ vf4: 334, level: 13, expectedScore: 9737609 },
{ vf4: 339, level: 13, expectedScore: 9800000 },
{ vf4: 341, level: 13, expectedScore: 9841270 },
{ vf4: 346, level: 13, expectedScore: 9900000 },
{ vf4: 348, level: 13, expectedScore: 9942857 },
{ vf4: 349, level: 13, expectedScore: 9971429 },
{ vf4: 350, level: 13, expectedScore: 10000000 },
{ vf4: 320, level: 14, expectedScore: 9000000 },
{ vf4: 324, level: 14, expectedScore: 9094737 },
{ vf4: 327, level: 14, expectedScore: 9178947 },
{ vf4: 334, level: 14, expectedScore: 9300000 },
{ vf4: 338, level: 14, expectedScore: 9388889 },
{ vf4: 345, level: 14, expectedScore: 9500000 },
{ vf4: 349, level: 14, expectedScore: 9594502 },
{ vf4: 356, level: 14, expectedScore: 9700000 },
{ vf4: 358, level: 14, expectedScore: 9741497 },
{ vf4: 363, level: 14, expectedScore: 9800000 },
{ vf4: 365, level: 14, expectedScore: 9831650 },
{ vf4: 371, level: 14, expectedScore: 9900000 },
{ vf4: 373, level: 14, expectedScore: 9946667 },
{ vf4: 374, level: 14, expectedScore: 9973333 },
{ vf4: 375, level: 14, expectedScore: 10000000 },
{ vf4: 342, level: 15, expectedScore: 9000000 },
{ vf4: 345, level: 15, expectedScore: 9078947 },
{ vf4: 349, level: 15, expectedScore: 9184211 },
{ vf4: 357, level: 15, expectedScore: 9300000 },
{ vf4: 360, level: 15, expectedScore: 9375000 },
{ vf4: 368, level: 15, expectedScore: 9500000 },
{ vf4: 372, level: 15, expectedScore: 9587629 },
{ vf4: 380, level: 15, expectedScore: 9700000 },
{ vf4: 382, level: 15, expectedScore: 9744898 },
{ vf4: 388, level: 15, expectedScore: 9800000 },
{ vf4: 390, level: 15, expectedScore: 9848485 },
{ vf4: 396, level: 15, expectedScore: 9900000 },
{ vf4: 398, level: 15, expectedScore: 9950000 },
{ vf4: 399, level: 15, expectedScore: 9975000 },
{ vf4: 400, level: 15, expectedScore: 10000000 },
{ vf4: 363, level: 16, expectedScore: 9000000 },
{ vf4: 367, level: 16, expectedScore: 9089783 },
{ vf4: 371, level: 16, expectedScore: 9188854 },
{ vf4: 379, level: 16, expectedScore: 9300000 },
{ vf4: 383, level: 16, expectedScore: 9387255 },
{ vf4: 391, level: 16, expectedScore: 9500000 },
{ vf4: 395, level: 16, expectedScore: 9581565 },
{ vf4: 404, level: 16, expectedScore: 9700000 },
{ vf4: 406, level: 16, expectedScore: 9747899 },
{ vf4: 412, level: 16, expectedScore: 9800000 },
{ vf4: 414, level: 16, expectedScore: 9839572 },
{ vf4: 420, level: 16, expectedScore: 9900000 },
{ vf4: 422, level: 16, expectedScore: 9929412 },
{ vf4: 424, level: 16, expectedScore: 9976471 },
{ vf4: 425, level: 16, expectedScore: 10000000 },
{ vf4: 384, level: 17, expectedScore: 9000000 },
{ vf4: 389, level: 17, expectedScore: 9099415 },
{ vf4: 393, level: 17, expectedScore: 9192982 },
{ vf4: 401, level: 17, expectedScore: 9300000 },
{ vf4: 406, level: 17, expectedScore: 9398148 },
{ vf4: 414, level: 17, expectedScore: 9500000 },
{ vf4: 419, level: 17, expectedScore: 9599084 },
{ vf4: 427, level: 17, expectedScore: 9700000 },
{ vf4: 429, level: 17, expectedScore: 9727891 },
{ vf4: 436, level: 17, expectedScore: 9800000 },
{ vf4: 438, level: 17, expectedScore: 9831650 },
{ vf4: 445, level: 17, expectedScore: 9900000 },
{ vf4: 447, level: 17, expectedScore: 9933333 },
{ vf4: 449, level: 17, expectedScore: 9977778 },
{ vf4: 450, level: 17, expectedScore: 10000000 },
{ vf4: 406, level: 18, expectedScore: 9000000 },
{ vf4: 410, level: 18, expectedScore: 9085873 },
{ vf4: 415, level: 18, expectedScore: 9196676 },
{ vf4: 424, level: 18, expectedScore: 9300000 },
{ vf4: 428, level: 18, expectedScore: 9385965 },
{ vf4: 437, level: 18, expectedScore: 9500000 },
{ vf4: 442, level: 18, expectedScore: 9593055 },
{ vf4: 451, level: 18, expectedScore: 9700000 },
{ vf4: 453, level: 18, expectedScore: 9731472 },
{ vf4: 460, level: 18, expectedScore: 9800000 },
{ vf4: 463, level: 18, expectedScore: 9845827 },
{ vf4: 470, level: 18, expectedScore: 9900000 },
{ vf4: 472, level: 18, expectedScore: 9936842 },
{ vf4: 474, level: 18, expectedScore: 9978947 },
{ vf4: 475, level: 18, expectedScore: 10000000 },
{ vf4: 427, level: 19, expectedScore: 9000000 },
{ vf4: 432, level: 19, expectedScore: 9094737 },
{ vf4: 437, level: 19, expectedScore: 9200000 },
{ vf4: 446, level: 19, expectedScore: 9300000 },
{ vf4: 451, level: 19, expectedScore: 9395833 },
{ vf4: 460, level: 19, expectedScore: 9500000 },
{ vf4: 465, level: 19, expectedScore: 9587629 },
{ vf4: 475, level: 19, expectedScore: 9700000 },
{ vf4: 477, level: 19, expectedScore: 9734694 },
{ vf4: 485, level: 19, expectedScore: 9800000 },
{ vf4: 487, level: 19, expectedScore: 9838384 },
{ vf4: 495, level: 19, expectedScore: 9900000 },
{ vf4: 497, level: 19, expectedScore: 9940000 },
{ vf4: 499, level: 19, expectedScore: 9980000 },
{ vf4: 500, level: 19, expectedScore: 10000000 },
{ vf4: 448, level: 20, expectedScore: 9000000 },
{ vf4: 453, level: 20, expectedScore: 9082707 },
{ vf4: 458, level: 20, expectedScore: 9182957 },
{ vf4: 468, level: 20, expectedScore: 9300000 },
{ vf4: 473, level: 20, expectedScore: 9384921 },
{ vf4: 483, level: 20, expectedScore: 9500000 },
{ vf4: 488, level: 20, expectedScore: 9582720 },
{ vf4: 499, level: 20, expectedScore: 9700000 },
{ vf4: 501, level: 20, expectedScore: 9737609 },
{ vf4: 509, level: 20, expectedScore: 9800000 },
{ vf4: 511, level: 20, expectedScore: 9831650 },
{ vf4: 519, level: 20, expectedScore: 9900000 },
{ vf4: 522, level: 20, expectedScore: 9942857 },
{ vf4: 524, level: 20, expectedScore: 9980952 },
{ vf4: 525, level: 20, expectedScore: 10000000 },
];
for (const data of testData) {
t.equal(
inverseVF4(data.vf4, data.level),
data.expectedScore,
`${data.vf4} VF4 on a level ${data.level} chart should be given by ${data.expectedScore} score.`
);
}
t.end();
});
t.test("VF5 Tests", (t) => {
function VF5TestCase(
level: number,
score: number,
lamp: SDVXLamps,
expectedVF5: number
): TestCase {
return (t) => {
const vf5 = calculateVF5(score, lamp, level);
t.equal(
vf5,
expectedVF5,
`${score} on a level ${level} chart with lamp ${lamp} should be worth ${expectedVF5} VF5. (Got ${vf5})`
);
};
}
// No test data from bemaniwiki this time, so I just tore out some examples
// myself.
const VF5ExampleData = [
VF5TestCase(20, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.46),
VF5TestCase(19, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.43),
VF5TestCase(18, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.41),
VF5TestCase(17, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.39),
VF5TestCase(16, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.36),
VF5TestCase(15, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.34),
VF5TestCase(14, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.32),
VF5TestCase(13, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.3),
VF5TestCase(12, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.27),
VF5TestCase(11, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.25),
VF5TestCase(10, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.23),
VF5TestCase(9, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.2),
VF5TestCase(8, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.18),
VF5TestCase(7, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.16),
VF5TestCase(6, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.13),
VF5TestCase(5, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.11),
VF5TestCase(4, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.09),
VF5TestCase(3, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.06),
VF5TestCase(2, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.04),
VF5TestCase(1, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.02),
VF5TestCase(18, 9_700_000, "ULTIMATE CHAIN", 0.36),
VF5TestCase(13, 9_700_000, "ULTIMATE CHAIN", 0.26),
VF5TestCase(19, 9_500_000, "EXCESSIVE CLEAR", 0.35),
VF5TestCase(19, 9_500_000, "CLEAR", 0.35),
VF5TestCase(19, 9_500_000, "FAILED", 0.17),
// The library used to return 0.28 for this case because of FloorToNDP(x, 2):
// 0.29 * 100 = 28.999999999999996
VF5TestCase(16, 9_500_000, "CLEAR", 0.29),
];
for (const testCase of VF5ExampleData) {
testCase(t);
}
t.end();
});
t.test("InverseVF5 Tests", (t) => {
function InvVF5TestCase(
level: number,
vf5: number,
lamp: Exclude<SDVXLamps, "PERFECT ULTIMATE CHAIN">,
expectedScore: number
): TestCase {
return (t) => {
const score = inverseVF5(vf5, lamp, level);
t.equal(
score,
expectedScore,
`${vf5} VF5 on a level ${level} chart with lamp ${lamp} should be inverted to ${expectedScore}. (Got ${score})`
);
};
}
const testCases = [
InvVF5TestCase(20, 0.4, "CLEAR", 9_803_922),
InvVF5TestCase(20, 0.4, "EXCESSIVE CLEAR", 9_800_000),
InvVF5TestCase(13, 0.12, "CLEAR", 5_769_231),
InvVF5TestCase(13, 0.12, "FAILED", 9_516_257),
InvVF5TestCase(18, 0.31, "ULTIMATE CHAIN", 9_012_152),
];
for (const testCase of testCases) {
testCase(t);
}
ThrowsToSnapshot(
t,
() => inverseVF5(900, "FAILED", 1),
"Should throw if the volforce is impossible to achieve with this lamp/level."
);
t.end();
});
t.test("VF6 Tests", (t) => {
function VF6TestCase(
level: number,
score: number,
lamp: SDVXLamps,
expectedVF6: number
): TestCase {
return (t) => {
const vf6 = calculateVF6(score, lamp, level);
t.equal(
vf6,
expectedVF6,
`${score} on a level ${level} chart with lamp ${lamp} should be worth ${expectedVF6} VF6. (Got ${vf6})`
);
};
}
// No test data from bemaniwiki again.
const VF5ExampleData = [
VF6TestCase(20, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.462),
VF6TestCase(19, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.438),
VF6TestCase(18, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.415),
VF6TestCase(17, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.392),
VF6TestCase(16, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.369),
VF6TestCase(15, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.346),
VF6TestCase(14, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.323),
VF6TestCase(13, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.3),
VF6TestCase(12, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.277),
VF6TestCase(11, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.254),
VF6TestCase(10, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.231),
VF6TestCase(9, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.207),
VF6TestCase(8, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.184),
VF6TestCase(7, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.161),
VF6TestCase(6, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.138),
VF6TestCase(5, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.115),
VF6TestCase(4, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.092),
VF6TestCase(3, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.069),
VF6TestCase(2, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.046),
VF6TestCase(1, 10_000_000, "PERFECT ULTIMATE CHAIN", 0.023),
VF6TestCase(18, 9_700_000, "ULTIMATE CHAIN", 0.366),
VF6TestCase(13, 9_700_000, "ULTIMATE CHAIN", 0.264),
// note: You can see the point of VF6 quite clearly here as EXC actually
// differentiates Volforce values here -- unlike in VF5 where these would be
// marked as the same.
VF6TestCase(19, 9_500_000, "EXCESSIVE CLEAR", 0.357),
VF6TestCase(18, 9_923_042, "MAXXIVE CLEAR", 0.390),
VF6TestCase(19, 9_500_000, "CLEAR", 0.35),
VF6TestCase(19, 9_500_000, "FAILED", 0.175),
VF6TestCase(20, 5_000_000, "FAILED", 0.08),
VF6TestCase(20, 7_000_000, "FAILED", 0.114),
VF6TestCase(20, 8_000_000, "FAILED", 0.136),
VF6TestCase(20, 8_700_000, "FAILED", 0.153),
];
for (const testCase of VF5ExampleData) {
testCase(t);
}
t.end();
});
t.test("InverseVF6 Tests", (t) => {
function InvVF6TestCase(
level: number,
vf6: number,
lamp: Exclude<SDVXLamps, "PERFECT ULTIMATE CHAIN">,
expectedScore: number
): TestCase {
return (t) => {
const score = inverseVF6(vf6, lamp, level);
t.equal(
score,
expectedScore,
`${vf6} VF6 on a level ${level} chart with lamp ${lamp} should be inverted to ${expectedScore}. (Got ${score})`
);
};
}
const testCases = [
InvVF6TestCase(20, 0.413, "CLEAR", 9_900_000),
InvVF6TestCase(20, 0.232, "MAXXIVE CLEAR", 6_971_154),
InvVF6TestCase(20, 0.421, "EXCESSIVE CLEAR", 9_900_000),
InvVF6TestCase(13, 0.121, "CLEAR", 5_817_308),
InvVF6TestCase(13, 0.121, "FAILED", 9_595_559),
InvVF6TestCase(18, 0.31, "ULTIMATE CHAIN", 9_012_152),
];
for (const testCase of testCases) {
testCase(t);
}
ThrowsToSnapshot(
t,
() => inverseVF6(900, "FAILED", 1),
"Should throw if the volforce is impossible to achieve with this lamp/level."
);
t.end();
});