Convenience builder for 2D Grid graphs from nested lists.
Supports custom movement topologies (rook, bishop, queen, knight) and movement predicates (walkable, avoiding, always).
Example
Create a maze grid:
maze = [
[".", ".", "#", "."],
[".", "#", "#", "."],
[".", ".", ".", "."]
]
grid = Yog.Builder.Grid.from_2d_list(
maze,
:undirected,
Yog.Builder.Grid.walkable(".")
)
graph = Yog.Builder.Grid.to_graph(grid)Distance Functions
Use these with A* pathfinding on grids:
manhattan_distance/3- For 4-way (rook) movementchebyshev_distance/3- For 8-way (queen) movementoctile_distance/3- For 8-way with diagonal costs
Movement Predicates
walkable/1- Only allow movement into specific cell valuesavoiding/1- Allow movement except into specific cell valuesalways/0- Always allow movementincluding/1- Allow movement into any of the specified values
Migration Note: This module was ported from Gleam to pure Elixir in v0.53.0. The API remains unchanged.
Summary
Types
Grid builder type: {:grid_builder, graph, rows, cols}
Topology is a list of {row_delta, col_delta} movement offsets.
Functions
Always allows movement between adjacent cells.
Creates a predicate that allows movement into any cell except wall_value.
4-way diagonal movement.
Calculates the Chebyshev distance between two grid node IDs.
Converts grid coordinates {row, col} to a node ID.
Finds a node in the grid where the cell data matches a predicate.
Creates a labeled grid graph from a 2D list of cell data.
Creates a grid graph using a custom movement topology.
Gets the cell data at a specific row and column.
Converts a node ID back to grid coordinates {row, col}.
Creates a predicate that allows movement into any of the specified values.
L-shaped knight jumps in all 8 orientations.
Calculates the Manhattan distance between two grid node IDs.
Calculates the Octile distance between two grid node IDs.
8-way movement (cardinal + diagonal).
4-way cardinal movement (up, down, left, right).
Converts a grid builder into a usable Graph for algorithms.
Creates a predicate that only allows movement into cells matching valid_value.
Types
Functions
Always allows movement between adjacent cells.
Examples
iex> can_move = Yog.Builder.Grid.always()
iex> can_move.("anything", "works")
true
Creates a predicate that allows movement into any cell except wall_value.
Examples
iex> can_move = Yog.Builder.Grid.avoiding("#")
iex> can_move.(".", ".")
true
iex> can_move.(".", "#")
false
@spec bishop() :: topology()
4-way diagonal movement.
Movement offsets: {-1,-1}, {-1,1}, {1,-1}, {1,1}
@spec chebyshev_distance(Yog.node_id(), Yog.node_id(), integer()) :: integer()
Calculates the Chebyshev distance between two grid node IDs.
Use with 8-way (queen) movement where diagonal costs equal cardinal costs.
Formula
distance = max(|row1 - row2|, |col1 - col2|)
@spec coord_to_id(integer(), integer(), integer()) :: Yog.node_id()
Converts grid coordinates {row, col} to a node ID.
Examples
iex> Yog.Builder.Grid.coord_to_id(2, 3, 10)
23
@spec find_node( Yog.Builder.GridGraph.t() | grid() | {:grid, Yog.graph(), integer(), integer()}, (term() -> boolean()) ) :: {:ok, Yog.node_id()} | {:error, nil}
Finds a node in the grid where the cell data matches a predicate.
Returns {:ok, node_id} or {:error, nil}.
Examples
iex> grid = Yog.Builder.Grid.from_2d_list([["S", "."]], :undirected, Yog.Builder.Grid.always())
iex> {:ok, start_id} = Yog.Builder.Grid.find_node(grid, fn cell -> cell == "S" end)
iex> is_integer(start_id)
true
@spec from_2d_list([[term()]], Yog.Model.graph_type(), (term(), term() -> boolean())) :: Yog.Builder.GridGraph.t()
Creates a labeled grid graph from a 2D list of cell data.
Nodes are labeled as {row, col} tuples.
Edges are created between adjacent cells (up, down, left, right) using
the default rook (4-way) topology.
Parameters
grid_data- 2D list of cell valuesgraph_type-:directedor:undirectedcan_move_fn- Function(from_cell, to_cell) -> booleandetermining if movement is allowed
Examples
iex> maze = [[".", ".", "#"], [".", "#", "."], [".", ".", "."]]
iex> grid = Yog.Builder.Grid.from_2d_list(maze, :undirected, Yog.Builder.Grid.walkable("."))
iex> is_struct(grid, Yog.Builder.GridGraph)
true
@spec from_2d_list_with_topology( [[term()]], Yog.Model.graph_type(), topology(), (term(), term() -> boolean()) ) :: Yog.Builder.GridGraph.t()
Creates a grid graph using a custom movement topology.
The topology is a list of {row_delta, col_delta} tuples defining
which neighbors each cell can reach. Use presets like rook/0, bishop/0,
queen/0, or knight/0.
Examples
iex> grid_data = [[1, 2], [3, 4]]
iex> grid = Yog.Builder.Grid.from_2d_list_with_topology(
...> grid_data,
...> :directed,
...> Yog.Builder.Grid.queen(),
...> Yog.Builder.Grid.always()
...> )
iex> is_struct(grid, Yog.Builder.GridGraph)
true
@spec get_cell( Yog.Builder.GridGraph.t() | grid() | {:grid, Yog.graph(), integer(), integer()}, integer(), integer() ) :: {:ok, term()} | {:error, nil}
Gets the cell data at a specific row and column.
Returns {:ok, cell_data} or {:error, nil} if out of bounds.
@spec id_to_coord(Yog.node_id(), integer()) :: {integer(), integer()}
Converts a node ID back to grid coordinates {row, col}.
Examples
iex> Yog.Builder.Grid.id_to_coord(23, 10)
{2, 3}
Creates a predicate that allows movement into any of the specified values.
Examples
iex> can_move = Yog.Builder.Grid.including([".", "S", "G"])
iex> can_move.(".", "S")
true
iex> can_move.(".", "#")
false
@spec knight() :: topology()
L-shaped knight jumps in all 8 orientations.
Chess knight movement: {-2,-1}, {-2,1}, {-1,-2}, {-1,2}, {1,-2}, {1,2}, {2,-1}, {2,1}
@spec manhattan_distance(Yog.node_id(), Yog.node_id(), integer()) :: integer()
Calculates the Manhattan distance between two grid node IDs.
Use with 4-way (rook) movement. Diagonal movement is not allowed.
Formula
distance = |row1 - row2| + |col1 - col2|
@spec octile_distance(Yog.node_id(), Yog.node_id(), integer()) :: float()
Calculates the Octile distance between two grid node IDs.
Use with 8-way movement where diagonal costs are sqrt(2) cardinal cost. Returns a float for precise A heuristics.
Formula
dx = |col1 - col2|
dy = |row1 - row2|
distance = max(dx, dy) + (sqrt(2) - 1) * min(dx, dy)
@spec queen() :: topology()
8-way movement (cardinal + diagonal).
Combines rook/0 and bishop/0. Use with appropriate distance heuristic.
@spec rook() :: topology()
4-way cardinal movement (up, down, left, right).
Default for from_2d_list/3. Movement offsets: {-1,0}, {1,0}, {0,-1}, {0,1}
@spec to_graph( Yog.Builder.GridGraph.t() | grid() | {:grid, Yog.graph(), integer(), integer()} ) :: Yog.graph()
Converts a grid builder into a usable Graph for algorithms.
Creates a predicate that only allows movement into cells matching valid_value.
Examples
iex> can_move = Yog.Builder.Grid.walkable(".")
iex> can_move.(".", ".")
true
iex> can_move.(".", "#")
false