feat: show cumulative maximum score probability in timeline tooltip
Publish GitHub Pages output / publish (push) Successful in 56s

This commit is contained in:
2026-09-10 14:40:07 +09:00
parent b1b99e6cc0
commit 1fa15cbe08
5 changed files with 88 additions and 13 deletions
+30
View File
@@ -1,6 +1,7 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { estimateScoreAchievementProbabilities } from './index.ts';
import { calculateMaximumScoreProbabilityTimeline } from './score-probability.ts';
import type { ExactSongChart, OptimizerCard } from './types.ts';
function card(
@@ -104,3 +105,32 @@ test('확정 발동이면 모든 달성 확률이 100%다', () => {
samples: 1_000
});
});
test('시간별 최고점 확률은 첫 영향 노트에서 감소하고 같은 발동을 중복 계산하지 않는다', () => {
const cards = [card('main', 100, 50, 0.9), card('weak', 20, 10)];
const source = chart([0.5, 1.1, 1.5, 1.95, 2.1, 2.1]);
const options = { perfectNoteScore: 100, sampleCount: 1 };
const probabilities = calculateMaximumScoreProbabilityTimeline(cards, {}, source, options);
// The weak skill alone raises the maximum at 1.95 seconds.
assert.deepEqual(probabilities, [1, 0.5, 0.5, 0.05, 0.025, 0.025]);
assert.equal(probabilities.at(-1), estimateScoreAchievementProbabilities(cards, {}, source, options).maximum);
assert.deepEqual(calculateMaximumScoreProbabilityTimeline(cards, {}, chart([]), options), []);
});
test('시간별 확률은 겹친 동률 액티브와 이후 필수 발동 사이의 의존성을 반영한다', () => {
const cards = [card('long', 100, 50, 0.9), card('short', 100, 50, 0.3)];
assert.deepEqual(calculateMaximumScoreProbabilityTimeline(cards, {}, chart([1.1, 1.5, 2.1]), {
perfectNoteScore: 100
}), [0.75, 0.5, 0.375]);
});
test('미발동 토글 시 현재 최고점 기준으로 확률을 계산하고 반올림으로 점수가 같으면 실패를 허용한다', () => {
const cards = [card('strong', 100, 50), card('weak', 20, 10)];
assert.deepEqual(calculateMaximumScoreProbabilityTimeline(cards, {}, chart([1.1, 2.1]), {
perfectNoteScore: 100,
excludedActiveWindows: [{ cardId: 'strong', startSeconds: 1 }]
}), [0.1, 0.05]);
const options = { perfectNoteScore: 0.1, sampleCount: 1 };
assert.deepEqual(calculateMaximumScoreProbabilityTimeline(cards, {}, chart([1.1]), options), [1]);
assert.equal(estimateScoreAchievementProbabilities(cards, {}, chart([1.1]), options).maximum, 1);
});