384 lines
13 KiB
TypeScript
384 lines
13 KiB
TypeScript
/**
|
|
* Empirical Challenger Verification Harness for Milestone M3 World Placement
|
|
*
|
|
* Adversarially challenges:
|
|
* 1. Property 1: 100% boundary sharing (sharedLength > 0, gap = 0) for all SEAMLESS_ADJACENCY pairs
|
|
* 2. Property 2a: 0 interior overlaps between any pair of placed seamless levels
|
|
* 3. Property 2b: 0 interior overlaps between ALL placed levels (seamless, isolated, dependent)
|
|
* 4. Property 3: Act 5 Level 111 West edge aligns at X = 702 and ka.ma0 Layer 79 bounding box
|
|
* 5. Property 4: Determinism replay produces 100% identical coordinate maps across all seeds
|
|
* 6. Level 33 relative placement at (3996, 966) abutting Level 32 at (4000, 1000)
|
|
* 7. Level 27 relative placement on Level 26
|
|
* 8. Strict finiteness and non-negativity across all coordinates
|
|
*/
|
|
|
|
import { SEAMLESS_ADJACENCY } from '../src/game/world-graph.ts'
|
|
import {
|
|
resolveWorldOffsets,
|
|
type WorldOffset,
|
|
CANONICAL_LEVEL_ROWS,
|
|
ACT_ANCHORS,
|
|
} from '../src/game/world-placement.ts'
|
|
|
|
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 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
|
|
}
|
|
|
|
export function checkBoundarySharing(a: WorldOffset, b: WorldOffset): BoundaryCheckResult {
|
|
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,
|
|
}
|
|
}
|
|
|
|
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,
|
|
}
|
|
}
|
|
|
|
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,
|
|
}
|
|
}
|
|
|
|
async function runAdversarialAudit(): Promise<void> {
|
|
console.log('================================================================================')
|
|
console.log('🛡️ EMPIRICAL CHALLENGER AUDIT: World Placement & Graph Topology')
|
|
console.log('================================================================================')
|
|
|
|
const seeds = generateTestSeeds()
|
|
console.log(`Generated ${seeds.length} test seeds (including edge seeds 0, 1, 0x7fffffff, 0xffffffff, etc.)`)
|
|
|
|
let totalAssertions = 0
|
|
let totalFailures = 0
|
|
|
|
// 1. Property 1: Boundary sharing for all SEAMLESS_ADJACENCY pairs
|
|
let p1Total = 0
|
|
let p1Passed = 0
|
|
let p1Failed = 0
|
|
const p1Failures: string[] = []
|
|
|
|
// 2a. Property 2a: Seamless levels interior non-overlap
|
|
let p2aTotal = 0
|
|
let p2aPassed = 0
|
|
let p2aFailed = 0
|
|
|
|
// 2b. Property 2b: All placed levels interior non-overlap
|
|
let p2bTotal = 0
|
|
let p2bPassed = 0
|
|
let p2bFailed = 0
|
|
const p2bCollisions: string[] = []
|
|
|
|
// 3. Property 3: Act 5 Level 111 alignment at X = 702
|
|
let p3Total = 0
|
|
let p3Passed = 0
|
|
let p3Failed = 0
|
|
|
|
// 4. Property 4: Determinism replay
|
|
let p4Total = 0
|
|
let p4Passed = 0
|
|
let p4Failed = 0
|
|
|
|
// 5. Level 33 relative placement at (3996, 966) abutting Level 32
|
|
let l33Total = 0
|
|
let l33Passed = 0
|
|
let l33Failed = 0
|
|
|
|
// 6. Level 27 relative placement on Level 26
|
|
let l27Total = 0
|
|
let l27Passed = 0
|
|
let l27Failed = 0
|
|
|
|
// 7. Sanity: Coordinates non-negative and finite
|
|
let sanityTotal = 0
|
|
let sanityPassed = 0
|
|
let sanityFailed = 0
|
|
|
|
for (const seed of seeds) {
|
|
for (let act = 1; act <= 5; act++) {
|
|
const offsets = resolveWorldOffsets(act, seed)
|
|
|
|
// Sanity checks on all offsets
|
|
for (const [id, off] of offsets) {
|
|
sanityTotal++
|
|
if (
|
|
Number.isFinite(off.offsetX) &&
|
|
Number.isFinite(off.offsetY) &&
|
|
Number.isFinite(off.sizeX) &&
|
|
Number.isFinite(off.sizeY) &&
|
|
off.offsetX >= 0 &&
|
|
off.offsetY >= 0 &&
|
|
off.sizeX > 0 &&
|
|
off.sizeY > 0
|
|
) {
|
|
sanityPassed++
|
|
} else {
|
|
sanityFailed++
|
|
console.error(`SANITY FAIL: Seed ${seed}, Act ${act}, Level ${id}: ${JSON.stringify(off)}`)
|
|
}
|
|
}
|
|
|
|
// Property 1: Boundary sharing
|
|
const actSeamless = SEAMLESS_ADJACENCY.filter(([a, b]) => offsets.has(a) && offsets.has(b))
|
|
for (const [aId, bId] of actSeamless) {
|
|
p1Total++
|
|
const a = offsets.get(aId)!
|
|
const b = offsets.get(bId)!
|
|
const res = checkBoundarySharing(a, b)
|
|
if (res.touching && res.sharedLength > 0 && res.gapX === 0 && res.gapY === 0) {
|
|
p1Passed++
|
|
} else {
|
|
p1Failed++
|
|
p1Failures.push(`Seed ${seed}, Act ${act}: [${aId}, ${bId}] gapX=${res.gapX}, gapY=${res.gapY}, sharedLen=${res.sharedLength}`)
|
|
}
|
|
}
|
|
|
|
// Property 2a: Seamless interior non-overlap
|
|
const seamlessIds = Array.from(new Set(actSeamless.flat()))
|
|
for (let i = 0; i < seamlessIds.length; i++) {
|
|
for (let j = i + 1; j < seamlessIds.length; j++) {
|
|
p2aTotal++
|
|
const a = offsets.get(seamlessIds[i]!)!
|
|
const b = offsets.get(seamlessIds[j]!)!
|
|
if (!doRectanglesOverlap(a, b)) {
|
|
p2aPassed++
|
|
} else {
|
|
p2aFailed++
|
|
}
|
|
}
|
|
}
|
|
|
|
// Property 2b: All placed levels interior non-overlap
|
|
const allLevels = Array.from(offsets.values())
|
|
for (let i = 0; i < allLevels.length; i++) {
|
|
for (let j = i + 1; j < allLevels.length; j++) {
|
|
p2bTotal++
|
|
const a = allLevels[i]!
|
|
const b = allLevels[j]!
|
|
if (!doRectanglesOverlap(a, b)) {
|
|
p2bPassed++
|
|
} else {
|
|
p2bFailed++
|
|
p2bCollisions.push(`Seed ${seed}, Act ${act}: Level ${a.levelId} overlaps Level ${b.levelId}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Property 3: Act 5 Alignment
|
|
if (act === 5) {
|
|
p3Total++
|
|
const l109 = offsets.get(109)
|
|
const l110 = offsets.get(110)
|
|
const l111 = offsets.get(111)
|
|
const l112 = offsets.get(112)
|
|
|
|
const cond1 = l109 !== undefined && l109.offsetX === 1000 && l109.offsetY === 1000
|
|
const cond2 = l110 !== undefined && l110.offsetX === 760 && l110.offsetY === 1000 && l110.sizeX === 240
|
|
const cond3 = l111 !== undefined && l111.offsetX === 702 && l111.sizeX === 58 && l111.offsetX + l111.sizeX === 760
|
|
const cond4 = l112 !== undefined && l112.offsetX === 702 && l112.sizeX === 58
|
|
const cond5 = l111 !== undefined && l112 !== undefined && l112.offsetY + l112.sizeY === l111.offsetY
|
|
const cond6 = l111 !== undefined && l112 !== undefined &&
|
|
Math.min(l111.offsetX, l112.offsetX) === 702 &&
|
|
Math.max(l111.offsetX + l111.sizeX - 1, l112.offsetX + l112.sizeX - 1) === 759 &&
|
|
Math.min(l111.offsetY, l112.offsetY) === 717 &&
|
|
Math.max(l111.offsetY + l111.sizeY, l112.offsetY + l112.sizeY) === 1026
|
|
|
|
if (cond1 && cond2 && cond3 && cond4 && cond5 && cond6) {
|
|
p3Passed++
|
|
} else {
|
|
p3Failed++
|
|
console.error(`P3 FAIL: Seed ${seed}, conds:`, { cond1, cond2, cond3, cond4, cond5, cond6 })
|
|
}
|
|
}
|
|
|
|
// Property 4: Determinism replay
|
|
p4Total++
|
|
const replay = resolveWorldOffsets(act, seed)
|
|
const aEntries = Array.from(offsets.entries()).sort((a, b) => a[0] - b[0])
|
|
const bEntries = Array.from(replay.entries()).sort((a, b) => a[0] - b[0])
|
|
if (JSON.stringify(aEntries) === JSON.stringify(bEntries)) {
|
|
p4Passed++
|
|
} else {
|
|
p4Failed++
|
|
}
|
|
|
|
// Level 33 relative placement at (3996, 966) abutting Level 32
|
|
if (act === 1) {
|
|
l33Total++
|
|
const l32 = offsets.get(32)
|
|
const l33 = offsets.get(33)
|
|
if (
|
|
l32 &&
|
|
l33 &&
|
|
l32.offsetX === 4000 &&
|
|
l32.offsetY === 1000 &&
|
|
l32.sizeX === 18 &&
|
|
l32.sizeY === 20 &&
|
|
l33.offsetX === 3996 &&
|
|
l33.offsetY === 966 &&
|
|
l33.sizeX === 28 &&
|
|
l33.sizeY === 34 &&
|
|
l33.offsetY + l33.sizeY === l32.offsetY && // Abuts north edge exactly at Y = 1000
|
|
!doRectanglesOverlap(l32, l33) && // Interior non-overlap
|
|
Math.max(l32.offsetX, l33.offsetX) === 4000 && // Shared segment [4000, 4018]
|
|
Math.min(l32.offsetX + l32.sizeX, l33.offsetX + l33.sizeX) === 4018
|
|
) {
|
|
l33Passed++
|
|
} else {
|
|
l33Failed++
|
|
console.error(`L33 FAIL: Seed ${seed}, l32=${JSON.stringify(l32)}, l33=${JSON.stringify(l33)}`)
|
|
}
|
|
|
|
// Level 27 relative placement on Level 26
|
|
l27Total++
|
|
const l26 = offsets.get(26)
|
|
const l27 = offsets.get(27)
|
|
if (
|
|
l26 &&
|
|
l27 &&
|
|
l27.offsetX === l26.offsetX + Math.floor((l26.sizeX - l27.sizeX) / 2) &&
|
|
l27.offsetY === l26.offsetY - l27.sizeY &&
|
|
l27.sizeX === 56 &&
|
|
l27.sizeY === 40 &&
|
|
l27.offsetY + l27.sizeY === l26.offsetY &&
|
|
!doRectanglesOverlap(l26, l27)
|
|
) {
|
|
l27Passed++
|
|
} else {
|
|
l27Failed++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
totalAssertions =
|
|
sanityTotal +
|
|
p1Total +
|
|
p2aTotal +
|
|
p2bTotal +
|
|
p3Total +
|
|
p4Total +
|
|
l33Total +
|
|
l27Total
|
|
totalFailures =
|
|
sanityFailed +
|
|
p1Failed +
|
|
p2aFailed +
|
|
p2bFailed +
|
|
p3Failed +
|
|
p4Failed +
|
|
l33Failed +
|
|
l27Failed
|
|
|
|
console.log('\n--------------------------------------------------------------------------------')
|
|
console.log('RESULTS BY PROPERTY:')
|
|
console.log('--------------------------------------------------------------------------------')
|
|
console.log(`Property 1 (Seamless Boundary Sharing, sharedLength > 0):`)
|
|
console.log(` Passed: ${p1Passed} / ${p1Total} (${((p1Passed / p1Total) * 100).toFixed(2)}%)`)
|
|
console.log(` Failed: ${p1Failed}`)
|
|
|
|
console.log(`Property 2a (Seamless Levels Interior Non-Overlap):`)
|
|
console.log(` Passed: ${p2aPassed} / ${p2aTotal} (${((p2aPassed / p2aTotal) * 100).toFixed(2)}%)`)
|
|
console.log(` Failed: ${p2aFailed}`)
|
|
|
|
console.log(`Property 2b (All Placed Levels Interior Non-Overlap):`)
|
|
console.log(` Passed: ${p2bPassed} / ${p2bTotal} (${((p2bPassed / p2bTotal) * 100).toFixed(4)}%)`)
|
|
console.log(` Failed: ${p2bFailed}`)
|
|
|
|
console.log(`Property 3 (Act 5 Level 111 Alignment X = 702 & Automap Cluster):`)
|
|
console.log(` Passed: ${p3Passed} / ${p3Total} (${((p3Passed / p3Total) * 100).toFixed(2)}%)`)
|
|
console.log(` Failed: ${p3Failed}`)
|
|
|
|
console.log(`Property 4 (Determinism Replay across 100 Seeds x 5 Acts):`)
|
|
console.log(` Passed: ${p4Passed} / ${p4Total} (${((p4Passed / p4Total) * 100).toFixed(2)}%)`)
|
|
console.log(` Failed: ${p4Failed}`)
|
|
|
|
console.log(`Level 33 Placement at (3996, 966) Abutting Level 32:`)
|
|
console.log(` Passed: ${l33Passed} / ${l33Total} (${((l33Passed / l33Total) * 100).toFixed(2)}%)`)
|
|
console.log(` Failed: ${l33Failed}`)
|
|
|
|
console.log(`Level 27 Placement Abutting Level 26:`)
|
|
console.log(` Passed: ${l27Passed} / ${l27Total} (${((l27Passed / l27Total) * 100).toFixed(2)}%)`)
|
|
console.log(` Failed: ${l27Failed}`)
|
|
|
|
console.log(`Coordinate Sanity (Non-negative, finite):`)
|
|
console.log(` Passed: ${sanityPassed} / ${sanityTotal} (${((sanityPassed / sanityTotal) * 100).toFixed(2)}%)`)
|
|
console.log(` Failed: ${sanityFailed}`)
|
|
|
|
console.log('================================================================================')
|
|
console.log(`TOTAL ASSERTIONS EXECUTED: ${totalAssertions}`)
|
|
console.log(`TOTAL FAILURES RECORDED: ${totalFailures}`)
|
|
console.log(`EMPIRICAL CHALLENGER VERDICT: ${totalFailures === 0 ? 'APPROVE' : 'CHALLENGE_FAILED'}`)
|
|
console.log('================================================================================')
|
|
|
|
if (totalFailures > 0) {
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
runAdversarialAudit().catch(err => {
|
|
console.error('Fatal error during adversarial audit:', err)
|
|
process.exit(1)
|
|
})
|