# `Yog.Builder.Grid`
[🔗](https://github.com/code-shoily/yog_ex/blob/v0.97.0/lib/yog/builder/grid.ex#L1)

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) movement
- `chebyshev_distance/3` - For 8-way (queen) movement
- `octile_distance/3` - For 8-way with diagonal costs

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

# `grid`

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

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

# `topology`

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

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

# `always`

```elixir
@spec always() :: (term(), term() -&gt; boolean())
```

Always allows movement between adjacent cells.

## Examples

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

# `avoiding`

```elixir
@spec avoiding(term()) :: (term(), term() -&gt; 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`

```elixir
@spec bishop() :: topology()
```

4-way diagonal movement.

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

```text
↖ . ↗
. · .
↙ . ↘
```

# `chebyshev_distance`

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

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

```elixir
@spec find_node(
  Yog.Builder.GridGraph.t()
  | grid()
  | {:grid, Yog.graph(), integer(), integer()},
  (term() -&gt; 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`

```elixir
@spec from_2d_list([[term()]], Yog.Model.graph_type(), (term(), term() -&gt; 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`

```elixir
@spec from_2d_list_with_topology(
  [[term()]],
  Yog.Model.graph_type(),
  topology(),
  (term(), term() -&gt;
     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`

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

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

```elixir
@spec including([term()]) :: (term(), term() -&gt; 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`

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

```text
. ♘ .
♞ . ♞
. ♘ .
```

This may seen less conventional, but if rook, bishop and queen could make it, why not knight?

# `manhattan_distance`

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

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

```elixir
@spec queen() :: topology()
```

8-way movement (cardinal + diagonal).

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

```text
↖ ↑ ↗
← · →
↙ ↓ ↘
```

# `rook`

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

```text
. ↑ .
← · →
. ↓ .
```

This is the topology used by `from_2d_list/3`.

# `to_graph`

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

```elixir
@spec walkable(term()) :: (term(), term() -&gt; 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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
