diablo2-web/tests/wilderness-acts.test.ts

826 lines
28 KiB
TypeScript

import { describe, expect, test } from 'vitest'
import {
generateWilderness,
getPerimeterOpenings,
UNIMPLEMENTED_PASSES,
} from '../src/game/wilderness.ts'
import type { WildernessPiece } from '../src/game/wilderness.ts'
import {
act2OasisRadius,
act5FrozenLakeRadius,
jitterFrac,
} from '../src/game/wilderness-acts.ts'
import type {
Act2TopographyStats,
Act3TopographyStats,
Act4TopographyStats,
Act5TopographyStats,
} from '../src/game/wilderness-acts.ts'
import { Rng } from '../src/game/rng.ts'
import type { Ds1, Ds1Cell } from '../src/formats/ds1.ts'
function makeMockDs1(
width: number,
height: number,
hasGapInWalls: boolean = false,
floorStyle: number = 1,
wallStyle: number = 1,
): Ds1 {
const cells: Ds1Cell[][] = []
for (let y = 0; y < height; y += 1) {
const row: Ds1Cell[] = []
for (let x = 0; x < width; x += 1) {
const isGap = hasGapInWalls && x === Math.floor(width / 2)
row.push({
walls: isGap ? [] : [{ prop1: 2, sequence: 0, style: wallStyle, type: 0, unknown1: 0, unknown2: 0, hidden: false }],
floors: [{ prop1: 2, sequence: 0, style: floorStyle, unknown1: 0, unknown2: 0, hidden: false }],
shadows: [],
substitutions: [],
})
}
cells.push(row)
}
return {
version: 18,
width,
height,
act: 1,
substitutionType: 0,
wallLayers: 1,
floorLayers: 1,
cells,
objects: [],
npcPathOffset: null,
}
}
function makeMockBorderPieces(actPrefix: string): WildernessPiece[] {
const borderDs1 = makeMockDs1(8, 8, false)
const openBorderDs1 = makeMockDs1(8, 8, true)
return [
{ name: `${actPrefix} - Border 1`, border: true, levels: [borderDs1, borderDs1, borderDs1, openBorderDs1] },
{ name: `${actPrefix} - Border 2`, border: true, levels: [borderDs1] },
{ name: `${actPrefix} - Border 3`, border: true, levels: [borderDs1] },
{ name: `${actPrefix} - Border 4`, border: true, levels: [borderDs1, borderDs1, borderDs1, openBorderDs1] },
{ name: `${actPrefix} - Border 5`, border: true, levels: [borderDs1] },
{ name: `${actPrefix} - Border 6`, border: true, levels: [borderDs1] },
{ name: `${actPrefix} - Border 7`, border: true, levels: [borderDs1] },
{ name: `${actPrefix} - Border 8`, border: true, levels: [borderDs1] },
]
}
describe('Wilderness Outdoor Initializers for Acts 2-5 (Issue #51)', () => {
test('UNIMPLEMENTED_PASSES has 5 outdoor passes removed', () => {
expect(UNIMPLEMENTED_PASSES).not.toContain('DRLGOUTDESR_InitAct2OutdoorLevel')
expect(UNIMPLEMENTED_PASSES).not.toContain('DRLGOUTPLACE_InitAct3OutdoorLevel')
expect(UNIMPLEMENTED_PASSES).not.toContain('DRLGOUTDOORS_InitAct4OutdoorLevel')
expect(UNIMPLEMENTED_PASSES).not.toContain('DRLGOUTSIEGE_InitAct5OutdoorLevel')
expect(UNIMPLEMENTED_PASSES).not.toContain('DRLG_GenerateJungles')
expect(UNIMPLEMENTED_PASSES).not.toContain('DRLGVER_CreateVertices')
expect(UNIMPLEMENTED_PASSES).toEqual([
'DRLGOUTPLACE_CreateLevelConnections',
'DRLGOUTDOORS_SpawnAct3Mephisto',
])
})
describe('Act 2: DRLGOUTDESR_InitAct2OutdoorLevel', () => {
test('Rocky Waste (41) generates sand caravan trails and canyon ridge walls', () => {
const pieces = makeMockBorderPieces('Act 2')
const result = generateWilderness({
levelId: 41,
levelName: 'Rocky Waste',
levelTypeName: 'Act 2 - Desert',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: 41001,
pieces,
substitutions: [],
})
expect(result.stats.act2Topography).toBeDefined()
const topo = result.stats.act2Topography as Act2TopographyStats
expect(topo.sandTrailCells).toBeGreaterThan(50)
expect(topo.canyonWalls).toBeGreaterThan(20)
let foundSandTrail = false
let foundCanyonWall = false
for (let y = 0; y < result.level.height; y += 1) {
for (let x = 0; x < result.level.width; x += 1) {
const cell = result.level.cells[y]![x]!
if (cell.floors.some(f => f.style === 5 && f.prop1 === 194)) foundSandTrail = true
if (cell.walls.some(w => w.style === 1 && w.prop1 === 129)) foundCanyonWall = true
}
}
expect(foundSandTrail).toBe(true)
expect(foundCanyonWall).toBe(true)
})
test('Far Oasis (43) generates lush water oasis pools', () => {
const pieces = makeMockBorderPieces('Act 2')
const result = generateWilderness({
levelId: 43,
levelName: 'Far Oasis',
levelTypeName: 'Act 2 - Desert',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: 43001,
pieces,
substitutions: [],
})
expect(result.stats.act2Topography).toBeDefined()
const topo = result.stats.act2Topography as Act2TopographyStats
expect(topo.oasisPoolCells).toBeGreaterThan(15)
let foundOasisWater = false
for (let y = 0; y < result.level.height; y += 1) {
for (let x = 0; x < result.level.width; x += 1) {
const cell = result.level.cells[y]![x]!
if (cell.floors.some(f => f.style === 2 && f.prop1 === 2)) foundOasisWater = true
}
}
expect(foundOasisWater).toBe(true)
})
test('Valley of Snakes (45) creates narrow canyon gorge', () => {
const pieces = makeMockBorderPieces('Act 2')
const result = generateWilderness({
levelId: 45,
levelName: 'Valley of Snakes',
levelTypeName: 'Act 2 - Desert',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: 45001,
pieces,
substitutions: [],
})
expect(result.stats.act2Topography).toBeDefined()
const topo = result.stats.act2Topography as Act2TopographyStats
expect(topo.canyonWalls).toBeGreaterThan(30)
})
test('Canyon of the Magi (46) places 7 Tal Rasha tombs, central dais, and has 0 perimeter openings', () => {
const pieces = makeMockBorderPieces('Act 2')
expect(getPerimeterOpenings(46, 10, 10).size).toBe(0)
const result = generateWilderness({
levelId: 46,
levelName: 'Canyon of the Magi',
levelTypeName: 'Act 2 - Desert',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: 46001,
pieces,
substitutions: [],
})
expect(result.stats.act2Topography).toBeDefined()
const topo = result.stats.act2Topography as Act2TopographyStats
expect(topo.tombEntrances).toBe(7)
// Verify central Arcane Sanctuary warp platform (portal/dais)
let foundCentralDais = false
for (let y = 35; y <= 45; y += 1) {
for (let x = 35; x <= 45; x += 1) {
const cell = result.level.cells[y]![x]!
if (cell.floors.some(f => f.style === 3)) foundCentralDais = true
}
}
expect(foundCentralDais).toBe(true)
})
})
describe('Act 3: DRLGOUTPLACE_InitAct3OutdoorLevel & DRLG_GenerateJungles', () => {
test('Jungles (76 Spider Forest) generate river channel, wooden river bridges, and jungle paths', () => {
const tailDs1 = makeMockDs1(64, 32)
const headDs1 = makeMockDs1(64, 32)
const clearingDs1 = makeMockDs1(32, 32)
const riverDs1 = makeMockDs1(32, 32)
const pieces: WildernessPiece[] = [
{ name: 'Act 3 - Jungle Tail', border: false, levels: [tailDs1] },
{ name: 'Act 3 - Jungle Head', border: false, levels: [headDs1] },
{ name: 'Act 3 - Clearing Webby E', border: false, levels: [clearingDs1] },
{ name: 'Act 3 - Jungle NS W', border: false, levels: [riverDs1] },
]
const result = generateWilderness({
levelId: 76,
levelName: 'Spider Forest',
levelTypeName: 'Act 3 - Jungle',
sizeX: 64,
sizeY: 192,
subType: 0,
subTheme: 0,
seed: 76001,
pieces,
substitutions: [],
})
expect(result.stats.act3Topography).toBeDefined()
const topo = result.stats.act3Topography as Act3TopographyStats
expect(topo.riverBridges).toBeGreaterThanOrEqual(2)
expect(topo.junglePathCells).toBeGreaterThan(20)
// Verify wooden river bridge planks (style 4, prop1 2) spanning across East channel
let foundBridgePlank = false
for (let y = 70; y <= 90; y += 1) {
for (let x = 32; x < 60; x += 1) {
const cell = result.level.cells[y]![x]!
if (cell.floors.some(f => f.style === 4 && f.prop1 === 2)) foundBridgePlank = true
}
}
expect(foundBridgePlank).toBe(true)
})
test('Kurast Causeway (82) stamps causeway with central walkway, balustrades, and canal water', () => {
const groundDs1 = makeMockDs1(48, 16)
const pieces: WildernessPiece[] = [
{ name: 'Act 3 - Ground', border: false, levels: [groundDs1] },
]
const result = generateWilderness({
levelId: 82,
levelName: 'Kurast Causeway',
levelTypeName: 'Act 3 - Kurast',
sizeX: 48,
sizeY: 16,
subType: 0,
subTheme: 0,
seed: 82001,
pieces,
substitutions: [],
})
expect(result.stats.act3Topography).toBeDefined()
const topo = result.stats.act3Topography as Act3TopographyStats
expect(topo.causewayCells).toBeGreaterThan(50)
expect(topo.canalCells).toBeGreaterThan(100)
// Verify central walkway (style 5), stone balustrade walls (style 1), and canal water (style 2)
let foundWalkway = false
let foundBalustrade = false
let foundCanalWater = false
for (let y = 0; y < result.level.height; y += 1) {
for (let x = 0; x < result.level.width; x += 1) {
const cell = result.level.cells[y]![x]!
if (cell.floors.some(f => (f.style === 5 || f.style === 20) && f.prop1 === 194)) foundWalkway = true
if (cell.walls.some(w => (w.style === 1 || w.style === 20) && w.prop1 === 129)) foundBalustrade = true
if (cell.floors.some(f => f.style === 2 && f.prop1 === 2)) foundCanalWater = true
}
}
expect(foundWalkway).toBe(true)
expect(foundBalustrade).toBe(true)
expect(foundCanalWater).toBe(true)
})
test('Kurast Causeway (82) preserves preset when Act 3 - Bridge is provided', () => {
const bridgeDs1 = makeMockDs1(48, 16, false, 29, 31)
const pieces: WildernessPiece[] = [
{ name: 'Act 3 - Bridge', border: false, levels: [bridgeDs1] },
]
const result = generateWilderness({
levelId: 82,
levelName: 'Kurast Causeway',
levelTypeName: 'Act 3 - Kurast',
sizeX: 48,
sizeY: 16,
subType: 0,
subTheme: 0,
seed: 82001,
pieces,
substitutions: [],
})
let foundPresetFloor = false
let foundPresetWall = false
for (let y = 0; y < result.level.height; y += 1) {
for (let x = 0; x < result.level.width; x += 1) {
const cell = result.level.cells[y]![x]!
if (cell.floors.some(f => f.style === 29)) foundPresetFloor = true
if (cell.walls.some(w => w.style === 31)) foundPresetWall = true
}
}
expect(foundPresetFloor).toBe(true)
expect(foundPresetWall).toBe(true)
})
test('Kurast Bazaar (79) generates paved stone avenues, canals, and stone bridges', () => {
const pieces = makeMockBorderPieces('Act 3')
const result = generateWilderness({
levelId: 79,
levelName: 'Kurast Bazaar',
levelTypeName: 'Act 3 - Kurast',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: 79001,
pieces,
substitutions: [],
})
expect(result.stats.act3Topography).toBeDefined()
const topo = result.stats.act3Topography as Act3TopographyStats
expect(topo.causewayCells).toBeGreaterThan(50)
expect(topo.canalCells).toBeGreaterThan(30)
expect(topo.riverBridges).toBeGreaterThanOrEqual(1)
})
})
describe('Act 4: DRLGOUTDOORS_InitAct4OutdoorLevel', () => {
test('Outer Steppes (104) generates lava chasms, basalt lips, and rocky bridge causeways', () => {
const pieces = makeMockBorderPieces('Act 4')
const result = generateWilderness({
levelId: 104,
levelName: 'Outer Steppes',
levelTypeName: 'Act 4 - Mesa',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: 104001,
pieces,
substitutions: [],
})
expect(result.stats.act4Topography).toBeDefined()
const topo = result.stats.act4Topography as Act4TopographyStats
expect(topo.lavaChasmCells).toBeGreaterThan(100)
expect(topo.rockyBridges).toBeGreaterThan(5)
// Verify boiling molten lava (style 20, sequence 3, prop1 2) and rocky bridges (style 10, sequence 0, prop1 2)
let foundLava = false
let foundRockyBridge = false
for (let y = 0; y < result.level.height; y += 1) {
for (let x = 0; x < result.level.width; x += 1) {
const cell = result.level.cells[y]![x]!
if (cell.floors.some(f => (f.style === 20 || f.style === 10) && f.prop1 === 2)) foundLava = true
if (cell.floors.some(f => f.style === 10 && f.sequence === 0 && f.prop1 === 2)) foundRockyBridge = true
}
}
expect(foundLava).toBe(true)
expect(foundRockyBridge).toBe(true)
})
test('Plains of Despair (105) creates Izual prison plateau', () => {
const pieces = makeMockBorderPieces('Act 4')
const result = generateWilderness({
levelId: 105,
levelName: 'Plains of Despair',
levelTypeName: 'Act 4 - Mesa',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: 105001,
pieces,
substitutions: [],
})
expect(result.stats.act4Topography).toBeDefined()
const topo = result.stats.act4Topography as Act4TopographyStats
expect(topo.plateauCells).toBeGreaterThan(20)
})
})
describe('Act 5: DRLGOUTSIEGE_InitAct5OutdoorLevel', () => {
test('Frigid Highlands (111) generates transverse siege barricades, choke-point gates, and trenches', () => {
const pieces = makeMockBorderPieces('Act 5')
const result = generateWilderness({
levelId: 111,
levelName: 'Frigid Highlands',
levelTypeName: 'Act 5 - Barricade',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: 111001,
pieces,
substitutions: [],
})
expect(result.stats.act5Topography).toBeDefined()
const topo = result.stats.act5Topography as Act5TopographyStats
expect(topo.barricadeWalls).toBeGreaterThan(40)
expect(topo.trenchCells).toBeGreaterThan(30)
// Verify spiked palisade walls (style 2, prop1 129) and trench floors (style 0/6)
let foundBarricadeWall = false
let foundTrench = false
for (let y = 0; y < result.level.height; y += 1) {
for (let x = 0; x < result.level.width; x += 1) {
const cell = result.level.cells[y]![x]!
if (cell.walls.some(w => w.style === 2 && w.prop1 === 129)) foundBarricadeWall = true
if (cell.floors.some(f => (f.style === 0 || f.style === 6) && f.prop1 === 2)) foundTrench = true
}
}
expect(foundBarricadeWall).toBe(true)
expect(foundTrench).toBe(true)
})
test('Arreat Plateau (112) generates snow crater frozen lakes with ice floors', () => {
const pieces = makeMockBorderPieces('Act 5')
const result = generateWilderness({
levelId: 112,
levelName: 'Arreat Plateau',
levelTypeName: 'Act 5 - Barricade',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: 112001,
pieces,
substitutions: [],
})
expect(result.stats.act5Topography).toBeDefined()
const topo = result.stats.act5Topography as Act5TopographyStats
expect(topo.frozenLakeCells).toBeGreaterThan(20)
let foundIceFloor = false
for (let y = 0; y < result.level.height; y += 1) {
for (let x = 0; x < result.level.width; x += 1) {
const cell = result.level.cells[y]![x]!
if (cell.floors.some(f => f.style === 2 && f.sequence === 0 && f.prop1 === 2)) foundIceFloor = true
}
}
expect(foundIceFloor).toBe(true)
})
})
describe('Phase D2: Organic Geometries & Fractional Jitter (Milestone M2)', () => {
function computePolarIsoperimetricQuotient(
radiusFn: (theta: number) => number,
steps: number = 1000,
): { area: number; perimeter: number; quotient: number } {
const dTheta = (2 * Math.PI) / steps
let area = 0
let perimeter = 0
for (let i = 0; i < steps; i += 1) {
const t1 = i * dTheta
const t2 = (i + 1) * dTheta
const r1 = radiusFn(t1)
const r2 = radiusFn(t2)
area += 0.5 * r1 * r2 * Math.sin(dTheta)
const x1 = r1 * Math.cos(t1)
const y1 = r1 * Math.sin(t1)
const x2 = r2 * Math.cos(t2)
const y2 = r2 * Math.sin(t2)
perimeter += Math.hypot(x2 - x1, y2 - y1)
}
const quotient = (perimeter * perimeter) / area
return { area, perimeter, quotient }
}
test('Act 2 Oasis satisfies organic isoperimetric quotient P^2/A > 4pi * 1.15', () => {
const threshold = 4 * Math.PI * 1.15
// 1. Analytical contour integration across various phase angles
const testPhases = [
[0, 0],
[0.5, 1.2],
[Math.PI / 3, Math.PI / 4],
[2.1, 0.9],
]
for (const [phi1, phi2] of testPhases) {
const { quotient } = computePolarIsoperimetricQuotient(
theta => act2OasisRadius(10, theta, phi1, phi2),
1000,
)
expect(quotient).toBeGreaterThan(threshold)
}
// 2. Discrete raster grid verification on generated Far Oasis level
const pieces = makeMockBorderPieces('Act 2')
const result = generateWilderness({
levelId: 43,
levelName: 'Far Oasis',
levelTypeName: 'Act 2 - Desert',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: 43002,
pieces,
substitutions: [],
})
const cells = result.level.cells
const W = result.level.width
const H = result.level.height
const isOasis = (x: number, y: number): boolean => {
if (x < 0 || x >= W || y < 0 || y >= H) return false
return cells[y]![x]!.floors.some(f => f.style === 2 && f.prop1 === 2)
}
let area = 0
let perimeter = 0
for (let y = 0; y < H; y += 1) {
for (let x = 0; x < W; x += 1) {
if (isOasis(x, y)) {
area += 1
let exposedEdges = 0
if (!isOasis(x + 1, y)) exposedEdges += 1
if (!isOasis(x - 1, y)) exposedEdges += 1
if (!isOasis(x, y + 1)) exposedEdges += 1
if (!isOasis(x, y - 1)) exposedEdges += 1
perimeter += exposedEdges
}
}
}
expect(area).toBeGreaterThan(15)
const discreteQuotient = (perimeter * perimeter) / area
expect(discreteQuotient).toBeGreaterThan(threshold)
})
test('Act 5 Frozen Lake satisfies organic isoperimetric quotient P^2/A > 4pi * 1.15', () => {
const threshold = 4 * Math.PI * 1.15
// 1. Analytical contour integration across various phase angles
const testPhases = [
[0, 0],
[0.7, 1.5],
[Math.PI / 2, Math.PI / 6],
[1.8, 2.3],
]
for (const [phi1, phi2] of testPhases) {
const { quotient } = computePolarIsoperimetricQuotient(
theta => act5FrozenLakeRadius(10, theta, phi1, phi2),
1000,
)
expect(quotient).toBeGreaterThan(threshold)
}
// 2. Discrete raster grid verification on generated Arreat Plateau level
const pieces = makeMockBorderPieces('Act 5')
const result = generateWilderness({
levelId: 112,
levelName: 'Arreat Plateau',
levelTypeName: 'Act 5 - Barricade',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: 112002,
pieces,
substitutions: [],
})
const cells = result.level.cells
const W = result.level.width
const H = result.level.height
const isLake = (x: number, y: number): boolean => {
if (x < 0 || x >= W || y < 0 || y >= H) return false
return cells[y]![x]!.floors.some(f => f.style === 2 && f.sequence === 0 && f.prop1 === 2)
}
let area = 0
let perimeter = 0
for (let y = 0; y < H; y += 1) {
for (let x = 0; x < W; x += 1) {
if (isLake(x, y)) {
area += 1
let exposedEdges = 0
if (!isLake(x + 1, y)) exposedEdges += 1
if (!isLake(x - 1, y)) exposedEdges += 1
if (!isLake(x, y + 1)) exposedEdges += 1
if (!isLake(x, y - 1)) exposedEdges += 1
perimeter += exposedEdges
}
}
}
expect(area).toBeGreaterThan(20)
const discreteQuotient = (perimeter * perimeter) / area
expect(discreteQuotient).toBeGreaterThan(threshold)
})
test('Act 4 Lava Fissure guarantees >= 90% walkable flood-fill connectivity', () => {
const pieces = makeMockBorderPieces('Act 4')
const testSeeds = [104001, 104002, 104003, 104004]
for (const seed of testSeeds) {
const result = generateWilderness({
levelId: 104,
levelName: 'Outer Steppes',
levelTypeName: 'Act 4 - Mesa',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed,
pieces,
substitutions: [],
})
const W = result.level.width
const H = result.level.height
const hasWall = (x: number, y: number): boolean => {
const cell = result.level.cells[y]![x]!
return cell.walls.some(w => w.prop1 !== 0 || w.style !== 0)
}
const isWalkable = (x: number, y: number): boolean => !hasWall(x, y)
// Find all walkable cells in the playable interior [8..W-8] x [8..H-8]
let totalWalkable = 0
let startX = -1
let startY = -1
for (let y = 8; y < H - 8; y += 1) {
for (let x = 8; x < W - 8; x += 1) {
if (isWalkable(x, y)) {
totalWalkable += 1
if (startX === -1 && y < 16) {
startX = x
startY = y
}
}
}
}
expect(totalWalkable).toBeGreaterThan(1000)
expect(startX).toBeGreaterThanOrEqual(8)
// BFS flood-fill from starting walkable cell
const visited = new Set<number>()
const queue: [number, number][] = [[startX, startY]]
visited.add(startY * W + startX)
while (queue.length > 0) {
const [cx, cy] = queue.shift()!
for (const [dx, dy] of [
[1, 0],
[-1, 0],
[0, 1],
[0, -1],
]) {
const nx = cx + dx
const ny = cy + dy
if (nx >= 8 && nx < W - 8 && ny >= 8 && ny < H - 8) {
const key = ny * W + nx
if (!visited.has(key) && isWalkable(nx, ny)) {
visited.add(key)
queue.push([nx, ny])
}
}
}
}
const reachableRatio = visited.size / totalWalkable
expect(reachableRatio).toBeGreaterThanOrEqual(0.90)
}
})
test('Act 5 Barricade palisades cut horizontally across X when height > width with gate at hubX', () => {
const pieces = makeMockBorderPieces('Act 5')
// 1. Vertical corridor (height > width): palisades run horizontally across X
const vertResult = generateWilderness({
levelId: 111,
levelName: 'Frigid Highlands',
levelTypeName: 'Act 5 - Barricade',
sizeX: 48,
sizeY: 160,
subType: 0,
subTheme: 0,
seed: 111001,
pieces,
substitutions: [],
})
const vW = vertResult.level.width
const vH = vertResult.level.height
const vGridW = Math.floor(48 / 8)
const vInX = Math.floor(vGridW / 2)
const vHubX = vInX * 8 + 4
// Locate horizontal barricade rows (at least 15 palisade wall tiles across the row)
const horizontalBarricadeRows: number[] = []
for (let y = 8; y < vH - 8; y += 1) {
let count = 0
for (let x = 8; x < vW - 8; x += 1) {
const cell = vertResult.level.cells[y]![x]!
if (cell.walls.some(w => w.style === 2 && w.prop1 === 129)) {
count += 1
}
}
if (count >= 15) {
horizontalBarricadeRows.push(y)
}
}
expect(horizontalBarricadeRows.length).toBeGreaterThanOrEqual(2)
// Verify each horizontal barricade row has an open choke-point gate at vHubX
for (const by of horizontalBarricadeRows) {
const gateCell = vertResult.level.cells[by]![vHubX]!
const hasWall = gateCell.walls.some(w => w.prop1 !== 0 || w.style !== 0)
expect(hasWall).toBe(false)
expect(gateCell.floors.some(f => (f.style === 0 || f.style === 6) && f.prop1 === 2)).toBe(true)
}
// 2. Horizontal corridor (width >= height): palisades run vertically across Y
const horizResult = generateWilderness({
levelId: 111,
levelName: 'Frigid Highlands',
levelTypeName: 'Act 5 - Barricade',
sizeX: 160,
sizeY: 48,
subType: 0,
subTheme: 0,
seed: 111002,
pieces,
substitutions: [],
})
const hW = horizResult.level.width
const hH = horizResult.level.height
const hGridH = Math.floor(48 / 8)
const hOutY = Math.floor(hGridH / 2)
const hHubY = hOutY * 8 + 4
const verticalBarricadeCols: number[] = []
for (let x = 8; x < hW - 8; x += 1) {
let count = 0
for (let y = 8; y < hH - 8; y += 1) {
const cell = horizResult.level.cells[y]![x]!
if (cell.walls.some(w => w.style === 2 && w.prop1 === 129)) {
count += 1
}
}
if (count >= 15) {
verticalBarricadeCols.push(x)
}
}
expect(verticalBarricadeCols.length).toBeGreaterThanOrEqual(2)
// Verify each vertical barricade col has an open choke-point gate at hHubY
for (const bx of verticalBarricadeCols) {
const gateCell = horizResult.level.cells[hHubY]![bx]!
const hasWall = gateCell.walls.some(w => w.prop1 !== 0 || w.style !== 0)
expect(hasWall).toBe(false)
expect(gateCell.floors.some(f => (f.style === 0 || f.style === 6) && f.prop1 === 2)).toBe(true)
}
})
test('Topographical jitter is bounded within +-0.05 and varies across seeds', () => {
const rng = new Rng(0x98765432)
const samples: number[] = []
for (let i = 0; i < 300; i += 1) {
const val = jitterFrac(rng, 0.50, 0.05)
expect(val).toBeGreaterThanOrEqual(0.45)
expect(val).toBeLessThanOrEqual(0.55)
samples.push(val)
}
const mean = samples.reduce((a, b) => a + b, 0) / samples.length
expect(Math.abs(mean - 0.50)).toBeLessThan(0.02)
const min = Math.min(...samples)
const max = Math.max(...samples)
expect(max - min).toBeGreaterThan(0.08)
// Verify that across different seeds, generated topographical feature counts vary organically
const pieces = makeMockBorderPieces('Act 2')
const seeds = [43001, 43002, 43003, 43004, 43005]
const results = seeds.map(s =>
generateWilderness({
levelId: 43,
levelName: 'Far Oasis',
levelTypeName: 'Act 2 - Desert',
sizeX: 80,
sizeY: 80,
subType: 0,
subTheme: 0,
seed: s,
pieces,
substitutions: [],
}),
)
const sandTrailStats = results.map(
r => (r.stats.act2Topography as Act2TopographyStats).sandTrailCells,
)
const oasisPoolStats = results.map(
r => (r.stats.act2Topography as Act2TopographyStats).oasisPoolCells,
)
// Confirm organic variation across runs
const uniqueSandTrails = new Set(sandTrailStats)
const uniqueOasisPools = new Set(oasisPoolStats)
expect(uniqueSandTrails.size).toBeGreaterThan(1)
expect(uniqueOasisPools.size).toBeGreaterThan(1)
})
})
})