41 lines
1.6 KiB
TypeScript
41 lines
1.6 KiB
TypeScript
import type { NoteScorePoint } from './types.ts';
|
|
|
|
export type ScorePoint = {
|
|
timeSeconds: number;
|
|
averageScore: number;
|
|
maximumScore: number;
|
|
maximumProbability?: number;
|
|
};
|
|
|
|
export function buildScorePoints(notes: readonly NoteScorePoint[], probabilities: readonly number[]): ScorePoint[] {
|
|
let averageScore = 0;
|
|
let maximumScore = 0;
|
|
return [{ timeSeconds: 0, averageScore: 0, maximumScore: 0, maximumProbability: 1 }, ...notes.map((note, index) => {
|
|
averageScore += note.expectedScore;
|
|
maximumScore += note.theoreticalMaxScore;
|
|
return { timeSeconds: note.timeSeconds, averageScore, maximumScore, maximumProbability: probabilities[index] };
|
|
})];
|
|
}
|
|
|
|
/** Include every simultaneous judgement, exactly as in the cumulative score graph. */
|
|
export function scorePointAtTime(points: readonly ScorePoint[], timeSeconds: number): ScorePoint {
|
|
let low = 0;
|
|
let high = points.length - 1;
|
|
while (low < high) {
|
|
const middle = Math.ceil((low + high) / 2);
|
|
if (points[middle].timeSeconds <= timeSeconds) low = middle;
|
|
else high = middle - 1;
|
|
}
|
|
return points[low] ?? { timeSeconds: 0, averageScore: 0, maximumScore: 0 };
|
|
}
|
|
|
|
export function formatTimelineScore(score: number): string {
|
|
return Math.round(score).toLocaleString('ko-KR');
|
|
}
|
|
|
|
export function formatTimelineProbability(probability: number | undefined): string {
|
|
if (probability === undefined) return '—';
|
|
if (probability > 0 && probability < 0.000001) return '<0.0001%';
|
|
return `${(probability * 100).toLocaleString('ko-KR', { maximumFractionDigits: probability < 0.0001 ? 4 : 2 })}%`;
|
|
}
|