import { describe, expect, it } from 'vitest' import { World } from '../src/renderer/game/engine/runtime/World' import { generateWorld } from '../src/renderer/game/engine/sim/worldgen' import { marketPrice, buyItem, sellItem } from '../src/renderer/game/engine/sim/Market' import { POOL_BASE } from '../src/renderer/game/engine/sim/worldsim-data' describe('矩阵:世界生成', () => { const SEEDS = Array.from({ length: 12 }, (_, i) => `wg-m${i}`) it.each(SEEDS)('seed %s:家数 4~8 / 无重名 / 关系对称 / era 合法', (seed) => { const g = generateWorld(seed) expect(g.npcs.length).toBeGreaterThanOrEqual(4) expect(g.npcs.length).toBeLessThanOrEqual(8) expect(new Set(g.npcs.map((n) => n.name)).size).toBe(g.npcs.length) for (const [k, rels] of Object.entries(g.relations)) { for (const [oid, v] of Object.entries(rels)) { expect(g.relations[oid]?.[k]).toBe(v) } } expect(['shengshi', 'pingshi', 'luanshi']).toContain(g.eras) }) it.each([0, 1, 2, 3, 4].map((i) => [`seed-${i}`]))('seed %s:世仇/盟友对生且目标存在', (seed) => { const g = generateWorld(seed) const ids = new Set(g.npcs.map((n) => n.id)) for (const [a, b] of g.feuds) expect(ids.has(a) && ids.has(b)).toBe(true) for (const [a, b] of g.alliances) expect(ids.has(a) && ids.has(b)).toBe(true) }) it.each(SEEDS.slice(0, 6))('seed %s:首月生成世界后生活延续 60 月不崩', (seed) => { const w = World.create({ seed, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) for (let i = 0; i < 60; i++) w.advanceMonth() expect(w.state.year).toBeGreaterThanOrEqual(5) }) }) describe('矩阵:市场实体化', () => { const GOODS = ['lingcao', 'lingkuang', 'beastcore', 'pill-qiyuan', 'pill-ningyuan'] it.each(GOODS)('%s:价格恒 ≥1 且随深池/浅池方向单调', (id) => { const w = World.create({ seed: `mk-${id}`, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) w.advanceMonth() const ws = w.state.worldSim as { marketPool: Record } const base = POOL_BASE[id] ?? 100 ws.marketPool[id] = base * 0.3 const cheap = marketPrice(w, id) ws.marketPool[id] = base * 2.0 const dear = marketPrice(w, id) expect(cheap).toBeGreaterThanOrEqual(1) expect(dear).toBeGreaterThan(cheap) }) it.each(GOODS)('%s:买后池浅、卖后池回(tradeSettle 单向因果)', (id) => { const w = World.create({ seed: `mkb-${id}`, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) w.advanceMonth() const ws = w.state.worldSim as { marketPool: Record } const p0 = ws.marketPool[id]! const okBuy = buyItem(w, id, 2) expect(okBuy).toBe(true) expect(ws.marketPool[id]!).toBeLessThan(p0) sellItem(w, id, 2) expect(ws.marketPool[id]!).toBeGreaterThan(ws.marketPool[id]! - 1) }) it.each([['深池', 2.3], ['中池', 1.0], ['浅池', 0.4]] as const)('%s:价格乘子随池深有界(clamp 生效)', (label, depth) => { const w = World.create({ seed: `md-${label}`, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) w.advanceMonth() const ws = w.state.worldSim as { marketPool: Record } ws.marketPool['lingcao'] = 600 * depth const price = marketPrice(w, 'lingcao') const base8 = 8 const ratio = price / (base8 * (1 - 0) ) expect(ratio).toBeGreaterThan(0.4) expect(ratio).toBeLessThan(3) }) it.each(GOODS)('%s:断供拒单(池深 < 0.35)后供给回填仍可成交', (id) => { const w = World.create({ seed: `ms-${id}`, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) w.advanceMonth() w.state.totalTicks = 60 const ws = w.state.worldSim as { marketPool: Record } ws.marketPool[id] = POOL_BASE[id] * 0.2 expect(buyItem(w, id, 1)).toBe(false) // 世界供给呼吸数年后回填 ws.marketPool[id] = POOL_BASE[id] * 1.5 expect(buyItem(w, id, 1)).toBe(true) }) it.each(GOODS)('%s:超卖不破顶且谷贱播报触发条件存在', (id) => { const w = World.create({ seed: `mo-${id}`, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) w.advanceMonth() const ws = w.state.worldSim as { marketPool: Record } ws.marketPool[id] = POOL_BASE[id] * 3 for (let i = 0; i < 6; i++) w.advanceMonth() expect(ws.marketPool[id]).toBeLessThanOrEqual(POOL_BASE[id] * 2.5) }) }) describe('矩阵:灵气潮汐与时代', () => { it.each([0, 1, 2, 3, 4, 5].map((i) => [`t-${i}`]))('seed %s:tide 恒在 [0.6, 1.4]', (seed) => { const w = World.create({ seed, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) for (let i = 0; i < 80; i++) { w.advanceMonth() const t = (w.state.worldSim as { tide?: number })?.tide ?? 1 expect(t).toBeGreaterThan(0.6) expect(t).toBeLessThanOrEqual(1.4000001) } }) it.each([0, 1, 2].map((i) => [`era-${i}`]))('seed %s:era 长期在四态内且年龄窗口合法', (seed) => { const w = World.create({ seed, surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' }) let seen = new Set() for (let i = 0; i < 600; i++) { w.advanceMonth() const e = (w.state.worldSim as { era?: string })?.era ?? 'pingshi' expect(['shengshi', 'pingshi', 'luanshi', 'mofa']).toContain(e) seen.add(e) } expect(seen.size).toBeGreaterThanOrEqual(1) }) })