import { describe, expect, it } from 'vitest' import { World } from '../src/renderer/game/engine/world' import { monthlyRate } from '../src/renderer/game/engine/systems/cultivation' import { combatPowerOf } from '../src/renderer/game/engine/systems/combat' import { resolveBreakthrough } from '../src/renderer/game/engine/systems/cultivation' import { computeLegacy, resolveLegacy } from '../src/renderer/game/core/legacy' import { yearAxis } from '../src/renderer/game/core/yearaxis' import { buildBiography } from '../src/renderer/game/core/biography' import { computeGenealogy } from '../src/renderer/game/core/genealogy' import { marketPrice, buyItem, sellItem } from '../src/renderer/game/engine/market' import { resetSaveBus, attachLogSink } from './world.helpers' function baseWorld(seed: string): World { const w = World.create({ seed, surname: '司', familyName: '司家', motto: 'm', difficulty: 'normal' }) resetSaveBus() attachLogSink(w) return w } describe('年龄边界矩阵', () => { it.each([5, 7, 8, 15, 16, 17, 55, 56, 70].map((a) => [a] as const))('%i 岁修炼速率不越界(0.4-1×)', (a) => { const w = baseWorld(`age-${a}`) const c = w.state.members['x5'] c.realm = { major: 'qi', minor: 1 } c.bornYear = 1 - a const r = monthlyRate(w, c) expect(r).toBeGreaterThan(0) expect(r).toBeLessThanOrEqual(10) if (a < 8) expect(r).toBeLessThan(2) if (a > 55) expect(r).toBeLessThan(2) }) }) describe('修为边界矩阵', () => { it.each([-5, 0, 0.1, 49.9, 99.9, 100, 150].map((p) => [p] as const))('进度 %p 被夹在 0-100', (p) => { const w = baseWorld(`rp-${p}`) const c = w.state.members['x5'] c.realm = { major: 'qi', minor: 1 } c.realmProgress = p w.advanceMonth() expect(c.realmProgress).toBeGreaterThanOrEqual(0) expect(c.realmProgress).toBeLessThanOrEqual(100) }) it('进度 100 且冷却中不自动冲击', () => { const w = baseWorld('rp-cooldown') const c = w.state.members['x5'] c.realm = { major: 'qi', minor: 1 } c.realmProgress = 100 c.lastBreakthroughAttempt = (w.state.year * 12 + w.state.month) - 2 w.advanceMonth() expect(c.realmProgress).toBeGreaterThanOrEqual(100) }) }) describe('气血边界矩阵', () => { it.each([-10, 0, 1, 29, 30, 50, 99, 100, 130].map((h) => [h] as const))('气血 %h 不越 0-100', (h) => { const w = baseWorld(`hp-${h}`) const c = w.state.members['x3'] c.health = h w.advanceMonth() expect(c.health).toBeGreaterThanOrEqual(0) expect(c.health).toBeLessThanOrEqual(100) }) }) describe('库存边界矩阵', () => { it.each(['lingcao', 'lingkuang', 'beastcore', 'pill-qiyuan', 'pill-ningyuan', 'weapon-qi'].map((i) => [i] as const))( '库存 %s 交易不为负', (id) => { const w = baseWorld(`inv-${id}`) const before = w.state.family.inventory[id] ?? 0 w.advanceMonth() expect((w.state.family.inventory[id] ?? 0) - before).toBeGreaterThanOrEqual(-20) } ) }) describe('金钱边界矩阵', () => { it.each([0, 1, 49, 50, 799, 800, 100000].map((s) => [s] as const))('灵石 %s 下买卖不穿仓', (s) => { const w = baseWorld(`money-${s}`) const fam = w.state.family fam.stones = s const price = marketPrice(w, 'lingcao') * 5 const bought = buyItem(w, 'lingcao', 5) expect(bought).toBe(s >= price) expect(fam.stones).toBeGreaterThanOrEqual(0) if (sellItem(w, 'lingcao', 1)) { expect(fam.stones).toBeGreaterThanOrEqual(0) } }) }) describe('传承与谱系矩阵', () => { it.each(['x1', 'x2', 'x3', 'x4', 'x5'].map((id) => [id] as const))('%s 列传可生成且不以空结尾', (id) => { const w = baseWorld(`bio-${id}`) const lines = buildBiography(w.state, id) expect(lines.length).toBeGreaterThan(0) expect(lines[lines.length - 1].label).toBeTruthy() }) it.each([0, 50, 200].map((y) => [y] as const))('谱系在 %i 年后仍可构建(不抛)', (y) => { const w = baseWorld(`gen-${y}`) w.state.year = 1 + y const rows = computeGenealogy(w) expect(Array.isArray(rows)).toBe(true) }) it.each([1, 3, 10, 100].map((y) => [y] as const))('年轴在 %i 年跨度下聚合', (y) => { const w = baseWorld(`axis-${y}`) w.state.year = y w.state.chronicle.push({ id: 'c1', year: Math.max(1, y - 2), month: 1, category: 'breakthrough', text: 'xx', important: false }) const cells = yearAxis(w.state) expect(cells.length).toBeGreaterThan(0) }) }) describe('境界战力矩阵', () => { it.each([ ['mortal', 0], ['qi', 0], ['qi', 8], ['foundation', 0], ['core', 0], ['nascent', 0], ['spirit', 0] ] as const)('%s/%i 战力为正且渐强', (major, minor) => { const w = baseWorld(`pw-${major}`) const c = w.state.members['x4'] c.realm = { major, minor } const p = combatPowerOf(w, c) expect(p).toBeGreaterThan(0) if (minor > 0 && major === 'qi') { const c0 = w.state.members['x5'] c0.realm = { major: 'qi', minor: 0 } expect(p).toBeGreaterThan(combatPowerOf(w, c0)) } }) it.each(Object.keys(marketStub()).map((it) => [it] as const))('装备 %s 战力增益为正', (item) => { const w = baseWorld(`eq-${item}`) const c = w.state.members['x4'] const p0 = combatPowerOf(w, c) c.equipment = item expect(combatPowerOf(w, c)).toBeGreaterThan(p0) }) }) function marketStub(): Record { return { 'weapon-fan': 1, 'weapon-qi': 2, 'weapon-ling': 3, 'weapon-fa': 4 } } describe('突破边界矩阵', () => { it.each([ ['mortal', 'qi'], ['qi', 'foundation'], ['foundation', 'core'], ['core', 'nascent'], ['nascent', 'spirit'] ] as const)('%s → %s 成功或失败均不破世界', (from, to) => { const w = baseWorld(`bt-${from}`) const c = w.state.members['x3'] c.realm = { major: from, minor: from === 'mortal' ? 0 : 8 } c.realmProgress = 100 c.mind = 8 resolveBreakthrough(w, c, 0.4) expect(c.realm.major === from || c.realm.major === to).toBe(true) expect(Number.isNaN(c.realmProgress)).toBe(false) }) }) describe('评分与结局矩阵', () => { it.each([0, 30, 55, 85, 120, 160, 200].map((v) => [v] as const))('虚弱档 %i 总评下仍可裁断', (v) => { const w = baseWorld(`legacy-${v}`) w.state.stats.repPeak = v w.state.stats.maxRealmIdx = v w.state.year = v + 1 w.state.stats.feishengCount = 0 const arch = resolveLegacy(w.state) expect(arch.title.length).toBeGreaterThan(0) expect(arch.dims.total).toBeGreaterThan(0) }) it.each(['renXing', 'daoXing', 'weiMing', 'xiangHuo'].map((k) => [k] as const))('四维 %s 分轴单调', (k) => { const w = baseWorld(`dim-${k}`) const d0 = computeLegacy(w.state) if (k === 'weiMing') w.state.stats.repPeak += 30 if (k === 'renXing') w.state.stats.popPeak += 10 if (k === 'daoXing') w.state.stats.maxRealmIdx += 10 if (k === 'xiangHuo') w.state.year += 50 const d1 = computeLegacy(w.state) expect((d1 as never as Record)[k]).toBeGreaterThan((d0 as never as Record)[k]) }) }) describe('跨难度矩阵', () => { it.each(['easy', 'normal', 'hard'].map((d) => [d] as const))('%s 难度 60 月无崩溃', (d) => { const w = World.create({ seed: 'diff-' + d, surname: '萧', familyName: `${d}家`, motto: 'm', difficulty: d as never }) resetSaveBus() attachLogSink(w) for (let i = 0; i < 60; i++) w.advanceMonth() expect(w.state.family.stones).toBeGreaterThanOrEqual(0) }) })