"use strict"; const path = require("path"); global.window = global; global.CONFIG = { worldPadding: 28 }; global.clamp = (v, a, b) => Math.max(a, Math.min(b, v)); class World {} global.World = World; require(path.join(__dirname, "..", "js", "world_pathfinding_system.js")); function makeWorld({ directBlocked = false } = {}) { const w = new World(); w.w = 1400; w.h = 1000; w.time = 10; w.routingObstacleVersion = 3; w.pointChecks = 0; w.edgeChecks = 0; w.pointBlockedByObstacle = () => { w.pointChecks++; return false; }; w.pathBlockedByFence = () => { w.edgeChecks++; return directBlocked; }; return w; } function waypointFor(id) { const w = makeWorld(); const actor = { id, x: 120, y: 120, radius: 20 }; const target = { id: "goal", x: 1120, y: 780 }; const first = w.findGridPathWaypoint(actor, target, { gridStep: 58, state: "seek_food" }); if (!first) throw new Error(`no path for ${id}`); const pointAfterFirst = w.pointChecks; const edgeAfterFirst = w.edgeChecks; const again = w.findGridPathWaypoint(actor, target, { gridStep: 58, state: "seek_food" }); if (!again || first.x !== again.x || first.y !== again.y) throw new Error("same actor route cache is not stable"); if (w.pointChecks !== pointAfterFirst || w.edgeChecks !== edgeAfterFirst) throw new Error("throttled actor cache still performs geometry checks every call"); if (actor._routeCache?.spatialVersion !== 3 || actor._routeCache?.mode !== "grid") throw new Error("unified route cache was not stored"); if (actor._gridPathWaypoint || actor._pathWaypoint) throw new Error("legacy route caches still exist"); w.routingObstacleVersion = 4; w.findGridPathWaypoint(actor, target, { gridStep: 58, state: "seek_food" }); if (actor._routeCache?.spatialVersion !== 4) throw new Error("cache did not invalidate after obstacle version change"); if (w.pointChecks > 18 * 18 * 3) throw new Error(`excessive point checks: ${w.pointChecks}`); if (w.edgeChecks > 18 * 18 * 8 * 3) throw new Error(`excessive edge checks: ${w.edgeChecks}`); return `${first.x.toFixed(3)},${first.y.toFixed(3)}`; } const routes = new Set(Array.from({ length: 64 }, (_, i) => waypointFor(`tarinai-${i}`))); if (routes.size < 2) throw new Error("ID-fixed bias did not diversify independently computed routes"); const directWorld = makeWorld(); const directTarget = { id: "direct-goal", x: 600, y: 400 }; const directActor = { id: "direct-a", x: 100, y: 100, radius: 20 }; directWorld.findTarinaiPathWaypoint(directActor, directTarget, { state: "seek_food" }); const directChecks = directWorld.edgeChecks; for (let i = 0; i < 100; i++) directWorld.findTarinaiPathWaypoint(directActor, directTarget, { state: "seek_food" }); if (directWorld.edgeChecks !== directChecks) throw new Error("direct-route throttle did not suppress repeated fence checks"); const w = makeWorld(); w.markSpatialDirty = World.prototype.markSpatialDirty; console.log(`[OK] throttled actor-local route cache, obstacle invalidation, stable ID bias, and route diversity passed (${routes.size} first-waypoint variants)`);