feat: simulate individual active skill failures in analysis
Publish GitHub Pages output / publish (push) Successful in 45s

This commit is contained in:
2026-09-08 12:46:00 +09:00
parent dad8b2c53a
commit 795bf47e80
7 changed files with 126 additions and 24 deletions
+33 -1
View File
@@ -1,7 +1,7 @@
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 { buildExactSkillWindows, calculateExactScore, perfectNoteScoreFromPower, scoreRatioFromNotes } from './index.ts';
import type { ExactSongChart, OptimizerCard } from './types.ts';
const inactiveCard: OptimizerCard = {
@@ -121,3 +121,35 @@ test('등록된 실측 ratio가 모든 1노트 측정값을 재현한다', () =>
}
}
});
test('지정한 액티브만 실패 처리하고 겹친 다른 카드와 다음 발동은 유지한다', () => {
const strong: OptimizerCard = {
...inactiveCard, id: 'strong',
active: { cooldownSeconds: 10, durationSeconds: 3, scoreUpPercent: 100, probabilityPercent: 50 }
};
const weak: OptimizerCard = {
...inactiveCard, id: 'weak',
active: { cooldownSeconds: 10, durationSeconds: 3, scoreUpPercent: 50, probabilityPercent: 100 }
};
const interval = 10 / 1.04;
const song = chart([interval - 0.01, interval, interval + 3, interval * 2].map((timeSeconds) => ({ timeSeconds, type: 'regular' as const, isFlick: false })));
const reductions = { strong: 4 as const, weak: 4 as const };
const cards = [strong, weak];
const original = calculateExactScore(cards, reductions, song, { perfectNoteScore: 100 });
const excludedActiveWindows = [{ cardId: 'strong', startSeconds: interval }];
const modified = calculateExactScore(cards, reductions, song, { perfectNoteScore: 100, excludedActiveWindows });
assert.deepEqual(modified.notes.map((note) => note.theoreticalMaxScore), [100, 150, 100, 200]);
assert.deepEqual(modified.notes.map((note) => note.expectedScore), [100, 150, 100, 175]);
assert.equal(modified.baselineScore, original.baselineScore);
assert.equal(modified.scoreBySecond[9], 250);
assert.deepEqual(calculateExactScore(cards, reductions, song, { perfectNoteScore: 100, excludedActiveWindows: [] }), original);
const windows = buildExactSkillWindows(cards, reductions, song, excludedActiveWindows);
assert.equal(windows.effectiveActiveWindows.find((window) => window.startSeconds === interval)?.cardId, 'weak');
assert.equal(windows.effectiveActiveWindows.find((window) => window.startSeconds === interval * 2)?.cardId, 'strong');
assert.ok(windows.activeWindows.some((window) => window.cardId === 'strong' && window.startSeconds === interval));
const bothExcluded = [...excludedActiveWindows, { cardId: 'weak', startSeconds: interval }];
const noActive = calculateExactScore(cards, reductions, song, { perfectNoteScore: 100, excludedActiveWindows: bothExcluded });
assert.equal(noActive.notes[1].theoreticalMaxScore, noActive.notes[1].baselineScore);
assert.ok(!buildExactSkillWindows(cards, reductions, song, bothExcluded).effectiveActiveWindows.some((window) => window.startSeconds === interval));
});