DijkstraMap
Dijkstra distance map from a fixed root position.
Overview
A DijkstraMap represents precomputed distances from a root position to the reachable cells of a window of a Grid (by default the whole grid, up to a radius of 511). It is created by Grid.get_dijkstra_map() and cannot be instantiated directly. Dijkstra maps are useful for AI pathfinding, influence maps, and flow-field navigation where multiple entities need to path toward the same goal.
Quick Reference
# Create Dijkstra map from goal position
dijkstra = grid.get_dijkstra_map((goal_x, goal_y))
# Get distance from an entity (or any (x, y) tile position) to the goal
dist = dijkstra.distance(enemy)
# Get full path from position to root (an AStarPath)
path = dijkstra.path_from(enemy)
# Get single step toward goal (for AI)
next_pos = dijkstra.step_from(enemy)
if next_pos:
enemy.grid_pos = next_pos
# Convert to heightmap for visualization (window-sized; see "Windowed maps")
heightmap = dijkstra.to_heightmap()
# Is this answer exact, or the heuristic's estimate? (see "Windowed maps")
if not dijkstra.is_exact(enemy):
...
Constructor
DijkstraMap cannot be instantiated directly. Use Grid.get_dijkstra_map() to create maps.
# Correct way to create a Dijkstra map
dijkstra = grid.get_dijkstra_map((goal_x, goal_y))
Dijkstra maps are cached by the Grid. Calling get_dijkstra_map() with the same root position returns the cached map while the grid is unchanged; after a walkability edit it returns a rebuilt one. A map you hold is a snapshot: it keeps its distances, and stays usable after the grid is gone. The cache is capped at grid.dijkstra_cache_budget bytes, and keyed by root, collide, diagonal_cost, radius and heuristic.
Properties
| Property | Type | Description |
|---|---|---|
root |
Vector | Goal position, read-only |
radius |
int | Radius of the field’s window around the roots, read-only |
window |
tuple | (x, y, w, h): the grid rectangle the field covers, read-only |
complete |
bool | True if the field holds the roots’ whole reachable region, read-only |
heuristic |
Heuristic or None | The estimate used beyond the field, read-only |
Methods
| Method | Description |
|---|---|
distance(pos) |
Get distance from pos (Vector, Entity, or (x, y) tuple) to root, or None if unreachable. Beyond the field, or off the grid, the heuristic’s estimate |
is_exact(pos) |
True if distance(pos) is exact (or certainly unreachable), False if it is an estimate |
path_from(pos) |
Get complete path from pos to root as an AStarPath, ordered from pos toward the root (origin is pos, destination is the root); None beyond the field |
step_from(pos) |
Get next step toward root as a Vector (always a cell adjacent to pos), or None if at root/unreachable. Beyond the field, a greedy step toward it |
descent_step(pos) |
Get the adjacent cell with the lowest distance (steepest single-hop descent) |
invert() |
Return a new DijkstraMap with distances inverted (a “flee” field), root unchanged |
to_heightmap(size=None, unreachable=-1.0) |
Convert distances to a HeightMap for visualization. Window-sized: cell (0, 0) is grid cell window[:2] |
Windowed maps
get_dijkstra_map(root, radius=None, heuristic=mcrfpy.Heuristic.OCTILE) computes the field over a window: the roots’ bounding box grown by radius, clipped to the grid (so it is off-centre near an edge). The default radius is the whole grid up to 511, a field of at most 1023 x 1023 cells at 4 bytes each; on a grid up to 1023 on a side the window is the whole grid, as it always was.
- Distances up to the radius are exact (
radius * diagonal_costifdiagonal_costis below 1). If the flood stayed inside the window,completeisTrue: every distance is exact and every other cell is certainly unreachable (None), off the grid included. - Otherwise, beyond the radius and off the grid,
distance()returns the heuristic’s estimate (a lower bound, never below the radius) andis_exact()isFalse. Build the map withheuristic=Noneto make those queries raiseIndexErrorinstead. step_from()/descent_step(), and SEEK/FLEE behaviors, take a greedy step toward the field (away, on an inverted map); this needs the grid to still exist.path_from()returnsNonethere.- Place
to_heightmap()on a grid-sizedHeightMapwithfull.copy_from(dijkstra.to_heightmap(), pos=dijkstra.window[:2]).
# A 129 x 129 field around the player on a 4000 x 4000 grid
dm = grid.get_dijkstra_map(player.cell_pos, radius=64)
print(dm.window) # (1936, 1936, 129, 129) for a player at (2000, 2000)
dm.distance((2010, 2005)) # 12.05, exact
dm.distance((2500, 2500)) # 705.0, the OCTILE estimate
dm.is_exact((2500, 2500)) # False
dm.step_from((2500, 2500)) # Vector(2499, 2499): a greedy step toward the field
On an 8192 x 8192 grid (the largest a grid can be), a default map builds in about 45 ms and holds 4 MB; a whole-grid map took about 6 s and 268 MB.
Usage Patterns
AI Movement Toward Goal
# Create map toward player
player_dijkstra = grid.get_dijkstra_map(player.grid_pos)
# Move all enemies toward player
for enemy in grid.entities:
if enemy != player:
next_step = player_dijkstra.step_from(enemy)
if next_step:
enemy.grid_pos = next_step
Flee Behavior
# To flee, move to neighbor with highest distance
danger_map = grid.get_dijkstra_map(threat.grid_pos)
def flee_step(entity):
best_pos = None
best_dist = danger_map.distance(entity)
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
nx, ny = entity.grid_x + dx, entity.grid_y + dy
if 0 <= nx < grid.grid_size.x and 0 <= ny < grid.grid_size.y and grid.at(nx, ny).walkable:
dist = danger_map.distance((nx, ny))
if dist is not None and (best_dist is None or dist > best_dist):
best_dist = dist
best_pos = (nx, ny)
if best_pos:
entity.grid_pos = best_pos
Distance-Based Decisions
goal_map = grid.get_dijkstra_map(treasure.grid_pos)
for entity in grid.entities:
dist = goal_map.distance(entity)
if dist is None:
print(f"{entity} cannot reach treasure")
elif dist < 5:
print(f"{entity} is very close!")
elif dist < 15:
print(f"{entity} is {dist} steps away")
Visualization with HeightMap
dijkstra = grid.get_dijkstra_map((goal_x, goal_y))
heightmap = dijkstra.to_heightmap()
# Apply to a color layer for visualization
color_layer = mcrfpy.ColorLayer(z_index=1, name="distance_gradient")
grid.add_layer(color_layer)
color_layer.apply_gradient(
heightmap,
(0, 20), # value range: 0 (root) to 20 tiles away
mcrfpy.Color(0, 255, 0), # Close = green
mcrfpy.Color(255, 0, 0) # Far = red
)
Cache Management
# Dijkstra maps are cached automatically
map1 = grid.get_dijkstra_map((10, 10))
map2 = grid.get_dijkstra_map((10, 10)) # Returns cached map
# Changing the grid: the next request is rebuilt
grid.at(5, 5).walkable = False
map3 = grid.get_dijkstra_map((10, 10)) # Recomputed against the new wall