feat: complete placement analysis workflow

This commit is contained in:
2026-08-18 14:22:04 +09:00
parent dd3ea911f6
commit 7b713bffe3
452 changed files with 97115 additions and 24 deletions
+65
View File
@@ -0,0 +1,65 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { calculateBoardEffects, type BoardSummarySource } from './board-effects.ts';
const summary: BoardSummarySource = {
members: [{ id: 'a', positionGroupId: 'model-a' }],
models: { 'model-a': [{ groupId: 'S-1', x: 0, y: 0 }, { groupId: 'B-1', x: 1, y: 0 }] },
nodeGroups: {
'B-1': [{ characterIds: [], effect: { type: 'performance_up', value: 100, targetId: 'skill_tree_effect_target-skill_tree_character', trigger: 'always' } }],
'B-2': [{ characterIds: [], effect: { type: 'live_active_skill_cool_time_shorten_permil_up', value: 40, targetId: 'skill_tree_effect_target-skill_tree_character', trigger: 'always' } }],
'R-1': [{ characterIds: [], effect: { type: 'all_parameter_up_permil_up', value: 30, targetId: 'skill_tree_effect_target-all', trigger: 'set_live_leader' } }],
'R-2': [{ characterIds: [], effect: {
id: 'skill_tree_effect-live_active_skill_effect_up_permil_up-100-skill_tree_effect_target-all-skill_tree_effect_passive_trigger-music_skill_tree_character-set_live_leader',
type: 'live_active_skill_effect_up_permil_up', value: 100, targetId: 'skill_tree_effect_target-all', trigger: 'set_live_leader'
} }],
'G-1': [{ characterIds: [], effect: { type: 'all_parameter_up', value: 5, targetId: 'skill_tree_effect_target-all', trigger: 'always' } }],
'Y-1': [{ characterIds: [], effect: {
id: 'skill_tree_effect-live_score_bonus_add_permil_up_by_music_skill_tree_character_and_music_singer_type-5-skill_tree_effect_passive_trigger-music_skill_tree_character_and_music_singer_type-solo-always-solo',
type: 'live_score_bonus_add_permil_up_by_music_skill_tree_character_and_music_singer_type', value: 5, trigger: 'always'
} }]
}
};
const cards = [
{ id: 'card-a', characterId: 'a', type: '큐트', generation: '0기생' },
{ id: 'card-b', characterId: 'b', type: '퓨어', generation: '1기생' }
];
test('편성 멤버의 파란 보드, 리더의 빨간 보드와 공통 초록 보드를 합산한다', () => {
const result = calculateBoardEffects(summary, {
a: ['B-1', 'B-2', 'Y-1'],
leader: ['R-1', 'R-2'],
account: ['G-1']
}, cards, 'leader', { singerType: 'solo', singerCharacterIds: ['a'] });
assert.equal(result.flatByCardId['card-a'].performance, 105);
assert.equal(result.flatByCardId['card-b'].performance, 5);
assert.equal(result.rateByCardId['card-a'].sense, 3);
assert.deepEqual(result.allowedCooldownReductionsByCardId['card-a'], [0, 4, 8, 12]);
assert.deepEqual(result.allowedCooldownReductionsByCardId['card-b'], [0, 4, 8, 12]);
assert.equal(result.directSongScoreBonusPercent, 0.5);
assert.equal(result.scoreSupportPercent, 0);
});
test('리더가 악곡 가창자이면 조건부 빨간 스코어 서포트를 적용한다', () => {
const result = calculateBoardEffects(summary, { leader: ['R-2'] }, cards, 'leader', {
singerType: 'group',
singerCharacterIds: ['leader']
});
assert.equal(result.scoreSupportPercent, 10);
});
test('연결 카드가 범위 안의 활성 노드 효과를 증폭한다', () => {
const result = calculateBoardEffects(summary, { a: ['B-1'] }, cards, 'a', null, {
placements: { a: { 'S-1': 'connect-card' } },
cards: [{
id: 'connect-card',
awakening: 0,
connect: [{ level: 1, effectPermilUp: 2000, extent: [{ x: 1, y: 0 }] }]
}]
});
assert.equal(result.flatByCardId['card-a'].performance, 300);
});