import { describe, expect, it } from 'vitest' import { GameEngine } from '../src/renderer/game/engine/GameEngine' import { makeKernel } from '../src/renderer/game/engine/kernel/Kernel' import { WorldSim } from '../src/renderer/game/engine/sim/WorldSim' import { WORLDSIM } from '../src/renderer/game/engine/sim/worldsim-data' describe('GameEngine 引擎门面', () => { it('构造:内核注入世界 + 门面 + 数据包', () => { const e = new GameEngine({ seed: 'engine-1' }) expect(e.world).toBeTruthy() expect(e.facade).toBeTruthy() expect(e.kernel.clock.subscriptionCount()).toBeGreaterThanOrEqual(10) expect(e.status()).toBe('running') }) it('advance 驱动内核时钟(确定单源)', () => { const e = new GameEngine({ seed: 'engine-2' }) const y0 = e.view().year const m0 = e.view().month e.advance() const crossed = e.view().month === 1 && e.view().year === y0 + 1 const normal = e.view().month === m0 + 1 expect(crossed || normal).toBe(true) }) it('act/query/subscribe 全链路', () => { const e = new GameEngine({ seed: 'engine-3' }) const events: string[] = [] const unsub = e.subscribe((ev) => events.push(ev.type)) expect(e.act('member.post', { memberId: 'x3', post: 'elder' })).toBe(true) e.advance() // 一次 advance 必然产生内核事件流(log/pending/paper 至少其一) expect(events.length).toBeGreaterThan(0) unsub() const fam = e.query('family') as { year: number } expect(fam.year).toBeGreaterThanOrEqual(1) }) it('datapack 覆写生效 + restore 可回放', () => { const e = new GameEngine({ seed: 'engine-4' }) e.datapack.override({ items: { ...e.datapack.current().items, lingcao: { ...e.datapack.current().items['lingcao'], basePrice: 250 } } }) const snap = e.snapshot() expect(snap.family.stones).toBeGreaterThanOrEqual(0) e.restore(snap) expect(e.status()).toBe('running') }) it('about 元数据含 plugins(≥3)与版本 0.1.14', () => { const e = new GameEngine({ seed: 'engine-5' }) const a = e.about() expect(a.plugins).toBeGreaterThanOrEqual(3) expect(a.version).toContain('0.1.14') }) }) describe('Kernel 三合一', () => { it('时钟/随机/总线协同(同 seed 同随机序列)', () => { const k1 = makeKernel('kernel-a') const k2 = makeKernel('kernel-a') expect(k1.rng.next()).toBe(k2.rng.next()) expect(k1.clock).toBeTruthy() expect(typeof k1.bus.onLog).toBe('function') }) }) function runEngine(e: GameEngine, months: number): void { for (let i = 0; i < months; i++) { if (e.view().gameOver) break e.advance() // 引擎语义:遇到待决事件停下来等处理;自动化直接取第一个选项继续 let guard = 0 while (e.view().pendingEvent && guard < 4) { e.resolvePending(0) guard++ } } } describe('WorldSim 世界健康(长跑)', () => { it.each(['ws-1', 'ws-2', 'ws-3'].map((s) => [s] as const))('%s:1000 月市场池/灵气在界、世界不崩', (seed) => { const e = new GameEngine({ seed }) runEngine(e, 1000) const ws = e.view().worldSim as (Record & { marketPool: Record secretQi: Record tide: number }) | undefined expect(ws).toBeTruthy() if (ws) { for (const v of Object.values(ws.marketPool)) expect(v).toBeGreaterThan(0) for (const v of Object.values(ws.secretQi ?? {})) expect(v).toBeGreaterThanOrEqual(0) expect(ws.tide).toBeGreaterThan(0.5) expect(ws.tide).toBeLessThan(1.4) } }) it('NPC 换代至少发生一次(世界在演化)', () => { const e = new GameEngine({ seed: 'ws-evolve' }) runEngine(e, 1200) const ws = e.view().worldSim as { npcSuccessions?: number } | undefined expect(ws?.npcSuccessions ?? 0).toBeGreaterThan(0) }) it('快讯持有且裁剪符合 N(≤120)', () => { const e = new GameEngine({ seed: 'ws-news' }) runEngine(e, 400) const ws = e.view().worldSim as { newsFeed?: unknown[] } | undefined expect(ws?.newsFeed.length ?? 0).toBeLessThanOrEqual(WORLDSIM.newsKeep) }) }) describe('Sim 与外层联动', () => { it('tideMult 在界(0.5-1.4)且随年景变化', () => { const e = new GameEngine({ seed: 'ws-tide-link' }) const sim = new WorldSim(e.world) const before = sim.tideMult() expect(before).toBeGreaterThanOrEqual(0.5) expect(before).toBeLessThan(1.4) }) })