Files
HolodoriCalc/src/lib/engine/score-timeline.ts
T
dal4segno 049e37bcff
Publish GitHub Pages output / publish (push) Successful in 47s
feat: show playable charts with score hover and special-start markers
2026-09-29 23:12:18 +09:00

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 })}%`;
}