import test from 'node:test'; import assert from 'node:assert/strict'; import { ZHANZHEN, matchWei, roleOf, zhanzhen, listZhanzhen, zhanzhenByDyn } from '../src/zhanzhen.js'; // 引擎纯逻辑用 mock 战役测(真战役数据由双证考据子代理陆续装入 ZHANZHEN)。 const MOCK = { id: 'mock', dyn: 'tang', chao: 'tang', name: '模拟之战', year: 621, side: 'A vs B', stake: '赌注', intro: '引子', situ: '处境', weiTiers: { '主帅': 5, '列将': 4, '裨将': 3, '甲士': 1, '民夫': 0 }, narr: { '主帅': '你居中军', '甲士': '你在阵列里' }, nodes: [{ label: '关键节点', cite: '《旧唐书》:「……」' }], choices: { fengkou: { prompt: '身处风口——', options: [{ id: 'go', text: '战', outcome: '胜', end: { tone: 'sheng', word: '受赏' } }] }, juzhong: { prompt: '身在局中——', options: [{ id: 'go', text: '冲', outcome: '死', end: { tone: 'wang', word: '身死' } }] }, boji: { prompt: '被波及——', options: [{ id: 'go', text: '躲', outcome: '幸存', end: { tone: 'xian', word: '未定' } }] }, }, real: '战果', cite: '《旧唐书》:「……」', note: null, }; test('matchWei:取不高于你 tier 的最高一档;全低于则 null', () => { assert.equal(matchWei(MOCK.weiTiers, 5), '主帅'); assert.equal(matchWei(MOCK.weiTiers, 3), '裨将'); assert.equal(matchWei(MOCK.weiTiers, 2), '甲士'); assert.equal(matchWei(MOCK.weiTiers, null), null); }); test('roleOf:顶档=风口,次档=局中,再低=波及,无=旁观', () => { assert.equal(roleOf('主帅', MOCK.weiTiers).key, 'fengkou'); assert.equal(roleOf('列将', MOCK.weiTiers).key, 'juzhong'); assert.equal(roleOf('甲士', MOCK.weiTiers).key, 'boji'); assert.equal(roleOf(null, MOCK.weiTiers).key, 'pangguan'); }); test('zhanzhen:按 tier 组装处境+对应抉择+双证战果', () => { ZHANZHEN.mock = MOCK; const top = zhanzhen('mock', 5); assert.equal(top.role.key, 'fengkou'); assert.equal(top.you.wei, '主帅'); assert.equal(top.choice.options[0].end.word, '受赏'); assert.ok(/《.+》/.test(top.cite)); const low = zhanzhen('mock', 1); assert.equal(low.role.key, 'boji'); const out = zhanzhen('mock', null); assert.equal(out.role.key, 'pangguan'); assert.equal(out.choice, null, '旁观无抉择'); assert.equal(zhanzhenByDyn('tang').includes('mock'), true); assert.ok(listZhanzhen().some((b) => b.id === 'mock')); delete ZHANZHEN.mock; }); test('未知战役→null', () => { assert.equal(zhanzhen('nope', 5), null); }); // ── 真实双证战役数据校验(11场·zhanzhenData.js)── test('11场战役全部装入,字段完整、cite可查', () => { const list = listZhanzhen(); assert.ok(list.length >= 11, '至少11场战役'); const ids = new Set(list.map((b) => b.id)); for (const id of ['julu', 'gaixia', 'baideng', 'kunyang', 'guandu', 'chibi', 'feishui', 'hulao', 'chanyuan', 'poyang', 'tumu']) { assert.ok(ids.has(id), `应含 ${id}`); } for (const id of ids) { const b = zhanzhen(id, 5); assert.ok(b.name && b.side && b.stake && b.intro && b.situ, `${id} 元信息完整`); assert.ok(/《.+》/.test(b.cite), `${id} 战果 cite 可查`); assert.ok(b.real, `${id} 有真实战果`); assert.ok(Object.keys(b.zaichang).length >= 5, `${id} 军职档≥5`); // 节点≥3;cite 须么带《篇名》双证、么为 null(诚实存疑节点·如"水灌鄢城非见史记") assert.ok(b.nodes.length >= 3 && b.nodes.every((n) => n.cite == null || /《.+》/.test(n.cite) || n.cite.length > 6), `${id} 节点≥3且双证/存疑标注`); // 抉择:至少一档;存在的每档每个选项都要有 outcome + 合法 end.tone(部分战役只给单一军职档的抉择,自选身份走该档) const choiceRoles = Object.keys(ZHANZHEN[id].choices || {}); assert.ok(choiceRoles.length >= 1, `${id} 至少一档抉择`); for (const roleKey of choiceRoles) { const ch = ZHANZHEN[id].choices[roleKey]; assert.ok(ch && ch.options.length >= 2, `${id}.${roleKey} 应有≥2选项`); assert.ok(ch.options.every((o) => o.outcome && ['sheng', 'wang', 'xian'].includes(o.end.tone)), `${id}.${roleKey} 选项须有outcome+合法end.tone`); } } }); test('引擎朝映射:能算tier的战役 dyn 落在8引擎朝内', () => { const ENGINE = new Set(['tang', 'xihan', 'han', 'weijin', 'song', 'ming', 'liao', 'nanchao']); for (const b of Object.values(ZHANZHEN)) { if (b.dyn != null) assert.ok(ENGINE.has(b.dyn), `${b.id} 的 dyn=${b.dyn} 应是引擎朝`); } // 引擎朝各有对应战役(扩容后数量增多,校验包含关系) for (const id of ['poyang', 'tumu']) assert.ok(zhanzhenByDyn('ming').includes(id), `ming 应含 ${id}`); for (const id of ['chibi', 'guandu']) assert.ok(zhanzhenByDyn('han').includes(id), `han 应含 ${id}`); for (const id of ['gaixia', 'jingxing', 'mobei']) assert.ok(zhanzhenByDyn('xihan').includes(id), `xihan 应含 ${id}`); for (const id of ['hulao', 'yinshan', 'suiyang']) assert.ok(zhanzhenByDyn('tang').includes(id), `tang 应含 ${id}`); });