58 lines
3 KiB
JavaScript
58 lines
3 KiB
JavaScript
"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)`);
|