import { test } from 'node:test'; import assert from 'node:assert/strict'; import { mulberry32, applyDamage, chooseAction, simulateCombat, runBatch, } from './sim-balance.mjs'; test('applyDamage: 방어 우선 차감 후 hp', () => { assert.deepEqual(applyDamage(80, 0, 10), { hp: 70, block: 0 }); assert.deepEqual(applyDamage(80, 5, 10), { hp: 75, block: 0 }); assert.deepEqual(applyDamage(80, 12, 10), { hp: 80, block: 2 }); assert.deepEqual(applyDamage(3, 0, 10), { hp: 0, block: 0 }); }); test('mulberry32: 동일 시드 동일 수열', () => { const a = mulberry32(1), b = mulberry32(1); assert.equal(a(), b()); assert.equal(a(), b()); }); const CARDS = { Strike: { name: '타격', cost: 1, kind: 'Attack', damage: 6 }, Defend: { name: '방어', cost: 1, kind: 'Skill', block: 5 }, Bash: { name: '강타', cost: 2, kind: 'Attack', damage: 10 }, }; test('chooseAction: 치사 가능하면 공격 선택', () => { const idx = chooseAction(['Strike', 'Defend'], CARDS, 3, 5, 0, { kind: 'Attack', value: 10 }); assert.equal(idx, 0); }); test('chooseAction: 치사 불가 + 적 공격 의도면 방어 선택', () => { const idx = chooseAction(['Strike', 'Defend'], CARDS, 3, 40, 0, { kind: 'Attack', value: 10 }); assert.equal(idx, 1); }); test('chooseAction: 적 방어 의도면 공격 우선', () => { const idx = chooseAction(['Defend', 'Strike'], CARDS, 3, 40, 0, { kind: 'Defend', value: 8 }); assert.equal(idx, 1); }); test('chooseAction: 사용 가능 카드 없으면 -1', () => { const idx = chooseAction(['Bash'], CARDS, 1, 40, 0, { kind: 'Attack', value: 10 }); assert.equal(idx, -1); }); const DATA = { cards: CARDS, starterDeck: ['Strike', 'Strike', 'Strike', 'Strike', 'Strike', 'Defend', 'Defend', 'Defend', 'Defend', 'Bash'], enemy: { name: '슬라임', maxHp: 45, intents: [ { kind: 'Attack', value: 10 }, { kind: 'Attack', value: 6 }, { kind: 'Defend', value: 8 }, ], }, }; test('simulateCombat: 결정적 결과(동일 시드)', () => { const r1 = simulateCombat(DATA, mulberry32(1)); const r2 = simulateCombat(DATA, mulberry32(1)); assert.deepEqual(r1, r2); assert.equal(typeof r1.win, 'boolean'); assert.ok(r1.turns >= 1); }); test('simulateCombat: 약한 적이면 대체로 승리', () => { let wins = 0; for (let i = 0; i < 50; i++) if (simulateCombat(DATA, mulberry32(i + 1)).win) wins++; assert.ok(wins >= 40, `예상 승리 다수, 실제 ${wins}/50`); }); test('runBatch: 집계 필드·승률 범위', () => { const r = runBatch(100, 1); assert.equal(r.N, 100); assert.ok(r.winRate >= 0 && r.winRate <= 1); assert.ok(r.avgTurns > 0); assert.ok(r.cardStats.Strike.plays > 0); }); test('runBatch: 동일 시드 동일 결과', () => { assert.deepEqual(runBatch(100, 7), runBatch(100, 7)); });