/** * Wilderness D2MOO Ground Truth Parity Test Suite (Issue #455). * * Verifies that outdoor wilderness level generation adheres with 100% fidelity * to Diablo II v1.13c ground truth as established by D2MOO C++ DRLG engine * (`D2Common/src/Drlg/`) and native binary execution (`d2moo_mapgen`). * * Multi-Tier Parity Coverage: * - Tier 1: Dynamic Level Dimensions (96x56 horizontal vs 56x96 vertical) & Topology (R1) * - Tier 2: Outer River Placement, Dual Columns (x=80, 88), and Boundary Suppression (R2) * - Tier 3: Town Transition (TownSTrans.ds1 56x8) Anchored to Eastern River Edge (R4) * - Tier 4: Unconditional Landmark Presets & FarAway Anti-Overlap Metric (R4) * - Tier 5: 4-Pass Secondary Border Evolution & Interior 2x3 Forest Closures (Bord9..12) (R3) * - Tier 6: Orthogonal Polyline Dirt Paths & Width-2 Bresenham Rasterization (R5) */ import { describe, it, expect, beforeAll } from 'vitest' import { existsSync, readFileSync } from 'node:fs' import { execFileSync } from 'node:child_process' import { join } from 'node:path' import { tmpdir } from 'node:os' import { MountedArchives } from '../src/mpq/mount.ts' import { MpqArchive } from '../src/mpq/archive.ts' import { fileSource } from '../src/mpq/file-source.ts' import { loadActTables, parseTable, cell, tileMemberPath, type D2Table } from '../src/game/acts.ts' import { decodeDs1, type Ds1 } from '../src/formats/ds1.ts' import { generateWilderness, type WildernessPiece, type WildernessSubstitution, type WildernessResult, } from '../src/game/wilderness.ts' /* ------------------------------------------------------------------------- * * D2MOO Data Contracts & Types * ------------------------------------------------------------------------- */ export interface OutdoorCellJson { readonly x: number readonly y: number readonly fPacked: number readonly fStyle: number readonly fSeq: number readonly wPacked: number readonly wStyle: number readonly wSeq: number readonly wType: number readonly dirt: number } export interface D2MooRoomJson { readonly index: number readonly type: number readonly tileX: number readonly tileY: number readonly tileW: number readonly tileH: number readonly mapPtr: number readonly mapX: number readonly mapY: number readonly mapW: number readonly mapH: number readonly lvlPrestId: number readonly pickedFile: number readonly ds1File: string readonly ds1Width: number readonly ds1Height: number readonly doorMask: number readonly orthCount: number readonly outdoorCells: readonly OutdoorCellJson[] } export interface D2MooLevelJson { readonly levelId: number readonly levelName: string readonly act: number readonly drlgType: number readonly levelType: number readonly seed: number readonly posX: number readonly posY: number readonly width: number readonly height: number readonly roomCount: number readonly rooms: readonly D2MooRoomJson[] } /* ------------------------------------------------------------------------- * * Dual-Mode Execution & Fixture Loader * ------------------------------------------------------------------------- */ const D2MOO_BIN = process.env.D2MOO_BIN ?? '/usr/local/google/home/taodao/tmp/d2moo_runner/d2moo_mapgen' const FIXTURES_DIR = 'tests/fixtures/d2moo' const hasD2Archives = existsSync('samples/d2/d2data.mpq') /** * Loads a D2MOO level layout either by executing `d2moo_mapgen` dynamically * or by reading the pre-recorded golden fixture from `tests/fixtures/d2moo/`. */ export function loadOrRunD2MooLevel(levelId: number, seed: number | string): D2MooLevelJson { const seedNum = typeof seed === 'number' ? seed : (seed.startsWith('0x') ? parseInt(seed, 16) : parseInt(seed, 10)) const seedHex = `0x${seedNum.toString(16)}` const seedDec = seedNum.toString(10) // 1. If native binary is present and not explicitly bypassed, run dynamically if (existsSync(D2MOO_BIN) && process.env.D2MOO_FORCE_FIXTURE !== '1') { try { const tmpOut = join(tmpdir(), `d2moo_parity_${levelId}_${seedNum.toString(16)}.json`) execFileSync(D2MOO_BIN, [String(levelId), seedHex, '0', tmpOut], { stdio: 'ignore' }) if (existsSync(tmpOut)) { return JSON.parse(readFileSync(tmpOut, 'utf8')) as D2MooLevelJson } } catch { // Fallback to pre-recorded fixture } } // 2. Fallback to pre-recorded golden JSON fixture const candidateNames = [ `level${levelId}_seed_0x${seedNum.toString(16)}.json`, `level${levelId}_seed_${seedDec}.json`, `level${levelId}_seed_${seedNum.toString(16)}.json`, `level_${levelId}_seed_${seedNum.toString(16)}.json`, ] for (const name of candidateNames) { const p = join(FIXTURES_DIR, name) if (existsSync(p)) { return JSON.parse(readFileSync(p, 'utf8')) as D2MooLevelJson } } throw new Error(`Neither executable d2moo_mapgen nor golden fixture found for Level ${levelId} Seed ${seedHex}`) } /** * Extracts distinct preset stamp instances (deduplicating multi-room presets sharing mapPtr). */ export function getDistinctPresetStamps(level: D2MooLevelJson): Array<{ file: string x: number y: number w: number h: number }> { const seenMapPtrs = new Set() const stamps: Array<{ file: string; x: number; y: number; w: number; h: number }> = [] for (const room of level.rooms) { if (!room.ds1File) continue if (seenMapPtrs.has(room.mapPtr)) continue seenMapPtrs.add(room.mapPtr) const file = room.ds1File.split('/').pop() ?? room.ds1File stamps.push({ file, x: room.mapX - level.posX, y: room.mapY - level.posY, w: room.mapW, h: room.mapH, }) } return stamps } /** * Extracts absolute world tile coordinates of all dirt road cells in the level. */ export function getDirtRoadCells(level: D2MooLevelJson): Array<{ x: number; y: number }> { const cells: Array<{ x: number; y: number }> = [] for (const room of level.rooms) { for (const cellRecord of room.outdoorCells) { if (cellRecord.dirt !== 0) { cells.push({ x: room.tileX - level.posX + cellRecord.x, y: room.tileY - level.posY + cellRecord.y, }) } } } return cells } /** * Analyzes connectivity of road cells via 8-way BFS flood-fill. */ export function evaluateRoadConnectivity(cells: Array<{ x: number; y: number }>): { totalCells: number componentCount: number largestComponentSize: number } { const cellSet = new Set(cells.map(c => `${c.x},${c.y}`)) const visited = new Set() const components: string[][] = [] for (const key of cellSet) { if (visited.has(key)) continue const queue = [key] visited.add(key) const comp = [key] while (queue.length > 0) { const current = queue.shift()! const [cx, cy] = current.split(',').map(Number) for (const [dx, dy] of [ [1, 0], [-1, 0], [0, 1], [0, -1], [1, 1], [1, -1], [-1, 1], [-1, -1], ]) { const neighbor = `${cx + dx},${cy + dy}` if (cellSet.has(neighbor) && !visited.has(neighbor)) { visited.add(neighbor) queue.push(neighbor) comp.push(neighbor) } } } components.push(comp) } return { totalCells: cells.length, componentCount: components.length, largestComponentSize: components[0]?.length ?? 0, } } /* ========================================================================= * * SUITE 1: D2MOO v1.13c Ground Truth Oracle & Fixture Invariants * ========================================================================= */ describe('Part 1: D2MOO Ground Truth Fixture & Runner Verification (Issue #455)', () => { // --- Tier 1: Dimensions & Orientations --- describe('Tier 1: Dynamic Level Dimensions & Topology Invariants (R1)', () => { it('verifies horizontal 96x56 Moor layout for Seed 0x12345678', () => { const d2moo = loadOrRunD2MooLevel(2, 0x12345678) expect(d2moo.width).toBe(96) expect(d2moo.height).toBe(56) expect(d2moo.width / 8).toBe(12) expect(d2moo.height / 8).toBe(7) expect(d2moo.roomCount).toBe(82) }) it('verifies horizontal 96x56 Moor layout for Seed 12345', () => { const d2moo = loadOrRunD2MooLevel(2, 12345) expect(d2moo.width).toBe(96) expect(d2moo.height).toBe(56) expect(d2moo.width / 8).toBe(12) expect(d2moo.height / 8).toBe(7) expect(d2moo.roomCount).toBe(84) }) it('verifies vertical 56x96 Moor layout for Seed 1', () => { const d2moo = loadOrRunD2MooLevel(2, 1) expect(d2moo.width).toBe(56) expect(d2moo.height).toBe(96) expect(d2moo.width / 8).toBe(7) expect(d2moo.height / 8).toBe(12) expect(d2moo.roomCount).toBe(79) }) it('verifies vertical 56x96 Moor layout for Seed 42', () => { const d2moo = loadOrRunD2MooLevel(2, 42) expect(d2moo.width).toBe(56) expect(d2moo.height).toBe(96) expect(d2moo.width / 8).toBe(7) expect(d2moo.height / 8).toBe(12) expect(d2moo.roomCount).toBe(82) }) it('verifies Cold Plains (Level 3) fixed 80x80 layout for Seed 1', () => { const d2moo = loadOrRunD2MooLevel(3, 1) expect(d2moo.width).toBe(80) expect(d2moo.height).toBe(80) expect(d2moo.roomCount).toBe(97) }) it('verifies Stony Field (Level 4) fixed 80x80 layout for Seed 1', () => { const d2moo = loadOrRunD2MooLevel(4, 1) expect(d2moo.width).toBe(80) expect(d2moo.height).toBe(80) expect(d2moo.roomCount).toBe(91) }) }) // --- Tier 2: Outer River Placement --- describe('Tier 2: Outer River Placement & Boundary Suppression (R2)', () => { it('spawns dual-column river on eastern boundary (x=80, x=88) for Seed 0x12345678', () => { const d2moo = loadOrRunD2MooLevel(2, 0x12345678) const riverRooms = d2moo.rooms.filter(r => r.ds1File && r.ds1File.includes('River')) expect(riverRooms.length).toBeGreaterThanOrEqual(12) const uRiverRooms = riverRooms.filter(r => r.ds1File.includes('URiverC')) const lRiverRooms = riverRooms.filter(r => r.ds1File.includes('LRiverC')) expect(uRiverRooms.length).toBeGreaterThanOrEqual(6) expect(lRiverRooms.length).toBeGreaterThanOrEqual(6) // Column x = 80 (block 10, nGridWidth - 2) for URiverC for (const room of uRiverRooms) { expect(room.mapX - d2moo.posX).toBe(80) } // Column x = 88 (block 11, nGridWidth - 1) for LRiverC for (const room of lRiverRooms) { expect(room.mapX - d2moo.posX).toBe(88) } // Outer boundary tree wall suppression: no Bord*.ds1 rooms exist at x = 88 where river flows const eastBorders = d2moo.rooms.filter(r => { const file = r.ds1File?.split('/').pop() ?? '' const relX = r.mapX - d2moo.posX return file.startsWith('Bord') && relX === 88 }) expect(eastBorders.length).toBe(0) }) it('spawns dual-column river on eastern boundary (x=80, x=88) for Seed 12345', () => { const d2moo = loadOrRunD2MooLevel(2, 12345) const uRiverRooms = d2moo.rooms.filter(r => r.ds1File && r.ds1File.includes('URiverC')) const lRiverRooms = d2moo.rooms.filter(r => r.ds1File && r.ds1File.includes('LRiverC')) expect(uRiverRooms.length).toBeGreaterThanOrEqual(6) expect(lRiverRooms.length).toBeGreaterThanOrEqual(6) for (const room of uRiverRooms) expect(room.mapX - d2moo.posX).toBe(80) for (const room of lRiverRooms) expect(room.mapX - d2moo.posX).toBe(88) }) }) // --- Tier 3: Town Transition Alignment --- describe('Tier 3: Town Transition Alignment & River Edge Anchoring (R4)', () => { it('places TownSTrans.ds1 (56x8) anchored to East river at (40, 0) for Seed 0x12345678', () => { const d2moo = loadOrRunD2MooLevel(2, 0x12345678) const townRoom = d2moo.rooms.find(r => r.ds1File && r.ds1File.includes('TownSTrans')) expect(townRoom).toBeDefined() expect(townRoom!.mapX - d2moo.posX).toBe(40) expect(townRoom!.mapY - d2moo.posY).toBe(0) expect(townRoom!.mapW).toBe(56) expect(townRoom!.mapH).toBe(8) // 40 + 56 = 96: transition extends exactly to the eastern river edge (x = 80..96) expect(townRoom!.mapX - d2moo.posX + townRoom!.mapW).toBe(d2moo.width) }) }) // --- Tier 4: Landmark Presets & FarAway Anti-Overlap Metric --- describe('Tier 4: Complete Landmark Presets & FarAway Distribution (R4)', () => { it('unconditionally places all 5 canonical landmark categories in Blood Moor (Seed 0x12345678)', () => { const d2moo = loadOrRunD2MooLevel(2, 0x12345678) const uniquePresets = new Set(d2moo.rooms.map(r => r.ds1File.split('/').pop()).filter(Boolean)) expect(uniquePresets.has('DenEnt.ds1')).toBe(true) expect(uniquePresets.has('Pond1.ds1')).toBe(true) expect(uniquePresets.has('Cott1.ds1')).toBe(true) expect(uniquePresets.has('Circle.ds1')).toBe(true) expect(uniquePresets.has('Clover.ds1')).toBe(true) }) it('verifies zero spatial overlap across all distinct preset stamps', () => { for (const [lvl, s] of [ [2, 0x12345678], [2, 12345], [2, 1], [2, 42], [3, 1], [4, 1], ] as const) { const d2moo = loadOrRunD2MooLevel(lvl, s) const stamps = getDistinctPresetStamps(d2moo) for (let i = 0; i < stamps.length; i += 1) { for (let j = i + 1; j < stamps.length; j += 1) { const a = stamps[i]! const b = stamps[j]! const xOverlap = Math.max(0, Math.min(a.x + a.w, b.x + b.w) - Math.max(a.x, b.x)) const yOverlap = Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y)) expect( xOverlap > 0 && yOverlap > 0, `Overlap detected in Level ${lvl} Seed ${s} between ${a.file} and ${b.file}`, ).toBe(false) } } } }) it('verifies FarAway metric (|dx| + 2*|dy|) places Den of Evil distant from Town Gate', () => { const d2moo = loadOrRunD2MooLevel(2, 0x12345678) const townRoom = d2moo.rooms.find(r => r.ds1File.includes('TownSTrans'))! const denRoom = d2moo.rooms.find(r => r.ds1File.includes('DenEnt'))! const townX = townRoom.mapX - d2moo.posX const townY = townRoom.mapY - d2moo.posY const denX = denRoom.mapX - d2moo.posX const denY = denRoom.mapY - d2moo.posY // FarAway Manhattan metric: |dx| + 2*|dy| const distance = Math.abs(denX - townX) + 2 * Math.abs(denY - townY) expect(distance).toBeGreaterThanOrEqual(48) }) it('verifies canonical presets in Cold Plains (Level 3) and Stony Field (Level 4)', () => { const l3 = loadOrRunD2MooLevel(3, 1) const l3Presets = new Set(l3.rooms.map(r => r.ds1File.split('/').pop()).filter(Boolean)) expect(l3Presets.has('CaveDr1.ds1')).toBe(true) expect(l3Presets.has('FallCmpBish.ds1')).toBe(true) expect(l3Presets.has('Cott1A.ds1')).toBe(true) expect(l3Presets.has('Circle.ds1')).toBe(true) expect(l3Presets.has('Clover.ds1')).toBe(true) const l4 = loadOrRunD2MooLevel(4, 1) const l4Presets = new Set(l4.rooms.map(r => r.ds1File.split('/').pop()).filter(Boolean)) expect(l4Presets.has('cairn2.ds1')).toBe(true) expect(l4Presets.has('clfcave.ds1')).toBe(true) expect(l4Presets.has('tome.ds1')).toBe(true) expect(l4Presets.has('Corral1.ds1')).toBe(true) expect(l4Presets.has('FallCmp.ds1')).toBe(true) }) }) // --- Tier 5: 4-Pass Secondary Borders & 2x3 Forest Closures --- describe('Tier 5: 4-Pass Secondary Border Evolution & 2x3 Closures (R3)', () => { it('spawns Bord5..8 outer peninsulas and Bord9..12 interior 2x3 forest closures', () => { for (const [lvl, s] of [ [2, 0x12345678], [2, 12345], [2, 1], [2, 42], [3, 1], [4, 1], ] as const) { const d2moo = loadOrRunD2MooLevel(lvl, s) const files = d2moo.rooms.map(r => r.ds1File.split('/').pop()).filter(Boolean) as string[] const peninsulas = files.filter(f => /^Bord[5678]\.ds1$/i.test(f)) const closures = files.filter(f => /^Bord(9|10|11|12)\.ds1$/i.test(f)) expect(peninsulas.length, `Level ${lvl} Seed ${s} missing Bord5..8 peninsulas`).toBeGreaterThan(0) expect(closures.length, `Level ${lvl} Seed ${s} missing Bord9..12 closures`).toBeGreaterThanOrEqual(4) } }) }) // --- Tier 6: Dirt Path Connectivity --- describe('Tier 6: Orthogonal Polyline Dirt Paths & Connectivity (R5)', () => { it('generates 100% contiguous dirt road network connecting gate, den, and exits', () => { for (const s of [0x12345678, 12345, 1, 42] as const) { const d2moo = loadOrRunD2MooLevel(2, s) const dirtCells = getDirtRoadCells(d2moo) expect(dirtCells.length, `Level 2 Seed ${s} must have dirt road cells`).toBeGreaterThanOrEqual(100) const connectivity = evaluateRoadConnectivity(dirtCells) expect( connectivity.componentCount, `Level 2 Seed ${s} road cells should form a single connected component`, ).toBe(1) expect(connectivity.largestComponentSize).toBe(dirtCells.length) } }) }) }) /* ========================================================================= * * SUITE 2: Wilderness Generator Ground Truth Parity (Issue #455 Milestones) * ========================================================================= */ describe.skipIf(!hasD2Archives)('Part 2: DRLG Wilderness Generator Parity vs D2MOO (Milestones M1..M6)', () => { let act1Pieces: WildernessPiece[] let act1Subs: WildernessSubstitution[] const WILDERNESS_PIECE_FAMILIES = [ 'Act 1 - Wild', 'Act 1 - Town 1 Transition', 'Act 1 - Cave Entrance', 'Act 1 - DOE Entrance', 'Act 1 - Corral Fill', 'Act 1 - Fence Fill', 'Act 1 - River', 'Act 1 - Bridge', 'Act 1 - Bivouac', 'Act 1 - Pond', 'Act 1 - Swamp Fill', 'Act 1 - Stone Fill', 'Act 1 - Cottages', 'Act 1 - Fallen Camp', 'Act 1 - Camp', 'Act 1 - Cairn Stones', 'Act 1 - Inifus', 'Act 1 - Tower', 'Act 1 - Ruin', 'Act 1 - Tree Fill', 'Act 1 - Graveyard', ] beforeAll(async () => { const archives = new MountedArchives() for (const name of ['d2data.mpq', 'd2exp.mpq', 'Patch_D2.mpq']) { try { archives.add(name, await MpqArchive.open(await fileSource(`samples/d2/${name}`))) } catch { // Skip optional archive if missing } } const actTables = await loadActTables(archives) const lvlsubTable = parseTable(await archives.read('data\\global\\excel\\LvlSub.txt')) const ds1Cache = new Map() async function loadDs1(relative: string): Promise { const member = tileMemberPath(relative) const cached = ds1Cache.get(member) if (cached !== undefined) return cached const decoded = decodeDs1(await archives.read(member)) ds1Cache.set(member, decoded) return decoded } async function rowDs1s(table: D2Table, row: readonly string[]): Promise { const levels: Ds1[] = [] for (let slot = 1; slot <= 6; slot += 1) { const val = cell(table, row, `File${String(slot)}`) if (val === '' || val === '0') continue levels.push(await loadDs1(val)) } return levels } // Load Act 1 wilderness preset pieces act1Pieces = [] for (const row of actTables.lvlprest.rows) { const name = cell(actTables.lvlprest, row, 'Name') if (!WILDERNESS_PIECE_FAMILIES.some(fam => name.startsWith(fam))) continue const levels = await rowDs1s(actTables.lvlprest, row) if (levels.length === 0) continue act1Pieces.push({ name, levels, border: /\bBorder\b/i.test(name) }) } // Load Act 1 substitutions (SubTypes 0, 1, 6) act1Subs = [] for (const row of lvlsubTable.rows) { const subType = Number(cell(lvlsubTable, row, 'Type')) if (subType !== 0 && subType !== 1 && subType !== 6) continue const file = cell(lvlsubTable, row, 'File') if (file === '' || file === '0') continue const levels = [await loadDs1(file)] act1Subs.push({ name: cell(lvlsubTable, row, 'Name'), type: subType, gridSize: Number(cell(lvlsubTable, row, 'GridSize')) || 1, bordType: Number(cell(lvlsubTable, row, 'BordType')), dt1Mask: Number(cell(lvlsubTable, row, 'Dt1Mask')) || 0, prob: [0, 1, 2, 3, 4].map(i => Number(cell(lvlsubTable, row, `Prob${String(i)}`)) || 0), trials: [0, 1, 2, 3, 4].map(i => Number(cell(lvlsubTable, row, `Trials${String(i)}`)) || 0), max: [0, 1, 2, 3, 4].map(i => Number(cell(lvlsubTable, row, `Max${String(i)}`)) || 0), levels, }) } }) // --- Milestone M1: Dynamic Dimensions --- describe('Milestone M1 Parity: Dynamic Level Dimensions (R1)', () => { it('generates 96x56 horizontal Moor for Seed 0x12345678', () => { const d2moo = loadOrRunD2MooLevel(2, 0x12345678) const result: WildernessResult = generateWilderness({ levelId: 2, levelName: 'Blood Moor', levelTypeName: 'Act 1 - Wilderness', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 0x12345678, pieces: act1Pieces, substitutions: act1Subs, }) expect(result.level.width).toBe(d2moo.width) expect(result.level.height).toBe(d2moo.height) }) it('generates 56x96 vertical Moor for Seed 1', () => { const d2moo = loadOrRunD2MooLevel(2, 1) const result: WildernessResult = generateWilderness({ levelId: 2, levelName: 'Blood Moor', levelTypeName: 'Act 1 - Wilderness', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 1, pieces: act1Pieces, substitutions: act1Subs, }) expect(result.level.width).toBe(d2moo.width) expect(result.level.height).toBe(d2moo.height) }) }) // --- Milestone M2: Outer River Placement --- describe('Milestone M2 Parity: Outer River Placement & Boundary Replacement (R2)', () => { it('spawns river along eastern boundary replacing outer tree border for Seed 0x12345678', () => { const result: WildernessResult = generateWilderness({ levelId: 2, levelName: 'Blood Moor', levelTypeName: 'Act 1 - Wilderness', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 0x12345678, pieces: act1Pieces, substitutions: act1Subs, }) // River tiles on east edge: x in [80..95] contains water floors let hasWaterTile = false const maxX = result.level.width const maxY = result.level.height for (let y = 8; y < maxY - 8; y += 1) { for (let x = Math.max(0, maxX - 16); x < maxX; x += 1) { const row = result.level.cells[y] if (row?.[x]?.floors.some(f => f.style === 2)) { hasWaterTile = true break } } if (hasWaterTile) break } expect(hasWaterTile).toBe(true) }) }) // --- Milestone M4: Town Transition Alignment --- describe('Milestone M4 Parity: Town Transition Alignment (R4)', () => { it('aligns TownSTrans gate to river edge for Seed 0x12345678', () => { const result: WildernessResult = generateWilderness({ levelId: 2, levelName: 'Blood Moor', levelTypeName: 'Act 1 - Wilderness', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 0x12345678, pieces: act1Pieces, substitutions: act1Subs, }) const notes = result.stats.notes ?? [] const hasTownTrans = notes.some(n => /Town.*Trans/i.test(n)) expect(hasTownTrans).toBe(true) }) }) // --- Milestone M4: Unconditional Landmarks --- describe('Milestone M4 Parity: Unconditional Landmark Presets & FarAway (R4)', () => { it('places all 5 canonical landmark categories in Blood Moor (Seed 0x12345678)', () => { const result: WildernessResult = generateWilderness({ levelId: 2, levelName: 'Blood Moor', levelTypeName: 'Act 1 - Wilderness', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 0x12345678, pieces: act1Pieces, substitutions: act1Subs, }) const notes = (result.stats.notes ?? []).join('\n') expect(/DOE|Den/i.test(notes)).toBe(true) expect(/Pond/i.test(notes)).toBe(true) expect(/Cottage|Bivouac/i.test(notes)).toBe(true) expect(/Circle|Stone/i.test(notes)).toBe(true) expect(/Clover/i.test(notes)).toBe(true) }) }) // --- Milestone M3: Secondary Borders & 2x3 Forest Closures --- describe('Milestone M3 Parity: Secondary Borders & 2x3 Forest Closures (R3)', () => { it('spawns Bord5..8 peninsulas and Bord9..12 interior 2x3 closures for Seed 0x12345678', () => { const result: WildernessResult = generateWilderness({ levelId: 2, levelName: 'Blood Moor', levelTypeName: 'Act 1 - Wilderness', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 0x12345678, pieces: act1Pieces, substitutions: act1Subs, }) const notes = (result.stats.notes ?? []).join('\n') // Must record secondary border peninsulas and interior 2x3 closures expect(/Bord[5678]|peninsula/i.test(notes)).toBe(true) expect(/Bord(9|10|11|12)|2x3|closure/i.test(notes)).toBe(true) }) }) // --- Milestone M5: Orthogonal Dirt Road Rasterization --- describe('Milestone M5 Parity: Orthogonal Dirt Paths & Width-2 Bresenham (R5)', () => { it('generates orthogonal width-2 dirt road without spline loop deviations', () => { const result: WildernessResult = generateWilderness({ levelId: 2, levelName: 'Blood Moor', levelTypeName: 'Act 1 - Wilderness', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 0x12345678, pieces: act1Pieces, substitutions: act1Subs, }) // Dirt road floor tiles in Act 1 use style 0 with sequence 1..46 (byte_6FDCF958 LUT) let dirtTileCount = 0 for (const row of result.level.cells) { for (const cellRecord of row) { if (cellRecord.floors.some(f => (f.style === 0 && f.sequence >= 1 && f.sequence <= 46) || (f.style === 1 && f.prop1 === 194))) { dirtTileCount += 1 } } } // Parity expectation: road connects town gate to den and exit with width-2 footprint (>= 100 tiles) expect(dirtTileCount).toBeGreaterThanOrEqual(100) }) }) })