diff --git a/gameblocks/modules/behavior/GridPathPlanner.js b/gameblocks/modules/behavior/GridPathPlanner.js index c352caf..fe770e2 100644 --- a/gameblocks/modules/behavior/GridPathPlanner.js +++ b/gameblocks/modules/behavior/GridPathPlanner.js @@ -27,6 +27,13 @@ function wrapDelta(delta, size) { return Math.min(Math.abs(delta), size - Math.abs(delta)); } +function wrapCoord(value, size) { + if (!Number.isFinite(size) || size <= 0) return 0; + let wrapped = value % size; + if (wrapped < 0) wrapped += size; + return wrapped; +} + function priorityInsert(open, entry) { let index = open.length; while (index > 0 && open[index - 1].f > entry.f) { @@ -43,10 +50,8 @@ function stepCell(cell, direction, board, wrap, navigation) { }; if (wrap) { - if (next.right < 0) next.right = board.columns - 1; - if (next.right >= board.columns) next.right = 0; - if (next.forward < 0) next.forward = board.rows - 1; - if (next.forward >= board.rows) next.forward = 0; + next.right = wrapCoord(next.right, board.columns); + next.forward = wrapCoord(next.forward, board.rows); return next; } diff --git a/gameblocks/modules/behavior/GridPathPlanner.wrap-neighbors.test.js b/gameblocks/modules/behavior/GridPathPlanner.wrap-neighbors.test.js new file mode 100644 index 0000000..d37894f --- /dev/null +++ b/gameblocks/modules/behavior/GridPathPlanner.wrap-neighbors.test.js @@ -0,0 +1,33 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { GridPathPlanner } from './GridPathPlanner.js'; + +const navigation = { + vectors: { + north: { right: 0, forward: 1 }, + east: { right: 1, forward: 0 }, + south: { right: 0, forward: -1 }, + west: { right: -1, forward: 0 }, + }, + neighborOrder: ['north', 'east', 'south', 'west'], +}; + +test('wrap neighbors from the origin use torus edges, not clamp', () => { + const planner = new GridPathPlanner({ navigation, columns: 10, rows: 10, wrap: true }); + const neighbors = Object.fromEntries( + planner.getNeighbors({ right: 0, forward: 0 }).map((entry) => [entry.direction, entry.cell]) + ); + assert.deepEqual(neighbors.west, { right: 9, forward: 0 }); + assert.deepEqual(neighbors.south, { right: 0, forward: 9 }); + assert.deepEqual(neighbors.east, { right: 1, forward: 0 }); +}); + +test('wrap neighbors from far out-of-board cells use modulo, not the far edge', () => { + const planner = new GridPathPlanner({ navigation, columns: 10, rows: 10, wrap: true }); + const neighbors = Object.fromEntries( + planner.getNeighbors({ right: -5, forward: 0 }).map((entry) => [entry.direction, entry.cell]) + ); + // -5 + 1 = -4 → 6 on a period-10 torus. Clamp-to-edge would have produced 9. + assert.deepEqual(neighbors.east, { right: 6, forward: 0 }); + assert.deepEqual(neighbors.west, { right: 4, forward: 0 }); +});