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