tarinai/scripts/pathfinding_regression_audit.js
2026-07-18 13:06:02 +09:00

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)`);