28 lines
1.8 KiB
JavaScript
28 lines
1.8 KiB
JavaScript
import { readFile } from 'node:fs/promises';
|
|
import { scoreRatioFromFormula } from '../src/lib/engine/score.ts';
|
|
|
|
// Pinned research snapshot, with source URL, retrieval time and SHA-256.
|
|
const reference = JSON.parse(await readFile(new URL('../docs/research/song-score-formula-reference.json', import.meta.url), 'utf8'));
|
|
const susReference = JSON.parse(await readFile(new URL('../docs/research/sus-score-formulas.json', import.meta.url), 'utf8'));
|
|
// Newly recovered charts carry their full reproducibility record in validationCharts until
|
|
// the next research snapshot is consolidated.
|
|
const rows = [...reference.charts, ...susReference.charts, ...susReference.validationCharts.filter((row) => row.fullComboNoteCount)];
|
|
const formulas = new Map(rows.map((row) => [`${row.songId}.${row.difficulty}`, row]));
|
|
if (formulas.size !== rows.length) throw new Error('중복 악곡 점수 공식입니다.');
|
|
|
|
export function formulaScoreMetadata(songId, difficulty, difficultyLevel, totalNotes, liveScoreCoefficientPermil, susSha256) {
|
|
const row = formulas.get(`${songId}.${difficulty.toLowerCase()}`);
|
|
if (!row) return null;
|
|
if (row.susSha256 && row.susSha256 !== susSha256) throw new Error(`${songId}.${difficulty}: 원본 SUS 해시가 다릅니다.`);
|
|
if (row.difficultyLevel !== difficultyLevel || row.fullComboNoteCount !== totalNotes ||
|
|
(liveScoreCoefficientPermil !== undefined && row.liveScoreCoefficientPermil !== liveScoreCoefficientPermil)) {
|
|
throw new Error(`${songId}.${difficulty}: 채보 또는 마스터 데이터가 점수 공식 참고값과 다릅니다.`);
|
|
}
|
|
const scoreFormula = {
|
|
perfectNoteWeight: row.perfectNoteWeight,
|
|
liveScoreCoefficientPermil: row.liveScoreCoefficientPermil,
|
|
difficultyLevel: row.difficultyLevel
|
|
};
|
|
return { scoreRatio: scoreRatioFromFormula(scoreFormula), scoreCalibrationSource: 'formula-v1', scoreFormula };
|
|
}
|