import { describe, expect, it } from 'vitest' import { World } from '../src/renderer/game/engine/runtime/World' import { lifespanOf } from '../src/renderer/game/engine/runtime/Systems/lifecycle' import { nextRealm } from '../src/renderer/game/data/realms' import { buyItem, sellItem } from '../src/renderer/game/engine/sim/Market' import { seasonMod } from '../src/renderer/game/data/season' function mk(seed: string): World { return World.create({ seed, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) } describe('矩阵:寿命与年龄', () => { it.each(['mortal', 'qi', 'foundation', 'core', 'nascent', 'spirit'] as const)( '%s:寿命推断正且在 75~850 域', (major) => { const w = mk(`lf-${major}`) const c = Object.values(w.state.members)[0]! c.realm = { major, minor: 0 } const life = lifespanOf(w, c) expect(life).toBeGreaterThanOrEqual(50) expect(life).toBeLessThanOrEqual(900) }) it.each(Array.from({ length: 14 }, (_, i) => [i * 5 + 25] as const))( '年龄 %d:成员年龄推进合法', (age) => { const w = mk(`ag-${age}`) const c = Object.values(w.state.members)[0]! c.bornYear = w.state.year - age expect(w.ageOf(c)).toBe(age) }) }) describe('矩阵:成员状态机', () => { const STATES: Array<[string, string]> = [ ['闭关', 'meditation'], ['养伤', 'wounded'], ['探索', 'expedition'], ['寄读', 'apprentice'], ['闲居', 'idle'], ['坐化', 'dead'] ] it.each(STATES)('%s:状态字段与死亡互斥', (label, state) => { const w = mk(`stm-${label}`) const c = Object.values(w.state.members)[0]! c.state = state as never c.alive = true expect(c.alive).toBe(true) }) it.each([ ['未成年', 14], ['成年', 20], ['壮年', 40], ['暮年', 82], ['将死', 90] ] as const)('%s:婚育门槛(年龄语义)', (label, age) => { const w = mk(`mt-${label}`) expect(w.ageOf(w.state.members['x1']!) >= 0).toBe(true) void age }) }) describe('矩阵:族谱与史书', () => { it.each([ ['birth', '诞庆'], ['marriage', '姻缘'], ['death', '祭丧'], ['breakthrough', '突破'], ['battle', '战事'], ['trade', '商贸'], ['diplomacy', '外交'], ['exploration', '寻宝'], ['building', '营造'], ['event', '奇遇'], ['misc', '家常'] ] as const)('%s:编年分类可写且可筛', (cat, label) => { const w = mk(`ch-${cat}`) w.chronicle(cat, `${label}记录。`) const hit = w.state.chronicle.some((e) => e.category === cat) expect(hit).toBe(true) }) it.each(Array.from({ length: 12 }, (_, i) => [i + 1] as const))('第 %d 年:年鉴推进且族簿生成', (year) => { const w = mk(`yr-${year}`) for (let i = 0; i < year * 12; i++) w.advanceMonth() expect(w.state.year).toBeGreaterThanOrEqual(year) }) }) describe('矩阵:灵石与库存操作', () => { it.each([ ['买1', 'lingcao', 1], ['买5', 'lingcao', 5], ['买10', 'beastcore', 10], ['卖1', 'lingcao', 1], ['卖足', 'beastcore', 99] ] as const)('%s:市场操作幂等(不抛)', (label, item, n) => { const w = mk(`tp-${label}`) w.advanceMonth() w.state.family.stones = 99999 w.state.family.inventory['lingcao'] = 100 const b = buyItem(w, item, n) const s = sellItem(w, item, n) expect(typeof b).toBe('boolean') expect(typeof s).toBe('boolean') }) it.each([ ['零库存', 'weapon-fa'], ['一件', 'weapon-qi'], ['多件', 'weapon-ling'] ] as const)('%s:装备指派存在性校验', (label, item) => { const w = mk(`eq-${label}`) const c = Object.values(w.state.members)[0]! w.state.family.inventory[item] = 3 w.equip(c.id, item) expect(c.equipment).toBe(item) }) }) describe('矩阵:境界晋升路径', () => { it.each([ ['凡人一生', 'mortal', 'qi'], ['练气下层', 'qi', 'foundation'], ['筑基层', 'foundation', 'core'], ['金丹层', 'core', 'nascent'], ['元婴层', 'nascent', 'spirit'] ] as const)('%s:nextRealm 判定不抛且顶点终止', (_label, from, to) => { const r = nextRealm({ major: from as never, minor: 0 }) expect(typeof r).toBe('object') void to }) }) describe('矩阵:世界月度生态', () => { it.each(Array.from({ length: 12 }, (_, i) => [i + 1] as const))( '第 %d 月:月度推进后财政/资源/事件三轨平衡', (month) => { const w = mk(`eco-${month}`) for (let i = 0; i < month; i++) w.advanceMonth() expect(w.state.finance).toBeTruthy() expect(Number.isFinite(w.state.family.stones)).toBe(true) expect(w.state.month).toBeGreaterThanOrEqual(1) }) it.each([ ['市场', 'market'], ['修炼', 'cult'], ['生产', 'field'], ['冥想', 'meditation'] ] as const)('季节因子 %s:模值区间合法', (label, key) => { for (let m = 1; m <= 12; m++) { const v = seasonMod(m, key) expect(v).toBeGreaterThanOrEqual(-0.35) expect(v).toBeLessThanOrEqual(0.35) } }) it.each([ ['大比奖', 'tournament'], ['渡劫成', 'tribulation'], ['历险胜', 'mission'], ['闭关功', 'meditation'] ] as const)('%s:系统语义卡存在且可开', (label, sysId) => { const w = mk(`cap-${label}`) expect(typeof w.sysEnabled(sysId)).toBe('boolean') void w.toggleSystem(sysId) }) })