Grid Module
A builder for creating graphs from 2D grids.
This module provides convenient ways to convert 2D grids (like heightmaps, mazes, or game boards) into graphs for pathfinding and traversal algorithms.
Key Concepts
- Row-Major Ordering: Node ID = row × cols + col
- Topology: Movement patterns (rook/bishop/queen/knight)
- Predicate: Function that determines if movement is allowed
Movement Topologies
The module provides 4 chess-inspired movement patterns:
Rook (4-way cardinal):
. ↑ . ← · → . ↓ .
Bishop (4-way diagonal):
↖ . ↗ . · . ↙ . ↘
Queen (8-way):
↖ ↑ ↗ ← · → ↙ ↓ ↘
Knight (L-shaped):
. ♞ . ♞ . ♞ . . . ♞ . . · . . ♞ . . . ♞ . ♞ . ♞ .
Example Usage
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: |
|
Use Cases
- Game pathfinding: Character movement on tile-based maps
- Maze solving: Finding paths through grid-based mazes
- Heightmap navigation: Terrain traversal with elevation constraints
- Puzzle solving: Grid-based puzzles like Sokoban, sliding puzzles
module Grid from Yog.Builder
<summary> A builder for creating graphs from 2D grids. This module provides convenient ways to convert 2D grids (like heightmaps, mazes, or game boards) into graphs for pathfinding and traversal algorithms. ## Key Concepts - **Row-Major Ordering**: Node ID = row × cols + col - **Topology**: Movement patterns (rook/bishop/queen/knight) - **Predicate**: Function that determines if movement is allowed ## Movement Topologies The module provides 4 chess-inspired movement patterns: **Rook (4-way cardinal):** . ↑ . ← · → . ↓ . **Bishop (4-way diagonal):** ↖ . ↗ . · . ↙ . ↘ **Queen (8-way):** ↖ ↑ ↗ ← · → ↙ ↓ ↘ **Knight (L-shaped):** . ♞ . ♞ . ♞ . . . ♞ . . · . . ♞ . . . ♞ . ♞ . ♞ . ## Example Usage open Yog.Builder // A simple heightmap where you can only climb up by 1 let heightmap = array2D [[1; 2; 3] [4; 5; 6] [7; 8; 9]] // Build a graph with rook movement, can move if height diff <= 1 let grid = Grid.fromArray2D heightmap Directed Grid.rook (fun fromHeight toHeight -> toHeight - fromHeight <= 1) // Convert to graph and use with algorithms let graph = Grid.toGraph grid let start = Grid.coordToId 0 0 grid.Cols // Top-left let goal = Grid.coordToId 2 2 grid.Cols // Bottom-right // Use with pathfinding Yog.Pathfinding.AStar.aStarInt (fun from to' -> Grid.manhattanDistance from to' grid.Cols) start goal graph ## Use Cases - **Game pathfinding**: Character movement on tile-based maps - **Maze solving**: Finding paths through grid-based mazes - **Heightmap navigation**: Terrain traversal with elevation constraints - **Puzzle solving**: Grid-based puzzles like Sokoban, sliding puzzles </summary>
--------------------
type Grid<'CellData,'EdgeData> = { Graph: Graph<'CellData,'EdgeData> Rows: int Cols: int }
<summary> A grid builder that wraps a graph and maintains grid dimensions. The grid uses row-major ordering: node_id = row × cols + col ## Type Parameters - 'CellData: The data stored in each grid cell (becomes node data) - 'EdgeData: The data stored on edges (typically int for weights) </summary>
<summary> Builds a grid-graph from a 2D array and a custom movement topology. Each cell becomes a node, and edges are added between cells based on the topology and the canMove predicate. ## Parameters - gridData: 2D array of cell data - kind: Directed or Undirected - topology: List of (row_delta, col_delta) offsets (e.g., rook, queen) - canMove: Predicate function (fromData -> toData -> bool) ## Returns Grid with the underlying graph and dimensions ## Time Complexity O(rows × cols × |topology|) ## Example // 8-way movement on a maze let maze = array2D [[".";"#";"."] [".";".";"."] ["#";".";"."] ] let grid = fromArray2D maze Undirected queen (avoiding "#") </summary>
<summary> Cardinal (4-way) movement: up, down, left, right. Named after the rook in chess, which moves along ranks and files. . ↑ . ← · → . ↓ . </summary>
<summary> Converts the grid to a standard Graph. The resulting graph can be used with all yog algorithms. ## Example let graph = toGraph grid // Now use with pathfinding, traversal, etc. </summary>
<summary> Converts grid coordinates (row, col) to a node ID. Uses row-major ordering: id = row × cols + col ## Example coordToId 0 0 3 // => 0 coordToId 1 2 3 // => 5 coordToId 2 1 3 // => 7 </summary>
<summary> A* (A-Star) search algorithm for optimal pathfinding with heuristic guidance. A* is an informed search algorithm that finds the shortest path from a start node to a goal node using a heuristic function to guide exploration. It combines the completeness of Dijkstra's algorithm with the efficiency of greedy best-first search. ## Algorithm | Algorithm | Function | Complexity | Best For | |-----------|----------|------------|----------| | A* Search | aStar | O((V + E) log V) | Pathfinding with good heuristics | | Implicit A* | implicitAStar | O((V + E) log V) | Large/infinite graphs generated on-demand | ## Key Concepts - **Evaluation Function**: f(n) = g(n) + h(n) - g(n): Actual cost from start to node n - h(n): Heuristic estimate from n to goal - f(n): Estimated total cost through n - **Admissible Heuristic**: h(n) ≤ actual cost (never overestimates) - **Consistent Heuristic**: h(n) ≤ cost(n→n') + h(n') (triangle inequality) ## When to Use A* **Use A* when:** - You have a specific goal node (not single-source to all) - You can provide a good heuristic estimate - The heuristic is admissible (underestimates) **Use Dijkstra when:** - No good heuristic available (h(n) = 0 reduces A* to Dijkstra) - You need shortest paths to all nodes from a source ## Heuristic Examples | Domain | Heuristic | Admissible? | |--------|-----------|-------------| | Grid (4-way) | Manhattan distance | Yes | | Grid (8-way) | Chebyshev distance | Yes | | Geospatial | Haversine/great-circle | Yes | | Road networks | Precomputed landmarks | Yes | ## Use Cases - Video games: NPC pathfinding on game maps - GPS navigation: Route planning with distance estimates - Robotics: Motion planning with obstacle avoidance - Puzzle solving: Sliding puzzles, mazes, labyrinths </summary>
<summary> Finds the shortest path using A* with integer weights. </summary>
<summary> Calculates the Manhattan distance between two node IDs. This is useful as a heuristic for A* pathfinding on grids. Manhattan distance is the sum of absolute differences in coordinates: |x1 - x2| + |y1 - y2| ## Example let start = coordToId 0 0 10 let goal = coordToId 3 4 10 let distance = manhattanDistance start goal 10 // => 7 (3 + 4) </summary>
Functions and values
| Function or value |
Description
|
||
Full Usage:
always arg1 arg2
Parameters:
'a
arg1 : 'b
Returns: bool
|
Always allows movement between adjacent cells. Every neighbor pair gets an edge regardless of cell data. Useful for fully connected grids or when the cell data is purely informational (e.g., storing coordinates or labels).
Example
val labels: string array2d
val array2D: rows: #('T seq) seq -> 'T array2d
val grid: obj
|
||
Full Usage:
avoiding wallValue from to'
Parameters:
'a
from : 'a
to' : 'a
Returns: bool
|
Allows movement between any cells except the specified wall value. Useful for maze-style grids where "#" or similar marks a wall. Both the source and destination cells must not be the wall value.
Example
val maze: string array2d
val array2D: rows: #('T seq) seq -> 'T array2d
val grid: obj
|
||
Full Usage:
bishop
Returns: (int * int) list
|
Diagonal (4-way) movement: the four diagonal directions. Named after the bishop in chess, which moves along diagonals. ↖ . ↗ . · . ↙ . ↘
|
||
Full Usage:
coordToId row col cols
Parameters:
int32
col : int32
cols : int
Returns: int32
|
Converts grid coordinates (row, col) to a node ID. Uses row-major ordering: id = row × cols + col
Example
|
||
|
Builds a grid-graph from a 2D array and a custom movement topology. Each cell becomes a node, and edges are added between cells based on the topology and the canMove predicate.
Parameters
Time ComplexityO(rows × cols × |topology|) Example
val maze: string array2d
val array2D: rows: #('T seq) seq -> 'T array2d
val grid: obj
|
||
Full Usage:
getCell row col grid
Parameters:
int
col : int
grid : Grid<'a, 'b>
Returns: 'a option
|
Gets the cell data at the specified grid coordinate. Returns Some(cell_data) if the coordinate is valid, None otherwise.
Example
union case Option.Some: Value: 'T -> Option<'T>
val cell: obj
union case Option.None: Option<'T>
|
||
Full Usage:
idToCoord id cols
Parameters:
int
cols : int
Returns: int * int
|
Converts a node ID back to grid coordinates (row, col).
Example
|
||
Full Usage:
knight
Returns: (int * int) list
|
L-shaped jumps in all 8 orientations. Named after the knight in chess, which jumps in an L-shape (2 squares in one direction, 1 square perpendicular). . ♞ . ♞ . ♞ . . . ♞ . . · . . ♞ . . . ♞ . ♞ . ♞ .
|
||
Full Usage:
manhattanDistance fromId toId cols
Parameters:
int
toId : int
cols : int
Returns: int
|
Calculates the Manhattan distance between two node IDs. This is useful as a heuristic for A* pathfinding on grids. Manhattan distance is the sum of absolute differences in coordinates: |x1 - x2| + |y1 - y2|
Example
val start: obj
val goal: obj
val distance: obj
|
||
Full Usage:
queen
Returns: (int * int) list
|
All 8 surrounding directions: cardinal + diagonal. Named after the queen in chess, which combines rook and bishop movement. ↖ ↑ ↗ ← · → ↙ ↓ ↘
|
||
Full Usage:
rook
Returns: (int * int) list
|
Cardinal (4-way) movement: up, down, left, right. Named after the rook in chess, which moves along ranks and files. . ↑ . ← · → . ↓ .
|
||
|
|||
Full Usage:
walkable validValue from to'
Parameters:
'a
from : 'a
to' : 'a
Returns: bool
|
Allows movement only between cells matching the specified value. The inverse of
Example
val terrain: string array2d
val array2D: rows: #('T seq) seq -> 'T array2d
val grid: obj
|