import assert from 'node:assert/strict'; import test from 'node:test'; import scoreCalibrations from '../../../data/score-calibrations.json' with { type: 'json' }; import { calculateExactScore, perfectNoteScoreFromPower, scoreRatioFromNotes } from './index.ts'; import type { ExactSongChart, OptimizerCard } from './types.ts'; const inactiveCard: OptimizerCard = { id: 'card', name: '카드', active: { cooldownSeconds: 999, durationSeconds: 1, scoreUpPercent: 100, probabilityPercent: 100 }, special: { durationSeconds: 0, scoreUpPercent: 0 } }; function chart(notes: ExactSongChart['notes']): ExactSongChart { return { songId: 'test', difficulty: 'expert', durationSeconds: 120, scoreRatio: 100, notes, specialStartSeconds: [] }; } test('종합력과 악곡 비율로 PERFECT 1노트 기본점수를 계산한다', () => { assert.equal(perfectNoteScoreFromPower(120_000, 200), 600); assert.equal(perfectNoteScoreFromPower(120_100, 200), 600.5); assert.equal(perfectNoteScoreFromPower(0, 200, 777), 777); }); test('플릭 1.05배와 홀드 중간 판정 각각의 가중치로 환산비율을 계산한다', () => { const ratio = scoreRatioFromNotes([ { timeSeconds: 1, type: 'regular', isFlick: false }, { timeSeconds: 2, type: 'regular', isFlick: true }, { timeSeconds: 3, type: 'mid', isFlick: false }, { timeSeconds: 3, type: 'mid', isFlick: false } ]); assert.ok(Math.abs(ratio - 2.25 * 0.3713416563352296) < 1e-12); }); test('기본점수의 소수점을 유지하고 가창 보너스 적용 후 노트별로 올림한다', () => { const result = calculateExactScore([inactiveCard], {}, chart([ { timeSeconds: 1, type: 'regular', isFlick: false } ]), { perfectNoteScore: 1_354.507, directSongScoreBonusPercent: 10 }); assert.equal(result.baselineScore, 1_490); }); test('100콤보부터 1% 콤보 보너스를 적용한다', () => { const notes = Array.from({ length: 100 }, (_, index) => ({ timeSeconds: index, type: 'regular' as const, isFlick: false })); const result = calculateExactScore([inactiveCard], {}, chart(notes), { perfectNoteScore: 600 }); assert.equal(result.baselineScore, 99 * 600 + 606); }); test('홀드 중간 노트는 10%, 플릭은 1.05배로 계산한다', () => { const result = calculateExactScore([inactiveCard], {}, chart([ { timeSeconds: 1, type: 'mid', isFlick: false }, { timeSeconds: 2, type: 'regular', isFlick: true } ]), { perfectNoteScore: 600 }); assert.equal(result.baselineScore, 60 + 630); }); test('같은 시각의 여러 홀드 중간 판정을 각각 계산한다', () => { const result = calculateExactScore([inactiveCard], {}, chart([ { timeSeconds: 1, type: 'mid', isFlick: false }, { timeSeconds: 1, type: 'mid', isFlick: false } ]), { perfectNoteScore: 600 }); assert.equal(result.baselineScore, 120); assert.equal(result.notes.length, 2); }); test('액티브 발동 확률과 스페셜 스코어 서포트를 기대 점수에 반영한다', () => { const card: OptimizerCard = { id: 'score', name: '스코어 카드', active: { cooldownSeconds: 1, durationSeconds: 1, scoreUpPercent: 100, probabilityPercent: 50 }, special: { durationSeconds: 2, scoreUpPercent: 100 } }; const result = calculateExactScore([card], {}, { ...chart([{ timeSeconds: 1, type: 'regular', isFlick: false }]), specialStartSeconds: [0] }, { perfectNoteScore: 600 }); assert.equal(result.expectedScore, 1_200); assert.equal(result.theoreticalMaxScore, 1_800); }); test('블루 보드와 스페셜의 발동률 증가 배율을 합산해 기대 점수에 반영한다', () => { const main: OptimizerCard = { id: 'main', name: '메인', active: { cooldownSeconds: 1, durationSeconds: 1, scoreUpPercent: 100, probabilityPercent: 55, activationRateUpPercent: 30 }, special: { durationSeconds: 0, scoreUpPercent: 0 } }; const support: OptimizerCard = { id: 'support', name: '서포트', active: { cooldownSeconds: 999, durationSeconds: 0, scoreUpPercent: 0, probabilityPercent: 0 }, special: { durationSeconds: 3, scoreUpPercent: 0, activationRateUpPercent: 40 } }; const result = calculateExactScore([main, support], {}, { ...chart([{ timeSeconds: 1, type: 'regular', isFlick: false }]), specialStartSeconds: [null, 0] }, { perfectNoteScore: 100 }); assert.equal(result.expectedScore, 194); assert.equal(result.theoreticalMaxScore, 200); }); test('등록된 실측 ratio가 모든 1노트 측정값을 재현한다', () => { for (const calibration of scoreCalibrations.charts) { for (const measurement of calibration.measurements) { const result = calculateExactScore([inactiveCard], {}, chart([ { timeSeconds: 1, type: 'regular', isFlick: false } ]), { perfectNoteScore: measurement.overallPower / calibration.scoreRatio, directSongScoreBonusPercent: measurement.directSongScoreBonusPercent }); assert.equal( result.baselineScore, measurement.normalPerfectScore, `${calibration.songId} ${calibration.difficulty}` ); } } });