feat: limit medium-probability cards and sync Docker dev dependencies

This commit is contained in:
2026-09-02 12:28:55 +09:00
parent 7308428945
commit 1e3d1d0b08
7 changed files with 138 additions and 11 deletions
+53 -1
View File
@@ -5,7 +5,8 @@ import {
generateAnchoredCombinationCandidates,
generateCombinationCandidates,
resolveCandidateAwakening,
selectMemberDiverseCandidates
selectMemberDiverseCandidates,
withinMediumProbabilityCardLimit
} from './index.ts';
const cards = [
@@ -15,6 +16,57 @@ const cards = [
{ id: 'c-1', characterId: 'c', score: 40 }
];
const probabilityCards = [46, 46, 55, 55, 55, 55].map((probabilityPercent, index) => ({
id: String(index),
characterId: String(index),
score: 100 - index,
details: { active: [{ probabilityPercent }, { probabilityPercent }] }
}));
test('중간 확률은 스킬 레벨 수와 무관하게 카드당 한 장으로 세고 낮음은 제외한다', () => {
assert.equal(withinMediumProbabilityCardLimit([probabilityCards[0]], 1), true);
assert.equal(withinMediumProbabilityCardLimit([probabilityCards[0]], 0), false);
assert.equal(withinMediumProbabilityCardLimit([
probabilityCards[2],
{ details: { active: [{ probabilityPercent: 37 }] } },
{ details: { active: [] } }
], 0), true);
assert.equal(withinMediumProbabilityCardLimit(probabilityCards, null), true);
});
test('높은 점수의 중간 확률 카드가 빔을 독점하기 전에 제한을 적용한다', () => {
const result = generateCombinationCandidates([], probabilityCards, 5, (card) => card.score, 1,
undefined, (selected) => withinMediumProbabilityCardLimit(selected, 1));
assert.equal(result.length, 1);
assert.deepEqual(result[0].map((card) => card.id).sort(), ['0', '2', '3', '4', '5']);
});
test('고정 카드도 중간 확률 제한에 포함하고 초과하거나 완성할 수 없으면 후보를 반환하지 않는다', () => {
const accepts = (selected: readonly (typeof probabilityCards)[number][]) => withinMediumProbabilityCardLimit(selected, 1);
const result = generateCombinationCandidates([probabilityCards[1]], probabilityCards, 5, (card) => card.score, 10, undefined, accepts);
assert.equal(result.length, 1);
assert.ok(result[0].some((card) => card.id === '1'));
assert.ok(result[0].every((card) => card.id !== '0'));
assert.deepEqual(generateCombinationCandidates(probabilityCards.slice(0, 2), probabilityCards, 5,
(card) => card.score, 10, undefined, accepts), []);
assert.deepEqual(generateCombinationCandidates([], probabilityCards, 5,
(card) => card.score, 10, undefined, (selected) => withinMediumProbabilityCardLimit(selected, 0)), []);
});
test('임시 축 후보에도 제한을 적용하고 중간 확률 카드를 모두 제외할 수 있다', () => {
const result = generateAnchoredCombinationCandidates([], probabilityCards, 3,
[{ scoreCard: (card) => card.score }], 1,
(selected) => withinMediumProbabilityCardLimit(selected, 0));
assert.ok(result.length > 0);
assert.ok(result.every((selected) => selected.every((card) => card.details.active[0].probabilityPercent === 55)));
});
test('제한 없음은 기존 조합 후보를 그대로 유지한다', () => {
const expected = generateCombinationCandidates([], probabilityCards, 5, (card) => card.score, 10);
assert.deepEqual(generateCombinationCandidates([], probabilityCards, 5, (card) => card.score, 10,
undefined, (selected) => withinMediumProbabilityCardLimit(selected, null)), expected);
});
test('고정 카드를 유지하고 같은 멤버의 이격 카드를 제외한다', () => {
const result = generateCombinationCandidates([cards[0]], cards, 3, (card) => card.score, 20);
assert.equal(result.length, 1);