feat: improve analysis and release v1.0.2
This commit is contained in:
@@ -8,6 +8,33 @@ type BeamState<T extends CombinationSearchCard> = {
|
||||
score: number;
|
||||
};
|
||||
|
||||
export type CombinationSearchLimits = {
|
||||
openSlots: number;
|
||||
screeningLimit: number;
|
||||
baseExactFinalistLimit: number;
|
||||
alternativeExactFinalistLimit: number;
|
||||
};
|
||||
|
||||
export type CombinationScoringStrategy<T extends CombinationSearchCard> = {
|
||||
scoreCard: (card: T) => number;
|
||||
scoreCombination?: (cards: readonly T[]) => number;
|
||||
};
|
||||
|
||||
const SCREENING_LIMIT_PER_OPEN_SLOT = 5_000;
|
||||
const EXACT_FINALIST_LIMIT_PER_OPEN_SLOT = 56;
|
||||
|
||||
export function combinationSearchLimits(fixedCardCount: number, targetSize = 5): CombinationSearchLimits {
|
||||
const normalizedTargetSize = Math.max(1, Math.floor(targetSize));
|
||||
const normalizedFixedCardCount = Math.min(normalizedTargetSize, Math.max(0, Math.floor(fixedCardCount)));
|
||||
const openSlots = Math.max(1, normalizedTargetSize - normalizedFixedCardCount);
|
||||
return {
|
||||
openSlots,
|
||||
screeningLimit: SCREENING_LIMIT_PER_OPEN_SLOT * openSlots,
|
||||
baseExactFinalistLimit: EXACT_FINALIST_LIMIT_PER_OPEN_SLOT * openSlots,
|
||||
alternativeExactFinalistLimit: EXACT_FINALIST_LIMIT_PER_OPEN_SLOT * openSlots
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveCandidateAwakening(
|
||||
configuredAwakening: number,
|
||||
owned: boolean,
|
||||
@@ -63,3 +90,64 @@ export function generateCombinationCandidates<T extends CombinationSearchCard>(
|
||||
.sort((left, right) => right.score - left.score)
|
||||
.map((state) => state.cards);
|
||||
}
|
||||
|
||||
export function generateAnchoredCombinationCandidates<T extends CombinationSearchCard>(
|
||||
fixedCards: readonly T[],
|
||||
candidateCards: readonly T[],
|
||||
targetSize: number,
|
||||
strategies: readonly CombinationScoringStrategy<T>[],
|
||||
perAnchorLimit: number
|
||||
): T[][] {
|
||||
if (strategies.length === 0 || perAnchorLimit <= 0 || fixedCards.length >= targetSize) return [];
|
||||
const fixedCharacters = new Set(fixedCards.map((card) => card.characterId));
|
||||
const anchors = candidateCards.filter((card) => !fixedCharacters.has(card.characterId));
|
||||
const perStrategyLimit = Math.max(1, Math.ceil(perAnchorLimit / strategies.length));
|
||||
const combined = new Map<string, T[]>();
|
||||
|
||||
for (const anchor of anchors) {
|
||||
const anchored = new Map<string, T[]>();
|
||||
for (const strategy of strategies) {
|
||||
for (const cards of generateCombinationCandidates(
|
||||
[...fixedCards, anchor],
|
||||
candidateCards,
|
||||
targetSize,
|
||||
strategy.scoreCard,
|
||||
perStrategyLimit,
|
||||
strategy.scoreCombination
|
||||
)) {
|
||||
anchored.set(cards.map((card) => card.id).sort().join('|'), cards);
|
||||
}
|
||||
}
|
||||
for (const [key, cards] of Array.from(anchored.entries()).slice(0, perAnchorLimit)) {
|
||||
combined.set(key, cards);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(combined.values());
|
||||
}
|
||||
|
||||
export function selectMemberDiverseCandidates<T>(
|
||||
candidates: readonly T[],
|
||||
cardsOf: (candidate: T) => readonly { id: string }[],
|
||||
scoreOf: (candidate: T) => number,
|
||||
fixedCardIds: readonly string[],
|
||||
limit: number
|
||||
): T[] {
|
||||
if (limit <= 0) return [];
|
||||
const fixedIds = new Set(fixedCardIds);
|
||||
const ranked = [...candidates].sort((left, right) => scoreOf(right) - scoreOf(left));
|
||||
const bestByCardId = new Map<string, T>();
|
||||
for (const candidate of ranked) {
|
||||
for (const card of cardsOf(candidate)) {
|
||||
if (!fixedIds.has(card.id) && !bestByCardId.has(card.id)) bestByCardId.set(card.id, candidate);
|
||||
}
|
||||
}
|
||||
|
||||
const selected = new Map<string, T>();
|
||||
for (const candidate of bestByCardId.values()) {
|
||||
const key = cardsOf(candidate).map((card) => card.id).sort().join('|');
|
||||
selected.set(key, candidate);
|
||||
if (selected.size >= limit) break;
|
||||
}
|
||||
return Array.from(selected.values());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user