feat: filter awakening candidates and search connection chains
Publish GitHub Pages output / publish (push) Canceled after 37s

This commit is contained in:
2026-10-01 22:39:28 +09:00
parent 384c400134
commit 1b7889e574
5 changed files with 102 additions and 7 deletions
+32
View File
@@ -1,9 +1,41 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { searchBoardConnectionChain } from './board-connection-chain.ts';
import { readFileSync } from 'node:fs';
import { boardInvestments, recommendBoardAwakening, type BoardAwakeningData, type BoardAwakeningRequest } from './board-awakening.ts';
const catalog = JSON.parse(readFileSync(new URL('../data/cards-summary.json', import.meta.url), 'utf8')) as { cards: BoardAwakeningData['cards'] };
const memory = JSON.parse(readFileSync(new URL('../data/memory-summary.json', import.meta.url), 'utf8'));
test('최소 개화는 투자 후보에만 적용하고 미보유 포함은 별도로 선택한다', () => {
const cards = ['zero', 'three', 'four', 'five', 'new'].map((id) => ({ id, rarity: 5 }));
const collection = Object.fromEntries([['zero', 0], ['three', 3], ['four', 4], ['five', 5]].map(([id, awakening]) => [id, { owned: true, awakening: Number(awakening) }]));
assert.deepEqual(boardInvestments(cards, collection, 4, false).map((r) => r.cardId), ['four']);
assert.deepEqual(boardInvestments(cards, collection, 3, false).map((r) => [r.cardId, r.copies]), [['three', 2], ['four', 1]]);
assert.deepEqual(boardInvestments(cards, collection, 4, true).map((r) => [r.cardId, r.to]), [['four', 5], ['new', 0], ['new', 5]]);
assert.equal(boardInvestments(cards, collection, 0, false).length, 3);
assert.throws(() => boardInvestments(cards, collection, -1), /최소 개화/);
});
test('대상 카드와 밀려난 두 장의 3단계 연쇄를 찾으며 중복 배치·입력 변경을 하지 않는다', () => {
const source = { members: [{ id: 'm', positionGroupId: 'model' }], models: { model: [0, 4, 8].map((x, i) => ({ groupId: `S${i}`, x, y: 0 })) }, nodeGroups: Object.fromEntries([0, 1, 2].map((i) => [`S${i}`, [{ characterIds: [], type: 'connection', effect: null }]])) };
const cards = ['target', 'a', 'b', 'c'].map((id) => ({ id, awakening: 0, connect: [{ level: 1, effectPermilUp: 1000, extent: [] }] }));
const initial = { m: { S0: 'a', S1: 'b', S2: 'c' } };
const result = searchBoardConnectionChain(source, { m: ['S1', 'S2'] }, cards, [], initial, 'target', (placements) => {
const ids = Object.values(placements).flatMap(Object.values);
assert.equal(new Set(ids).size, ids.length);
const maximum = (placements.m?.S0 === 'target' ? 10 : 0) + (placements.m?.S1 === 'a' ? 5 : 0) + (placements.m?.S2 === 'b' ? 2 : 0);
return { maximum, expected: maximum, placements };
});
assert.equal(result.maximum, 17);
assert.deepEqual(initial, { m: { S0: 'a', S1: 'b', S2: 'c' } });
});
test('연쇄 배치에서도 편성 멤버의 큰 파란 발동률 노드 범위를 지킨다', () => {
const source = { members: [{ id: 'm', positionGroupId: 'model' }], models: { model: [{ groupId: 'S', x: 7, y: 0 }, { groupId: 'B', x: 5, y: 0 }] }, nodeGroups: {
S: [{ characterIds: [], type: 'connection', effect: null }],
B: [{ characterIds: [], type: 'card', grade: 2, effect: { type: 'live_active_skill_activation_probability_up_permil_up', value: 60 } }]
} };
const cards = [{ id: 'a', awakening: 0, connect: [{ level: 1, effectPermilUp: 1000, extent: [{ x: -2, y: 0 }] }] }, { id: 'target', awakening: 5, connect: [{ level: 2, effectPermilUp: 2000, extent: [{ x: 2, y: 0 }] }] }];
const result = searchBoardConnectionChain(source, { m: ['S', 'B'] }, cards, ['m'], { m: { S: 'a' } }, 'target', (placements) => ({ maximum: placements.m?.S === 'target' ? 100 : 1, expected: 1, placements }));
assert.equal(result.placements.m.S, 'a');
});
function fixture() {
const cards = ['a', 'b', 'c', 'd', 'e', 'new'].map((id) => {
const card = structuredClone(catalog.cards.find((card) => card.rarity === 5)!);