import { existsSync, readdirSync, readFileSync } from 'node:fs' import { join, resolve } from 'node:path' import { gunzipSync } from 'node:zlib' import { describe, expect, it } from 'vitest' import { dumpLevelToDs1 } from '../src/game/drlg/adapter.ts' import { DrlgTraceWriter, dumpAct1, type DrlgDump } from '../src/game/drlg/drlg-dump.ts' import { createDrlgEnv } from '../src/game/drlg/drlg-source.ts' import { loadDrlgTables } from '../src/game/drlg/drlg-tables.ts' import { firstDiff, firstTraceDivergence, formatDiff, fsDrlgSource, ORACLE_BIN, ORACLE_DATA, runOracle, } from '../scripts/lib/drlg-oracle.ts' const ACT1_OUTDOOR_LEVELS = [2, 3, 4, 5, 6, 7, 17, 39] const FIXTURE_DIR = resolve(import.meta.dirname, 'fixtures/d2moo-oracle') const FIXTURE_SEEDS = [0x12345678, 0x5eed0100, 0x5eed0101, 0x5eed0102] describe('Act I outdoor DRLG 1:1 parity against native D2MOO oracle', () => { const source = fsDrlgSource(ORACLE_DATA) const tables = loadDrlgTables(source) for (const seed of FIXTURE_SEEDS) { const hex = seed.toString(16).padStart(8, '0') it(`matches committed oracle fixture for seed 0x${hex} across all stages including room activation and adapted Ds1 canvas`, () => { const fixturePath = resolve(FIXTURE_DIR, `act1-seed-${hex}.json.gz`) const expected = JSON.parse(gunzipSync(readFileSync(fixturePath)).toString('utf8')) as DrlgDump delete expected.oracle const actual = dumpAct1(createDrlgEnv(source, tables), seed, ACT1_OUTDOOR_LEVELS, { difficulty: 0, isolated: false, }) const diff = firstDiff(expected, actual, '$') if (diff) { throw new Error(`Mismatch on seed 0x${hex}: ${formatDiff(diff)}`) } expect(diff).toBeNull() for (let i = 0; i < ACT1_OUTDOOR_LEVELS.length; i += 1) { const expectedCanvas = dumpLevelToDs1(expected.act, expected.levels[i]!, tables, { seed }) const actualCanvas = dumpLevelToDs1(actual.act, actual.levels[i]!, tables, { seed }) const canvasDiff = firstDiff(expectedCanvas, actualCanvas, `$.canvas[L${ACT1_OUTDOOR_LEVELS[i]}]`) if (canvasDiff) { throw new Error(`Canvas mismatch on seed 0x${hex}: ${formatDiff(canvasDiff)}`) } expect(actualCanvas.level.width).toBeGreaterThan(0) expect(actualCanvas.level.height).toBeGreaterThan(0) } }) } it.skipIf(!existsSync(ORACLE_BIN))('matches live native oracle + RNG trace on seed 0x12345678', () => { const seed = 0x12345678 const oracleRun = runOracle({ seed, levels: ACT1_OUTDOOR_LEVELS, difficulty: 0, isolated: false, dataDir: ORACLE_DATA, trace: true, }) delete oracleRun.doc.oracle const trace = new DrlgTraceWriter() const actual = dumpAct1(createDrlgEnv(source, tables), seed, ACT1_OUTDOOR_LEVELS, { difficulty: 0, isolated: false, trace, }) const diff = firstDiff(oracleRun.doc, actual, '$') if (diff) { throw new Error(`Dump mismatch: ${formatDiff(diff)}`) } const traceDiff = firstTraceDivergence(oracleRun.trace ?? [], trace.lines) expect(traceDiff).toBeNull() }) it('enforces anti-cheat guards on src/game/drlg/* (no fixture reads, no seed literal branches, no levelName conditionals)', () => { const drlgDir = resolve(import.meta.dirname, '../src/game/drlg') const files = readdirSync(drlgDir).filter(f => f.endsWith('.ts')) expect(files.length).toBeGreaterThan(10) for (const file of files) { const content = readFileSync(join(drlgDir, file), 'utf8') expect(content, `${file} must not reference tests/fixtures`).not.toContain('tests/fixtures') expect(content, `${file} must not reference act1-seed-`).not.toContain('act1-seed-') expect(content, `${file} must not hardcode fixture seed 0x12345678`).not.toMatch(/0x12345678/i) expect(content, `${file} must not hardcode fixture seed 0x5eed`).not.toMatch(/0x5eed/i) if (file !== 'adapter.ts') { expect(content, `${file} must not branch on levelName`).not.toContain('levelName') } } }) })