Grid
- class w9_pathfinding.envs.Grid
A 2D grid environment for pathfinding with support for obstacles, weighted traversal, diagonal movement, and edge-wrapping.
The grid can be initialized either with an explicit size (width and height) or from a nested list of weights.
- Parameters:
weights (nested list of float, optional) –
A grid-shaped array representing movement cost to enter each node. Each value must be a non-negative float or exactly -1.
A positive value indicates the movement cost for that node.
A value of -1 marks the node as an obstacle (impassable).
width (int, optional) – Width of the grid. Required if weights is not provided.
height (int, optional) – Height of the grid. Required if weights is not provided.
diagonal_movement (DiagonalMovement, default DiagonalMovement.never) – Diagonal movement policy.
passable_left_right_border (bool, default False) – Whether the grid wraps horizontally (left/right edges connect).
passable_up_down_border (bool, default False) – Whether the grid wraps vertically (top/bottom edges connect).
diagonal_movement_cost_multiplier (float, default 1.0) – A multiplier for diagonal moves. Must be in the range [1.0, 2.0]. When diagonal movement is enabled, this value determines how much more expensive diagonal steps are compared to orthogonal ones. This parameter is usually 1.0 or sqrt(2).
pause_weights (nested list of float, optional) –
A grid-shaped array representing the cost of pausing (waiting in place) at each node. Each value must be a non-negative float or exactly -1.
A non-negative value indicates the cost to pause at that node.
A value of -1 means pausing is not allowed at that node.
edge_collision (bool, default False) – Whether to enable edge collision checks. If set to True, prevents two agents from using the same edge simultaneously, even if they are moving in opposite directions. This helps avoid edge conflicts (e.g. node swapping) in multi-agent pathfinding scenarios.
Examples
Create a 3x3 grid with one obstacle in the middle:
>>> grid = Grid(width=3, height=3) # all weights will default to 1.0 >>> grid.add_obstacle((1, 1))
Create a 3x3 grid with custom weights:
>>> grid = Grid( ... [ ... [1, 2, 3], ... [0.1, 0.2, 0.3], ... [-1, -1, -1], # -1 means obstacles; this row is impassable ... ] ... )
- add_obstacle(node)
Mark the specified node as an obstacle.
- Parameters:
node (tuple of int) – The coordinate to mark as an obstacle.
- Raises:
ValueError – If the node is outside the bounds of the grid.
- adjacent(v1, v2) bool
Check whether two nodes are directly connected.
- Parameters:
v1 (node) – First node.
v2 (node) – Second node.
- Returns:
True if there is a direct edge from v1 to v2.
- Return type:
bool
- Raises:
ValueError – If either v1 or v2 is not a valid node in the environment.
- calculate_cost(path) double
Calculate the total cost of a given path through the environment.
If the path is invalid (e.g. contains non-adjacent nodes), this will return -1.
- Parameters:
path (iterable of nodes) – A sequence of nodes representing the path to evaluate.
- Returns:
Total cost of traversing the path, or -1 if the path is invalid.
- Return type:
float
- Raises:
ValueError – If one of the nodes in the path is not a valid node in the environment.
- contains(node) bool
Check if the environment contains a given node.
- Parameters:
node – The node to check.
- Returns:
True if the node is in the environment, False otherwise.
- Return type:
bool
- diagonal_movement
Diagonal movement policy.
- Returns:
Enum value representing how diagonal movement is handled.
- Return type:
- diagonal_movement_cost_multiplier
Multiplier applied to the cost of diagonal movement.
- edge_collision
Whether edge collision checks are enabled.
- find_components()
Find connected components.
- Returns:
List of components, each as a list of nodes.
- Return type:
List[List[node]]
- get_neighbors(node, include_self=False) list
Get neighboring nodes of a given node, along with the cost of moving to each.
Each neighbor is returned as a tuple of (neighbor_node, cost). For example: [(n1, 1.0), (n2, 2.5)]
- Parameters:
node – The target node.
include_self (bool, default False) – Whether to include the node itself if there is an self loop.
- Returns:
List of neighbors with movement costs.
- Return type:
List[Tuple[node, float]]
- Raises:
ValueError – If the node is not a valid node in the environment.
- get_pause_weight(node) double
Get the pause cost at a specific node.
- Parameters:
node (tuple of int) – The coordinate to query.
- Returns:
The cost of remaining at the specified node.
- Return type:
float
- Raises:
ValueError – If the node is outside the bounds of the grid.
- get_weight(node) double
Get the movement cost to enter a node in the grid.
- Parameters:
node (tuple of int) – The coordinate to query.
- Returns:
The movement cost to enter the specified node.
- Return type:
float
- Raises:
ValueError – If the node is outside the bounds of the grid.
- has_obstacle(node) bool
Check whether the specified node is marked as an obstacle.
- Parameters:
node (tuple of int) – The grid coordinate to check.
- Returns:
True if the node is an obstacle; False otherwise.
- Return type:
bool
- Raises:
ValueError – If the node is outside the bounds of the grid.
- height
Number of rows in the grid.
- is_valid_path(path) bool
Check if a given path is valid within the environment.
- Parameters:
path (iterable of nodes) – A sequence of nodes representing the path
- Returns:
True if the path is valid, False otherwise.
- Return type:
bool
- Raises:
ValueError – If one of the nodes in the path is not a valid node in the environment.
- obstacle_map
Get a binary map of obstacles in the grid.
- Returns:
A grid-shaped nested structure where each cell contains:
1 if the corresponding node is an obstacle (weight = -1)
0 otherwise
- Return type:
array-like
- passable_left_right_border
Whether the grid wraps horizontally (left/right edges connect).
- passable_up_down_border
Whether the grid wraps vertically (top/bottom edges connect).
- pause_weights
A grid-shaped array representing the cost of pausing (waiting in place) at each node
Notes
This is a read-only snapshot of internal weights; modifying it won’t change the grid.
- remove_obstacle(node)
Remove the obstacle status from the specified node.
- Parameters:
node (tuple of int) – The coordinate to unmark as an obstacle.
- Raises:
ValueError – If the node is outside the bounds of the grid.
- shape
Shape of the grid, (height, width).
- show()
Print a plain visual representation of the grid.
Same as show_path(None) — shows only the obstacle layout.
- show_path(path)
Print a visual representation of the grid with a path overlay.
The grid is printed to the console with symbols indicating path-related features:
s: Start node (first in path)
e: End node (last in path)
x: Intermediate nodes on the path
#: Obstacles (impassable cells)
` ` (space): Free cells
- Parameters:
path (list of tuple[int, int] or None) – A sequence of nodes representing a valid path. If None, only the grid is shown without any path annotations.
- size
Total number of nodes in the environment.
- to_dict()
Convert environment settings to a serializable dictionary.
- Returns:
Dictionary of environment configuration options.
- Return type:
dict
- update_weight(node, new_value)
Update the movement cost (weight) of a specific node in the grid.
- Parameters:
node (tuple of int) – The coordinate whose weight should be updated.
new_value (float) – The new cost value for moving into this node.
- Raises:
ValueError – If the node is outside the bounds of the grid
ValueError – If new_value is negative and not equal to -1.
- weights
A grid-shaped array representing movement cost to enter each node.
Notes
This is a read-only snapshot of internal weights; modifying it won’t change the grid.
- width
Number of columns in the grid.
- class w9_pathfinding.envs.DiagonalMovement(value)
Bases:
IntEnumEnum defining when diagonal movement is allowed in grid-based pathfinding.
Modes:
- never (0):
Disallow diagonal movement entirely
- only_when_no_obstacle (1):
Allow diagonal movement only if both adjacent cardinal cells are free
- if_at_most_one_obstacle (2):
Allow diagonal movement if at least one of the adjacent cardinal cells is free
- always (3):
Allow diagonal movement unconditionally, regardless of obstacles in cardinal directions
- never = DiagonalMovement.never
Disallow diagonal movement entirely
- only_when_no_obstacle = DiagonalMovement.only_when_no_obstacle
Allow diagonal movement only if both adjacent cardinal cells are free
- if_at_most_one_obstacle = DiagonalMovement.if_at_most_one_obstacle
Allow diagonal movement if at least one of the adjacent cardinal cells is free
- always = DiagonalMovement.always
Allow diagonal movement unconditionally, regardless of obstacles in cardinal directions