feat: improve analysis and release v1.0.2
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/// <reference lib="webworker" />
|
||||
|
||||
import { countOptimizationCandidates, optimizeExactOrderAndCooldowns } from '../engine/index.ts';
|
||||
import type { ExactOptimizationResult, ExactOptimizeOptions, ExactSongChart, OptimizerCard } from '../engine/index.ts';
|
||||
import { countOptimizationCandidates, estimateScoreAchievementProbabilities, optimizeExactOrderAndCooldowns } from '../engine/index.ts';
|
||||
import type { ExactOptimizationResult, ExactOptimizeOptions, ExactSongChart, OptimizerCard, ScoreAchievementProbabilities } from '../engine/index.ts';
|
||||
|
||||
type OptimizeRequest = {
|
||||
type: 'optimize-exact';
|
||||
@@ -9,6 +9,7 @@ type OptimizeRequest = {
|
||||
chart: ExactSongChart;
|
||||
perfectNoteScore: number;
|
||||
directSongScoreBonusPercent?: number;
|
||||
includeAchievementProbabilities?: boolean;
|
||||
};
|
||||
|
||||
type CombinationCandidate = {
|
||||
@@ -24,7 +25,31 @@ type CombinationRequest = {
|
||||
chart: ExactSongChart;
|
||||
};
|
||||
|
||||
self.onmessage = (event: MessageEvent<OptimizeRequest | CombinationRequest>) => {
|
||||
type RankedCombination = {
|
||||
result: ExactOptimizationResult;
|
||||
overallPower: number;
|
||||
cards?: OptimizerCard[];
|
||||
directSongScoreBonusPercent?: number;
|
||||
achievementProbabilities?: ScoreAchievementProbabilities;
|
||||
};
|
||||
|
||||
type AchievementRequest = {
|
||||
type: 'estimate-achievement';
|
||||
rankings: RankedCombination[];
|
||||
chart: ExactSongChart;
|
||||
sampleCount?: number;
|
||||
};
|
||||
|
||||
function cardsForResult(cards: readonly OptimizerCard[], result: ExactOptimizationResult): OptimizerCard[] {
|
||||
const cardById = new Map(cards.map((card) => [card.id, card]));
|
||||
return result.order.map((cardId) => {
|
||||
const card = cardById.get(cardId);
|
||||
if (!card) throw new Error('확률 계산 대상의 스페셜 순서를 복원하지 못했습니다.');
|
||||
return card;
|
||||
});
|
||||
}
|
||||
|
||||
self.onmessage = (event: MessageEvent<OptimizeRequest | CombinationRequest | AchievementRequest>) => {
|
||||
|
||||
try {
|
||||
if (event.data.type === 'optimize-exact') {
|
||||
@@ -34,14 +59,49 @@ self.onmessage = (event: MessageEvent<OptimizeRequest | CombinationRequest>) =>
|
||||
onProgress: (progress) => self.postMessage({ type: 'progress', progress })
|
||||
};
|
||||
const result = optimizeExactOrderAndCooldowns(event.data.cards, event.data.chart, options);
|
||||
self.postMessage({ type: 'result', result });
|
||||
let achievementProbabilities: ScoreAchievementProbabilities | undefined;
|
||||
if (event.data.includeAchievementProbabilities) {
|
||||
const orderedCards = cardsForResult(event.data.cards, result);
|
||||
achievementProbabilities = estimateScoreAchievementProbabilities(
|
||||
orderedCards,
|
||||
result.cooldownReductions,
|
||||
event.data.chart,
|
||||
{ ...options, sampleCount: 25_000 }
|
||||
);
|
||||
}
|
||||
self.postMessage({ type: 'result', result, achievementProbabilities });
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data.type === 'estimate-achievement') {
|
||||
const request = event.data;
|
||||
const rankings = request.rankings.map((ranking, rankingIndex) => {
|
||||
if (!ranking.cards) throw new Error('확률 계산에 필요한 카드 정보가 없습니다.');
|
||||
const orderedCards = cardsForResult(ranking.cards, ranking.result);
|
||||
const achievementProbabilities = estimateScoreAchievementProbabilities(
|
||||
orderedCards,
|
||||
ranking.result.cooldownReductions,
|
||||
event.data.chart,
|
||||
{
|
||||
perfectNoteScore: ranking.result.perfectNoteScore,
|
||||
directSongScoreBonusPercent: ranking.directSongScoreBonusPercent,
|
||||
sampleCount: request.sampleCount
|
||||
}
|
||||
);
|
||||
self.postMessage({
|
||||
type: 'achievement-progress',
|
||||
progress: { evaluatedCandidates: rankingIndex + 1, totalCandidates: request.rankings.length }
|
||||
});
|
||||
return { ...ranking, achievementProbabilities };
|
||||
});
|
||||
self.postMessage({ type: 'achievement-result', rankings });
|
||||
return;
|
||||
}
|
||||
|
||||
const totalCandidates = event.data.candidates.reduce((sum, candidate) => sum + countOptimizationCandidates(candidate.cards), 0);
|
||||
let completedCandidates = 0;
|
||||
let best: { result: ExactOptimizationResult; overallPower: number } | null = null;
|
||||
const rankings: { result: ExactOptimizationResult; overallPower: number }[] = [];
|
||||
let best: RankedCombination | null = null;
|
||||
const rankings: RankedCombination[] = [];
|
||||
for (const candidate of event.data.candidates) {
|
||||
const result = optimizeExactOrderAndCooldowns(candidate.cards, event.data.chart, {
|
||||
perfectNoteScore: candidate.perfectNoteScore,
|
||||
@@ -54,9 +114,19 @@ self.onmessage = (event: MessageEvent<OptimizeRequest | CombinationRequest>) =>
|
||||
completedCandidates += countOptimizationCandidates(candidate.cards);
|
||||
if (!best || result.theoreticalMaxScore > best.result.theoreticalMaxScore ||
|
||||
(result.theoreticalMaxScore === best.result.theoreticalMaxScore && result.expectedScore > best.result.expectedScore)) {
|
||||
best = { result, overallPower: candidate.overallPower };
|
||||
best = {
|
||||
result,
|
||||
overallPower: candidate.overallPower,
|
||||
cards: candidate.cards,
|
||||
directSongScoreBonusPercent: candidate.directSongScoreBonusPercent
|
||||
};
|
||||
}
|
||||
rankings.push({ result, overallPower: candidate.overallPower });
|
||||
rankings.push({
|
||||
result,
|
||||
overallPower: candidate.overallPower,
|
||||
cards: candidate.cards,
|
||||
directSongScoreBonusPercent: candidate.directSongScoreBonusPercent
|
||||
});
|
||||
}
|
||||
if (!best) throw new Error('정밀 분석할 조합이 없습니다.');
|
||||
rankings.sort((left, right) => right.result.theoreticalMaxScore - left.result.theoreticalMaxScore ||
|
||||
|
||||
Reference in New Issue
Block a user