diablo2-web/tests/stress-world-placement.test.ts

245 lines
8.0 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { SEAMLESS_ADJACENCY } from '../src/game/world-graph.ts'
import { resolveWorldOffsets, type WorldOffset } from '../src/game/world-placement.ts'
/**
* 100 distinct seeds for adversarial stress testing:
* - 6 canonical / regression seeds (0x301cd095, 0x416d61c5, 0xdeadbeef, 42, 123, 9999)
* - 7 boundary / extreme seeds (0, 1, 0x7fffffff, 0xffffffff, 241, 572, 919)
* - 87 pseudo-random seeds generated via standard LCG to ensure broad coverage
*/
export function generateTestSeeds(): number[] {
const seedSet = new Set<number>([
0x301cd095, 0x416d61c5, 0xdeadbeef, 42, 123, 9999,
0, 1, 0x7fffffff, 0xffffffff, 241, 572, 919,
])
let state = 0x8546291a
while (seedSet.size < 100) {
state = (state * 1664525 + 1013904223) >>> 0
seedSet.add(state)
}
return Array.from(seedSet)
}
export const TEST_SEEDS = generateTestSeeds()
/** Check if two rectangles have overlapping interior areas. */
export function doRectanglesOverlap(a: WorldOffset, b: WorldOffset): boolean {
return (
a.offsetX < b.offsetX + b.sizeX &&
a.offsetX + a.sizeX > b.offsetX &&
a.offsetY < b.offsetY + b.sizeY &&
a.offsetY + a.sizeY > b.offsetY
)
}
export interface BoundaryCheckResult {
readonly touching: boolean
readonly type: 'vertical-boundary' | 'horizontal-boundary' | 'none'
readonly sharedLength: number
readonly gapX: number
readonly gapY: number
}
/**
* Check if two rectangular levels share an exact boundary edge without tears or gaps.
* Must share a non-zero length segment along their touching edge.
*/
export function checkBoundarySharing(a: WorldOffset, b: WorldOffset): BoundaryCheckResult {
// Check horizontal adjacency (A is West of B or B is West of A)
const touchLR = a.offsetX + a.sizeX === b.offsetX || b.offsetX + b.sizeX === a.offsetX
const overlapY = Math.max(
0,
Math.min(a.offsetY + a.sizeY, b.offsetY + b.sizeY) - Math.max(a.offsetY, b.offsetY),
)
if (touchLR && overlapY > 0) {
return {
touching: true,
type: 'vertical-boundary',
sharedLength: overlapY,
gapX: 0,
gapY: 0,
}
}
// Check vertical adjacency (A is North of B or B is North of A)
const touchTB = a.offsetY + a.sizeY === b.offsetY || b.offsetY + b.sizeY === a.offsetY
const overlapX = Math.max(
0,
Math.min(a.offsetX + a.sizeX, b.offsetX + b.sizeX) - Math.max(a.offsetX, b.offsetX),
)
if (touchTB && overlapX > 0) {
return {
touching: true,
type: 'horizontal-boundary',
sharedLength: overlapX,
gapX: 0,
gapY: 0,
}
}
// Compute gaps / tears if not touching along an edge
const gapX = a.offsetX + a.sizeX <= b.offsetX
? b.offsetX - (a.offsetX + a.sizeX)
: b.offsetX + b.sizeX <= a.offsetX
? a.offsetX - (b.offsetX + b.sizeX)
: 0
const gapY = a.offsetY + a.sizeY <= b.offsetY
? b.offsetY - (a.offsetY + a.sizeY)
: b.offsetY + b.sizeY <= a.offsetY
? a.offsetY - (b.offsetY + b.sizeY)
: 0
return {
touching: false,
type: 'none',
sharedLength: 0,
gapX,
gapY,
}
}
describe('Adversarial Stress Test: resolveWorldOffsets across 100 Seeds for Acts 1..5', () => {
it('generates exactly 100 distinct test seeds', () => {
expect(TEST_SEEDS).toHaveLength(100)
expect(new Set(TEST_SEEDS).size).toBe(100)
})
it('Act 5: Level 111 West edge consistently aligns to X = 702 when connected to Level 110 at (760, 1000)', () => {
let checkedCount = 0
for (const seed of TEST_SEEDS) {
const offsets = resolveWorldOffsets(5, seed)
const l110 = offsets.get(110)
const l111 = offsets.get(111)
const l112 = offsets.get(112)
expect(l110).toBeDefined()
expect(l111).toBeDefined()
expect(l112).toBeDefined()
expect(l110!.offsetX).toBe(760)
expect(l110!.offsetY).toBe(1000)
expect(l111!.sizeX).toBe(58)
expect(l111!.offsetX).toBe(702)
expect(l111!.offsetX + l111!.sizeX).toBe(760) // Zero gap to Level 110
expect(l112!.sizeX).toBe(58)
expect(l112!.offsetX).toBe(702)
expect(l112!.offsetY + l112!.sizeY).toBe(l111!.offsetY) // Zero gap North to Level 111
checkedCount++
}
expect(checkedCount).toBe(100)
})
it('Determinism: identical seeds yield strictly identical WorldOffset maps', () => {
let checkedCount = 0
for (const seed of TEST_SEEDS) {
for (let act = 1; act <= 5; act++) {
const run1 = resolveWorldOffsets(act, seed)
const run2 = resolveWorldOffsets(act, seed)
expect(Array.from(run1.entries())).toEqual(Array.from(run2.entries()))
checkedCount++
}
}
expect(checkedCount).toBe(500)
})
it('Non-Overlapping Interiors: seamless outdoor levels have zero interior overlap', () => {
let pairsChecked = 0
for (const seed of TEST_SEEDS) {
for (let act = 1; act <= 5; act++) {
const offsets = resolveWorldOffsets(act, seed)
const actSeamless = SEAMLESS_ADJACENCY.filter(([a, b]) => offsets.has(a) && offsets.has(b))
const placedSeamlessIds = Array.from(new Set(actSeamless.flat()))
for (let i = 0; i < placedSeamlessIds.length; i++) {
for (let j = i + 1; j < placedSeamlessIds.length; j++) {
const a = offsets.get(placedSeamlessIds[i]!)!
const b = offsets.get(placedSeamlessIds[j]!)!
expect(doRectanglesOverlap(a, b)).toBe(false)
pairsChecked++
}
}
}
}
expect(pairsChecked).toBe(9900)
})
it('Acts 1, 2, 4, 5: contiguous pairs in SEAMLESS_ADJACENCY share exact boundary coordinates without tears or gaps', () => {
let pairsChecked = 0
const failures: string[] = []
for (const seed of TEST_SEEDS) {
for (const act of [1, 2, 4, 5]) {
const offsets = resolveWorldOffsets(act, seed)
const actSeamless = SEAMLESS_ADJACENCY.filter(([a, b]) => offsets.has(a) && offsets.has(b))
for (const [aId, bId] of actSeamless) {
const a = offsets.get(aId)!
const b = offsets.get(bId)!
const res = checkBoundarySharing(a, b)
if (!res.touching) {
failures.push(`Act ${act}: [${aId}, ${bId}] seed=${seed}`)
}
pairsChecked++
}
}
}
expect(failures).toEqual([])
// 18 pairs (Act 1: 7, Act 2: 5, Act 4: 3, Act 5: 3) x 100 seeds.
expect(pairsChecked).toBe(1800)
})
it('Act 3 Seamless Adjacency: contiguous pairs in SEAMLESS_ADJACENCY share exact boundary coordinates without tears or gaps', () => {
let pairsChecked = 0
const failures: { seed: number; pair: string; reason: string }[] = []
for (const seed of TEST_SEEDS) {
const offsets = resolveWorldOffsets(3, seed)
const actSeamless = SEAMLESS_ADJACENCY.filter(([a, b]) => offsets.has(a) && offsets.has(b))
for (const [aId, bId] of actSeamless) {
const a = offsets.get(aId)!
const b = offsets.get(bId)!
const res = checkBoundarySharing(a, b)
if (!res.touching) {
failures.push({
seed,
pair: `[${aId}, ${bId}]`,
reason: `Touch at corner only, sharedLength=0, gapX=${res.gapX}, gapY=${res.gapY}`,
})
}
pairsChecked++
}
}
expect(pairsChecked).toBe(800)
expect(failures).toEqual([])
})
it('All-Level Interior Non-Overlap: all placed levels across all acts have zero interior overlap', () => {
let pairsChecked = 0
const overlaps: { seed: number; act: number; a: number; b: number }[] = []
for (const seed of TEST_SEEDS) {
for (let act = 1; act <= 5; act++) {
const offsets = resolveWorldOffsets(act, seed)
const placed = Array.from(offsets.values())
for (let i = 0; i < placed.length; i++) {
for (let j = i + 1; j < placed.length; j++) {
if (doRectanglesOverlap(placed[i]!, placed[j]!)) {
overlaps.push({ seed, act, a: placed[i]!.levelId, b: placed[j]!.levelId })
}
pairsChecked++
}
}
}
}
expect(pairsChecked).toBe(210700)
expect(overlaps).toEqual([])
})
})