GridGraph

class GridGraph @JvmOverloads constructor(val columns: Int, val rows: Int, val torus: Boolean = false, val movementRule: MovementRule = MovementRule.MOORE, val allowCornerCutting: Boolean = false)(source)

A 2D-lattice graph: cells are first-class nodes, edges represent allowed movement between adjacent cells, and cells carry movement costs and blocked / passable status. Separate from GridProjection, which places agents on a lattice but doesn't model the lattice's navigation structure.

Use GridGraph for:

  • Pathfinding: agents plan routes through an environment with obstacles (evacuation models, warehouse AGVs, robotics).

  • Distance fields: precompute "distance to goal" for every cell once via distanceField, then have agents follow the gradient — typical pattern for many agents sharing a target.

  • Terrain analysis: reachability, bottleneck detection, impact of removing cells from passability.

  • Dynamics on grids: state propagation where edge weights reflect transmission likelihoods (fire spread, contagion on a spatial substrate).

Composition with the other agent-layer primitives:

  • An agent's position lives in a GridProjection (cellOf, placeAt, moveTo).

  • GridGraph is the world the agent navigates. The agent's process { } body calls graph.shortestPath(currentCell, targetCell) to plan, then grid.moveTo(agent, nextCell) to step. The two abstractions interact only through Cell values; neither owns the other.

Edge model:

  • Each cell has a cellCost, default 1.0. Set via setCellCost (and read via cellCostOf).

  • Edge cost from cell u to neighbor v is cellCost(v) * stepLength, where stepLength is 1.0 for orthogonal moves and √2 for diagonal moves (Moore only).

  • Blocked cells (set via block) have no incident edges and are unreachable from any other cell.

Boundary semantics:

  • Bounded (default) — coordinates outside [0, columns) x [0, rows) throw. Cells on the boundary have fewer neighbors.

  • Torus (torus = true) — coordinates wrap; the top-right cell is a neighbor of the top-left cell.

Time complexity:

Corner-cutting (Moore only):

  • By default (allowCornerCutting = false) a diagonal move is permitted only when both orthogonally-adjacent "shoulder" cells it passes between are passable. This prevents agents from slipping diagonally between two blocked cells (clipping a wall corner) — the physically correct behavior for obstacle, evacuation, and warehouse models.

  • Set allowCornerCutting = true to recover the looser rule where a diagonal move only requires its destination to be passable (faster, occasionally desired for abstract grids).

  • Has no effect under VON_NEUMANN (no diagonal moves).

Parameters

columns

lattice width

rows

lattice height

torus

if true, coordinates wrap at boundaries

movementRule

MOORE (8-way) or VON_NEUMANN (4-way)

allowCornerCutting

if true, diagonal moves ignore the passability of the two shoulder cells (default false)

Constructors

Link copied to clipboard
constructor(columns: Int, rows: Int, torus: Boolean = false, movementRule: MovementRule = MovementRule.MOORE, allowCornerCutting: Boolean = false)

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard
Link copied to clipboard

Number of cells currently marked as blocked.

Link copied to clipboard

Total cell count (columns × rows).

Link copied to clipboard
Link copied to clipboard

The minimum cell cost across the grid: min(1.0, smallest explicitly-set cost). Equals 1.0 for a uniform grid. Pass this to GridHeuristics.scaled to keep an A* heuristic admissible when some cells cost less than 1.0.

Link copied to clipboard
Link copied to clipboard
val rows: Int
Link copied to clipboard

Functions

Link copied to clipboard
fun block(cell: Cell)

Mark cell as impassable.

Link copied to clipboard
fun cellCostOf(cell: Cell): Double

Read the cost of entering cell. Default 1.0 for cells not explicitly set.

Link copied to clipboard
fun distanceField(sources: Set<Cell>): Map<Cell, Double>

Multi-source distance field: for each passable cell, the shortest-path cost from the nearest source in sources. The canonical use case is "distance from any exit cell" for evacuation models — compute once at setup, then every agent follows the gradient toward zero in O(1) per step.

fun distanceField(source: Cell): Map<Cell, Double>

Single-source distance field: for each passable cell, the shortest-path cost from source to that cell. Unreachable cells are absent from the result map. Useful when many agents share the same goal; compute the field once via multi-source distanceField, then look up distances per-cell in O(1).

Link copied to clipboard
fun edgeWeight(from: Cell, to: Cell): Double

Weight of the edge from from to to. Equals cellCostOf(to) * stepLength, where stepLength is 1.0 for orthogonal steps and √2 for diagonal steps. The caller is responsible for ensuring to is a neighbor of from under the movement rule; this function does not validate that.

Link copied to clipboard
fun isPassable(cell: Cell): Boolean

True if cell is in bounds (always true on a torus) and not blocked.

Link copied to clipboard
fun isReachable(from: Cell, to: Cell): Boolean

True if to is reachable from from. Uses early-exit BFS; faster than to in reachableFrom(from) for large grids where the target is close.

Link copied to clipboard

Passable neighbors of cell under the configured movement rule. Excludes blocked cells and (for non-torus grids) out-of-range cells. Returns the cells without their edge weights; use edgeWeight if you need the weight too.

Link copied to clipboard
fun reachableFrom(start: Cell): Set<Cell>

All cells reachable from start via passable neighbors (BFS). Includes start itself if it's passable; returns an empty set if start is blocked.

Link copied to clipboard
fun setCellCost(cell: Cell, cost: Double)

Set the per-step cost of entering cell. Must be ≥ 0. Cells with no explicit cost have cost 1.0 implicitly.

Link copied to clipboard
fun shortestPath(from: Cell, to: Cell, heuristic: (current: Cell, target: Cell) -> Double = GridHeuristics.ZERO): WeightedPath<Cell>?

Shortest weighted path from from to to. Returns the path (including both endpoints) plus total cost, or null if to is unreachable. A self-path returns WeightedPath(listOf(from), 0.0).

Link copied to clipboard
fun shortestPathLength(from: Cell, to: Cell, heuristic: (current: Cell, target: Cell) -> Double = GridHeuristics.ZERO): Double

Convenience accessor: total weighted cost from from to to, or Double.POSITIVE_INFINITY if unreachable. Returns 0.0 for the self-path.

Link copied to clipboard
fun unblock(cell: Cell)

Mark cell as passable (no-op if it wasn't blocked).