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
+93
View File
@@ -0,0 +1,93 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { calculateUnitPower, type UnitPowerCard } from './unit-power.ts';
function card(id: string, passive: string, awakenedPassive = passive): UnitPowerCard {
return {
id,
characterId: id,
name: id,
type: '큐트',
generation: '0기생',
power: { maxLevel: 80, statsAtMaxLevel: { performance: 100, technique: 100, sense: 100 } },
details: { passive: [{ level: 1, description: passive }, { level: 2, description: awakenedPassive }] }
};
}
test('최대 레벨 기본 스탯, 개화 보너스와 패시브로 종합력을 계산한다', () => {
const result = calculateUnitPower([
{ card: card('a', '0기생(이)가 2명 이상이면 자신의 전체 파라미터가 20% UP'), awakening: 5 },
...['b', 'c', 'd', 'e'].map((id) => ({ card: card(id, ''), awakening: 5 }))
]);
assert.equal(result.baseParameterTotal, 1500);
assert.equal(result.potentialTotal, 150);
assert.equal(result.passiveTotal, 66);
assert.equal(result.overallPower, 1716);
});
test('패시브 2레벨은 4개화부터 적용한다', () => {
const passiveCard = card('a', '자신의 전체 파라미터가 10% UP', '자신의 전체 파라미터가 20% UP');
const otherCards = ['b', 'c', 'd', 'e'].map((id) => ({ card: card(id, ''), awakening: 0 }));
const before = calculateUnitPower([{ card: passiveCard, awakening: 3 }, ...otherCards]);
const after = calculateUnitPower([{ card: passiveCard, awakening: 4 }, ...otherCards]);
assert.equal(before.passiveTotal, 33);
assert.equal(after.passiveTotal, 66);
});
test('편성 조건을 만족한 리더 의상 효과를 종합력에 더한다', () => {
const result = calculateUnitPower(
['a', 'b', 'c', 'd', 'e'].map((id) => ({ card: card(id, ''), awakening: 0 })),
{ name: '테스트 의상', description: '0기생(이)가 2명 이상이면 전원의 전체 파라미터가 50% UP' }
);
assert.equal(result.outfitTotal, 750);
assert.equal(result.overallPower, 2250);
});
test('보유 메모리 효과는 개화 적용 후 각 멤버의 능력치마다 올림한다', () => {
const result = calculateUnitPower(
['a', 'b', 'c', 'd', 'e'].map((id) => ({ card: card(id, ''), awakening: 2 })),
null,
null,
46
);
assert.equal(result.potentialTotal, 150);
assert.equal(result.memoryTotal, 90);
assert.equal(result.overallPower, 1740);
});
test('멤버 강화는 메모리를 제외한 파라미터·패시브·의상·보드 합계에 적용한다', () => {
const result = calculateUnitPower(
['a', 'b', 'c', 'd', 'e'].map((id) => ({ card: card(id, ''), awakening: 0 })),
null,
null,
100,
10
);
assert.equal(result.memoryTotal, 150);
assert.equal(result.memberPowerUpTotal, 150);
assert.equal(result.overallPower, 1800);
});
test('조건부 기수 패시브는 복수 소속 멤버도 대상으로 삼는다', () => {
const fubuki = {
...card('fubuki', '1기생(이)가 2명 이상이면 1기생2명의 퍼포먼스가 10% UP'),
generation: 'hololive 1기생 / GAMERS'
};
const matsuri = {
...card('matsuri', ''),
generation: 'hololive 1기생'
};
const result = calculateUnitPower([
{ card: fubuki, awakening: 0 },
{ card: matsuri, awakening: 0 },
...['c', 'd', 'e'].map((id) => ({ card: card(id, ''), awakening: 0 }))
]);
assert.equal(result.passiveTotal, 20);
});