This commit is contained in:
scamiv
2026-02-27 01:47:29 +01:00
parent 0275f6a9ef
commit 7a3de19a3e
10 changed files with 844 additions and 117 deletions
@@ -0,0 +1,60 @@
import { describe, expect, it } from "vitest";
import { GameMapImpl } from "../src/core/game/GameMap";
import { densePathToKeypointSegments } from "../src/core/game/MotionPlans";
function makeMap(width: number, height: number): GameMapImpl {
return new GameMapImpl(width, height, new Uint8Array(width * height), 0);
}
function expandPlanDda(
map: GameMapImpl,
points: Uint32Array,
segmentSteps: Uint32Array,
): number[] {
const out: number[] = [];
if (points.length === 0) return out;
out.push(points[0] >>> 0);
for (let i = 0; i < segmentSteps.length; i++) {
const steps = segmentSteps[i] >>> 0;
const a = points[i] >>> 0;
const b = points[i + 1] >>> 0;
const ax = map.x(a);
const ay = map.y(a);
const bx = map.x(b);
const by = map.y(b);
const dx = bx - ax;
const dy = by - ay;
for (let t = 1; t <= steps; t++) {
out.push(
map.ref(
Math.round(ax + (dx * t) / steps),
Math.round(ay + (dy * t) / steps),
) >>> 0,
);
}
}
return out;
}
describe("densePathToKeypointSegments", () => {
it("expands back to the dense path for axis segments", () => {
const map = makeMap(10, 10);
const dense = [
map.ref(1, 1),
map.ref(2, 1),
map.ref(3, 1),
map.ref(4, 1),
map.ref(4, 2),
map.ref(4, 3),
map.ref(4, 4),
];
const plan = densePathToKeypointSegments(dense);
expect(plan).not.toBeNull();
if (!plan) return;
const expanded = expandPlanDda(map, plan.points, plan.segmentSteps);
expect(expanded).toEqual(dense.map((t) => t >>> 0));
});
});
+62
View File
@@ -0,0 +1,62 @@
import { describe, expect, it } from "vitest";
import {
packMotionPlans,
unpackMotionPlans,
} from "../src/core/game/MotionPlans";
describe("MotionPlans grid_segments", () => {
it("packs/unpacks grid_segments", () => {
const packed = packMotionPlans([
{
kind: "grid_segments",
unitId: 123,
planId: 7,
startTick: 10,
ticksPerStep: 2,
points: Uint32Array.from([1, 6, 11]),
segmentSteps: Uint32Array.from([5, 5]),
},
]);
const records = unpackMotionPlans(packed);
expect(records).toHaveLength(1);
const r = records[0];
expect(r.kind).toBe("grid_segments");
if (r.kind !== "grid_segments") throw new Error("type guard");
expect(r.unitId).toBe(123);
expect(r.planId).toBe(7);
expect(r.startTick).toBe(10);
expect(r.ticksPerStep).toBe(2);
expect(Array.from(r.points)).toEqual([1, 6, 11]);
expect(Array.from(r.segmentSteps)).toEqual([5, 5]);
});
it("skips unknown kinds using wordCount", () => {
const gridPacked = packMotionPlans([
{
kind: "grid_segments",
unitId: 1,
planId: 1,
startTick: 1,
ticksPerStep: 1,
points: Uint32Array.from([10, 12]),
segmentSteps: Uint32Array.from([2]),
},
]);
const gridRecordWords = gridPacked.slice(1); // strip recordCount
const unknownWordCount = 4;
const out = new Uint32Array(1 + unknownWordCount + gridRecordWords.length);
out[0] = 2;
let o = 1;
out[o++] = 999;
out[o++] = unknownWordCount;
out[o++] = 111;
out[o++] = 222;
out.set(gridRecordWords, o);
const records = unpackMotionPlans(out);
expect(records).toHaveLength(1);
expect(records[0].kind).toBe("grid_segments");
});
});
+34
View File
@@ -0,0 +1,34 @@
import { describe, expect, it } from "vitest";
import { PathFinderStepper } from "../src/core/pathfinding/PathFinderStepper";
import { PathStatus } from "../src/core/pathfinding/types";
describe("PathFinderStepper cache priming", () => {
it("does not prime next() cache via findPath()", () => {
let calls = 0;
const finder = {
findPath(from: number | number[], to: number) {
calls++;
const start = Array.isArray(from) ? from[0] : from;
return [start, to];
},
};
const stepper = new PathFinderStepper<number>(finder, {
equals: (a, b) => a === b,
});
const from = 10;
const to = 42;
const path = stepper.findPath(from, to);
expect(path).toEqual([from, to]);
expect(calls).toBe(1);
const r1 = stepper.next(from, to);
expect(r1.status).toBe(PathStatus.NEXT);
if (r1.status === PathStatus.NEXT) {
expect(r1.node).toBe(to);
}
expect(calls).toBe(2);
});
});