ai-code-battle/starters/javascript/grid.js
jedarden 7a0de02059 feat(evolver): persist cross-pollination state to Postgres per §10.2
Add crosspoll_state table to persist per-island generation counters
across evolver restarts. Load state on startup and save after each
cross-pollination check. Add persistence pattern and translation
structure tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 16:04:15 -04:00

71 lines
1.9 KiB
JavaScript

/**
* Grid utility functions for AI Code Battle.
*
* Provides toroidal distance calculations, neighbor enumeration,
* and BFS pathfinding on a wrapping grid.
*/
function toroidalManhattan(r1, c1, r2, c2, cols, rows) {
const dr = Math.min(Math.abs(r1 - r2), rows - Math.abs(r1 - r2));
const dc = Math.min(Math.abs(c1 - c2), cols - Math.abs(c1 - c2));
return dr + dc;
}
function toroidalChebyshev(r1, c1, r2, c2, cols, rows) {
const dr = Math.min(Math.abs(r1 - r2), rows - Math.abs(r1 - r2));
const dc = Math.min(Math.abs(c1 - c2), cols - Math.abs(c1 - c2));
return Math.max(dr, dc);
}
function neighbors(row, col, rows, cols) {
const offsets = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1],
];
return offsets.map(([dr, dc]) => [
(row + dr + rows) % rows,
(col + dc + cols) % cols,
]);
}
/**
* BFS pathfinding on a toroidal grid.
*
* @param {[number,number]} start - [row, col]
* @param {[number,number]} goal - [row, col]
* @param {function(number,number): boolean} passable - returns true if walkable
* @param {number} rows
* @param {number} cols
* @returns {[number,number][]|null} path from start to goal (excl. start), or null
*/
function bfs(start, goal, passable, rows, cols) {
const [sr, sc] = start;
const [gr, gc] = goal;
if (sr === gr && sc === gc) return [];
const key = (r, c) => `${r},${c}`;
const visited = new Set([key(sr, sc)]);
const queue = [{ r: sr, c: sc, path: [] }];
while (queue.length > 0) {
const { r, c, path } = queue.shift();
for (const [nr, nc] of neighbors(r, c, rows, cols)) {
const newPath = [...path, [nr, nc]];
if (nr === gr && nc === gc) return newPath;
const k = key(nr, nc);
if (!visited.has(k) && passable(nr, nc)) {
visited.add(k);
queue.push({ r: nr, c: nc, path: newPath });
}
}
}
return null;
}
module.exports = {
toroidalManhattan,
toroidalChebyshev,
neighbors,
bfs,
};