Grid
geometry/grid.ts:67A fixed-size rectangular lattice of Cells addressed by integer
x (column) / y (row) coordinates — the backbone for tile maps, board
games, tactics movement, fog-of-war, and anything else that reasons about
discrete positions.
Coordinate model
The grid is width columns by height rows. x runs 0 … width - 1 left
to right; y runs 0 … height - 1 top to bottom, matching the engine's
screen-space convention (so Cell.up moves toward smaller y). Every
position is named in x, y order throughout — never row, column.
Fixed size, stable cells
Dimensions are immutable: there is no adding or removing rows. Each cell is
created once at construction and lives for the grid's lifetime, so cell
references stay valid and reference-equality (===) is a reliable identity
check — Grid.cellAt returns the same Cell instance every time.
Lookups never throw
Grid.cellAt returns null for out-of-bounds coordinates rather than
throwing, so callers branch on the result. Grid.clamp and
Grid.wrap always resolve to a real cell by folding the coordinate
back in range.
Navigation, queries, and pathfinding
Walk locally with Cell.neighbors and Cell.left/right/up/
down; sweep areas with Grid.line (Bresenham) and
Grid.within (radius); and route with Grid.findPath (A*),
which reads Cell.passable/Cell.cost by default and accepts
per-call overrides.
Example
type Tile = 'floor' | 'wall';
// Seed every cell with floor, then wall off a column.
const grid = new Grid<Tile>(16, 12, () => 'floor');
for (let y = 0; y < grid.height; y++) {
const cell = grid.cellAt(8, y)!;
cell.data = 'wall';
cell.passable = false;
}
grid.cellAt(8, 6)!.passable = true; // a gap in the wall
const path = grid.findPath({ x: 1, y: 6 }, { x: 14, y: 6 });
// -> ordered Cells routing through the gap, or null if blockedConstructors
constructor(width: number, height: number, init?: GridInit<TData>): Grid<TData> Creates a grid and all of its cells.
Parameters
-
widthnumber -
heightnumber -
init?GridInit<TData>
Throws
An EngineError with ErrorCode.GRID_INVALID_SIZE if
width or height is not a positive integer.
Properties
Accessors
Methods
[iterator](): IterableIterator<Cell<TData>> Iterates every cell in row-major order (row 0 left-to-right, then row 1,
and so on), making the grid spreadable and for…of-able.
Returns
IterableIterator<Cell<TData>> Example
for (const cell of grid) {
if (cell.empty) cell.data = 'floor';
}
const all = [...grid];contains(x: number, y: number): boolean Whether an integer coordinate lies within the grid.
Parameters
-
xnumber -
ynumber
Returns
boolean findPath(start: PointPrimitive, end: PointPrimitive, options?: PathfindingOptions<TData>): null | Cell<TData>[] Finds the cheapest path between two coordinates using A*. Delegates to the grid findPath helper.
Walkability and movement cost default to each cell's Cell.passable
and Cell.cost; override them per call via
PathfindingOptions.isPassable / PathfindingOptions.cost
without touching the grid. Connectivity follows
PathfindingOptions.diagonal (4-connected by default), and diagonal
steps cost √2 times the destination cell's cost.
The start is always a valid origin even if impassable; the end must be
passable.
Parameters
-
startPointPrimitive -
endPointPrimitive -
options?PathfindingOptions<TData>
Returns
null | Cell<TData>[] The ordered cells from start to end inclusive, or null if
either endpoint is out of bounds, the end is impassable, or no route
exists.
Example
// Treat a separate boolean mask as the walkability source.
const path = grid.findPath(start, goal, {
diagonal: true,
isPassable: (cell) => mask.cellAt(cell.x, cell.y)?.data === true,
});line(from: PointPrimitive, to: PointPrimitive): Cell<TData>[] Traces a straight line of cells between two coordinates using Bresenham's algorithm — the basis for line-of-sight, ray casts, and beam effects. Endpoints are inclusive; cells that fall outside the grid are skipped.
Parameters
-
fromPointPrimitive -
toPointPrimitive
Example
const sight = grid.line(archer, target);
const blocked = sight.some((cell) => !cell.passable);toString(): string Returns a string representation of this grid (its dimensions).
Returns
string within(center: PointPrimitive, radius: number, options: WithinOptions): Cell<TData>[] Collects the cells within a given radius of a centre coordinate. The radius
is measured with the chosen GridMetric: 'manhattan' (the default)
yields a diamond, 'chebyshev' a square, and 'euclidean' a disc.
Parameters
-
centerPointPrimitive -
radiusnumber -
optionsWithinOptions
Example
const blast = grid.within(impact, 3, { metric: 'euclidean' });
const reachable = grid.within(unit, unit.moves, { includeCenter: false });wrap(x: number, y: number): Cell<TData> Resolves a cell by wrapping the coordinate across the grid's edges, so the grid behaves as a torus. Always returns a cell; negative and overflowing coordinates fold back in range. Non-integer inputs are floored.
Parameters
-
xnumber -
ynumber
Returns
Cell<TData>