rg-stats
Version:
A library for calculating various rhythm game stats.
96 lines • 4.68 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.calculateFromBPMPerMeasure = exports.calculateFromNPSPerMeasure = void 0;
const throw_if_1 = require("../util/throw-if");
/**
* Given an array of notes per measure, NPS per measure and (optionally) when the user died
* calculate the fastest series of unbroken '16ths' at N measures.
*
* 32 @ 190 will return 190,
* 96 @ 190, user died at m40 is converted to 40 @ 190 and will return 190.
*
* 31 @ 190 will return null,
* 32 @ 200 / 32 @ 220 will return 220.
* 31 @ 200 into 1 @ 50 will return 50. (assuming no break between the streams)
*
* @param npsPerMeasure - The notes per second per measure.
* @param notesPerMeasure - The notes per measure.
* @param diedAt - Optionally, when to cut this short, such as if the user died at measure
* 39, and still might've technically did 32 measures of stream.
* @param measures - Optionally, override how many measures need to be unbroken. This must
* be greater than 1. This allows you to calculate Highest 256, or similar.
*
* @returns The BPM of the highest N unbroken measures in this chart.
*/
function calculateFromNPSPerMeasure(npsPerMeasure, notesPerMeasure, diedAt = null, measures = 32) {
// Implementation note: Measures of 1 is annoying to handle, and nobody should
// ever want to calculate that because it's stupid. Just gonna not allow it.
(0, throw_if_1.ThrowIf)(measures <= 1, `Measures must be greater than 1`, { measures });
(0, throw_if_1.ThrowIf)(npsPerMeasure.length !== notesPerMeasure.length, `Notes Per Measure and NPS Per Measure didn't have the same length.`, { npsLen: npsPerMeasure.length, notesLen: notesPerMeasure.length });
(0, throw_if_1.ThrowIf)(diedAt !== null && diedAt < 0, `DiedAt must be positive.`, {
diedAt: diedAt ?? "Null? (Impossible)",
});
// Given an array of equivalent bpms per measure, return the fastest bpm
// this user could've effectively streamed N measures of.
let candidates = [];
let highestUnbroken = null;
const measureData = npsPerMeasure.map((e, i) => [e, notesPerMeasure[i]]);
// If the user died at an earlier measure, only iterate through those measures.
for (let curMeasureIdx = 0; curMeasureIdx < (diedAt ?? measureData.length); curMeasureIdx++) {
const [nps, notes] = measureData[curMeasureIdx];
// if any measure we're in stops doing anything, this is a break.
// lerping over this sucks and is counterintuitive, so we're not going to.
if (notes === 0) {
candidates = [];
}
const unexhaustedCandidates = [];
for (const candidate of candidates) {
candidate.npsWindow.push(nps);
candidate.notesWindow.push(notes);
// If this window contains more than (16 * measures) notes
// it meets the criteria for fastest unbroken of 16ths for N measures.
if (candidate.notesWindow.reduce((a, e) => a + e, 0) >= measures * 16) {
const lowestNPSInWindow = candidate.npsWindow.slice(0).sort((a, b) => a - b)[0];
// if highestUnbroken is null, this is our best shot
if (highestUnbroken === null || lowestNPSInWindow > highestUnbroken) {
highestUnbroken = lowestNPSInWindow;
}
}
else {
unexhaustedCandidates.push(candidate);
}
}
// Only keep candidates who's window doesn't already subsume 512 notes.
candidates = unexhaustedCandidates;
candidates.push({
// for debugging
windowStart: curMeasureIdx,
npsWindow: [nps],
notesWindow: [notes],
});
}
if (highestUnbroken === null) {
return null;
}
return npsToBPM(highestUnbroken);
}
exports.calculateFromNPSPerMeasure = calculateFromNPSPerMeasure;
/**
* Given an array of bpms per measure instead of nps per measure, calculate the fastest
* N measures of unbroken streams played. This is a thin wrapper around the NPS/Measure
* calculator, used for humanising input.
*
* {@see calculateFromNPSPerMeasure}
*/
function calculateFromBPMPerMeasure(bpmPerMeasure, notesPerMeasure, diedAt, measures) {
return calculateFromNPSPerMeasure(bpmPerMeasure.map(bpmToNPS), notesPerMeasure, diedAt, measures);
}
exports.calculateFromBPMPerMeasure = calculateFromBPMPerMeasure;
// Always assume 4/4 time. I know it sucks, but stepmania does the same.
function npsToBPM(nps) {
return (nps * 60) / 4;
}
function bpmToNPS(bpm) {
return (bpm * 4) / 60;
}
//# sourceMappingURL=itg-highest-unbroken.js.map