Grids topic

Grid data structures and adaptors to work as a graph.


Table of Contents:


Overview

A grid is a collection of elements accessible by a two-dimensional index and with a default (empty) value:

enum Tile {
  wall,
  floor,
}

// Create a 5x8 grid filled with `Tile.wall`.
//
// When resizing a grid, the `empty` value is used to fill the new cells.
final grid = Grid.filled(5, 8, empty: Tile.wall);

Or, to specify a different default fill:

// Create a 5x8 grid filled with `Tile.floor`, but the default is `Tile.wall`.
final grid = Grid.filled(5, 8, empty: Tile.wall, fill: Tile.floor);

To retrieve and update cells in the grid:

// Retrieve the value at (2, 3).
print(grid.get(2, 3)); // Tile.wall

// Update the value at (2, 3).
grid.set(2, 3, Tile.floor);
print(grid.get(2, 3)); // Tile.floor

Or to iterate over the grid:

for (final row in grid.rows) {
  for (final cell in row) {
    print(cell);
  }
}

Sparse Grids

The default grid implementation is a dense grid, ListGrid, which is backed by a 1-dimensional List.

Dense grids are ideal for small to medium-sized grids, or where most cells are filled with the non-empty value. For larger grids where most cells are empty and iteration over the full set of cells is a rare operation, a sparse grid, provided by SplayTreeGrid, can be more efficient:

final grid = SplayTreeGrid.fileld(5, 8, empty: Tile.wall);

print(grid.get(2, 3)); // Tile.wall
print(grid.nonEmptyEntries.length); // 0

Adapting a Grid to a Graph

A Grid can be represented as a graph-like structure with GridWalkable:

final grid = Grid.filled(5, 8, empty: Tile.wall);
final graph = GridWalkable.from(grid);

An implicit grid is formed where each cell is a node, and edges are created between horizontal and vertical neighbors.

Alternatively, custom directions can be specified:

// Use diagonal directions instead of horizontal and vertical.
final a = GridWalkable.diagonal(grid);

// Use all 8 directions, both horizontal, vertical, and diagonal.
final b = GridWalkable.all8Directions(grid);

// Or, to specify your own directions. For example, just left and down:
final c = GridWalkable.from(grid, directions: [
  const Pos(1, 0),
  const Pos(0, 1),
]);

These adaptors are entirely lazy, and you could imagine using a different one for different movement patterns:

// A knight in chess can move in an L-shape.
const chessKnight = [
  Pos(1, 2),
  Pos(2, 1),
  Pos(2, -1),
  Pos(1, -2),
  Pos(-1, -2),
  Pos(-2, -1),
  Pos(-2, 1),
  Pos(-1, 2),
];

Classes

Grid<E> Grids
A collection of elements accessible by a two-dimensional index.
GridWalkable<E> Grids
Adapts a Grid into a WeightedWalkable.
ListGrid<E> Grids
A dense grid implementation using a 1-dimension List to store elements.
SplayTreeGrid<E> Grids
A sparse grid implementation backed by a SplayTreeMap.