Yog.Builder.Grid (YogEx v0.70.0)

Copy Markdown View Source

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:

Movement Predicates

  • walkable/1 - Only allow movement into specific cell values
  • avoiding/1 - Allow movement except into specific cell values
  • always/0 - Always allow movement
  • including/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

grid()

@type grid() :: {:grid_builder, Yog.graph(), integer(), integer()}

Grid builder type: {:grid_builder, graph, rows, cols}

topology()

@type topology() :: [{integer(), integer()}]

Topology is a list of {row_delta, col_delta} movement offsets.

Functions

always()

@spec always() :: (term(), term() -> boolean())

Always allows movement between adjacent cells.

Examples

iex> can_move = Yog.Builder.Grid.always()
iex> can_move.("anything", "works")
true

avoiding(wall_value)

@spec avoiding(term()) :: (term(), term() -> boolean())

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

bishop()

@spec bishop() :: topology()

4-way diagonal movement.

Movement offsets: {-1,-1}, {-1,1}, {1,-1}, {1,1}

chebyshev_distance(from_id, to_id, cols)

@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|)

coord_to_id(row, col, cols)

@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

find_node(arg1, predicate)

@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

from_2d_list(grid_data, graph_type, can_move_fn)

@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 values
  • graph_type - :directed or :undirected
  • can_move_fn - Function (from_cell, to_cell) -> boolean determining 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

from_2d_list_with_topology(grid_data, graph_type, topology, can_move_fn)

@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

get_cell(grid, row, col)

@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.

id_to_coord(id, cols)

@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}

including(valid_values)

@spec including([term()]) :: (term(), term() -> boolean())

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

knight()

@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}

manhattan_distance(from_id, to_id, cols)

@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|

octile_distance(from_id, to_id, cols)

@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)

queen()

@spec queen() :: topology()

8-way movement (cardinal + diagonal).

Combines rook/0 and bishop/0. Use with appropriate distance heuristic.

rook()

@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}

to_graph(grid)

@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.

walkable(valid_value)

@spec walkable(term()) :: (term(), term() -> boolean())

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