51 return x >= 0 && x < grid->
width &&
52 y >= 0 && y < grid->
height &&
53 z >= 0 && z < grid->
depth;
57static inline int iabs(
int v) {
58 return v < 0 ? -v : v;
89 if (!new_data)
return 0;
114 int parent = (i - 1) / 2;
131 int left = 2 * i + 1;
132 int right = 2 * i + 2;
135 if (left < h->size && h->
data[left].
f < h->
data[smallest].
f)
137 if (right < h->size && h->
data[right].
f < h->
data[smallest].
f)
167 int dx =
iabs(x2 - x1);
168 int dy =
iabs(y2 - y1);
169 int dz =
iabs(z2 - z1);
176 double dist = sqrt((
double)(dx * dx + dy * dy + dz * dz));
182 int vals[3] = {dx, dy, dz};
184 if (vals[0] > vals[1]) {
189 if (vals[1] > vals[2]) {
194 if (vals[0] > vals[1]) {
220 if (width <= 0 || height <= 0 || depth <= 0)
return NULL;
223 if (!grid)
return NULL;
229 size_t total = (size_t)width * (
size_t)height * (size_t)depth;
231 grid->
walkable = (uint8_t*)malloc(total);
238 grid->
cost = (
int*)malloc(total *
sizeof(
int));
244 for (
size_t i = 0; i < total; i++) {
271 if (!grid || !
in_bounds(grid, x, y, z))
return;
284 if (!grid || !
in_bounds(grid, x, y, z))
return 0;
297 if (!grid || !
in_bounds(grid, x, y, z))
return;
310 if (!grid || !
in_bounds(grid, x, y, z))
return 0;
348 for (
int z = z1; z <= z2; z++)
349 for (
int y = y1; y <= y2; y++)
350 for (
int x = x1; x <= x2; x++)
362 int cx = gx, cy = gy, cz = gz;
363 while (cx != sx || cy != sy || cz != sz) {
379 if (!path)
return NULL;
397 if (cx == sx && cy == sy && cz == sz)
break;
486 if (!grid)
return NULL;
492 if (sx == gx && sy == gy && sz == gz) {
494 if (!path)
return NULL;
509 int is_3d = (grid->
depth > 1);
512 if (!cells)
return NULL;
514 for (
int i = 0; i < total; i++) {
530 cells[si].
f = cells[si].
h;
532 heap_push(open, sx, sy, sz, cells[si].f);
536 while (open->
size > 0) {
538 int cx = current.
x, cy = current.
y, cz = current.
z;
544 if (cx == gx && cy == gy && cz == gz) {
553 for (
int d = 0; d < 6; d++) {
557 if (!
in_bounds(grid, nx, ny, nz))
continue;
563 int move_cost = (grid->
cost[ci] + grid->
cost[ni]) / 2;
564 int tentative_g = cells[ci].
g + move_cost;
566 if (cells[ni].status ==
ASTAR_NODE_NONE || tentative_g < cells[ni].g) {
567 cells[ni].
g = tentative_g;
569 cells[ni].
f = tentative_g + cells[ni].
h;
574 heap_push(open, nx, ny, nz, cells[ni].f);
580 for (
int d = 0; d < 20; d++) {
587 if (!
in_bounds(grid, nx, ny, nz))
continue;
607 if (blocked)
continue;
609 int axes = (ddx != 0) + (ddy != 0) + (ddz != 0);
616 int cell_cost = (grid->
cost[ci] + grid->
cost[ni]) / 2;
618 int tentative_g = cells[ci].
g + move_cost;
620 if (cells[ni].status ==
ASTAR_NODE_NONE || tentative_g < cells[ni].g) {
621 cells[ni].
g = tentative_g;
623 cells[ni].
f = tentative_g + cells[ni].
h;
628 heap_push(open, nx, ny, nz, cells[ni].f);
634 for (
int d = 0; d < 4; d++) {
637 if (!
in_bounds(grid, nx, ny, 0))
continue;
643 int move_cost = (grid->
cost[ci] + grid->
cost[ni]) / 2;
644 int tentative_g = cells[ci].
g + move_cost;
646 if (cells[ni].status ==
ASTAR_NODE_NONE || tentative_g < cells[ni].g) {
647 cells[ni].
g = tentative_g;
649 cells[ni].
f = tentative_g + cells[ni].
h;
660 for (
int d = 0; d < 4; d++) {
665 if (!
in_bounds(grid, nx, ny, 0))
continue;
675 int cell_cost = (grid->
cost[ci] + grid->
cost[ni]) / 2;
677 int tentative_g = cells[ci].
g + move_cost;
679 if (cells[ni].status ==
ASTAR_NODE_NONE || tentative_g < cells[ni].g) {
680 cells[ni].
g = tentative_g;
682 cells[ni].
f = tentative_g + cells[ni].
h;
int f
priority (f = g + h)
int capacity
allocated capacity
int parent_z
parent cell Z
int parent_x
parent cell X (-1 if none)
int depth
grid depth (Z axis, 1 for 2D)
int cost
total path cost (x1000 fixed-point)
uint8_t status
ASTAR_NODE_NONE / OPEN / CLOSED.
int width
grid width (X axis)
ASTAR_NODE * nodes
array of path nodes from start to goal
int height
grid height (Y axis)
int g
cost from start to this cell
uint8_t * walkable
walkability map: 1=passable, 0=blocked
int * cost
per-cell movement cost multiplier (x1000)
ASTAR_HEAP_NODE * data
heap array
int size
current number of elements
int h
heuristic estimate to goal
int parent_y
parent cell Y
int z
grid Z coordinate (0 for 2D)
int length
number of nodes in the path
#define ASTAR_NODE_OPEN
Node is in the open list.
int n_astar_grid_get_cost(const ASTAR_GRID *grid, int x, int y, int z)
Get a cell's movement cost multiplier.
#define ASTAR_NODE_CLOSED
Node has been fully evaluated.
ASTAR_PATH * n_astar_find_path(const ASTAR_GRID *grid, int sx, int sy, int sz, int gx, int gy, int gz, int diagonal, ASTAR_HEURISTIC heuristic)
Find a path using A* search.
void n_astar_grid_set_cost(ASTAR_GRID *grid, int x, int y, int z, int cost)
Set a cell's movement cost multiplier.
uint8_t n_astar_grid_get_walkable(const ASTAR_GRID *grid, int x, int y, int z)
Get a cell's walkability.
ASTAR_HEURISTIC
Heuristic function selection for h(n) estimation.
void n_astar_grid_free(ASTAR_GRID *grid)
Free a grid and all its internal data.
#define ASTAR_COST_CARDINAL
Default cost for straight movement (fixed-point x1000)
int n_astar_heuristic(int x1, int y1, int z1, int x2, int y2, int z2, ASTAR_HEURISTIC heuristic)
Compute heuristic distance between two 3D points.
void n_astar_grid_set_rect_blocked(ASTAR_GRID *grid, int x1, int y1, int z1, int x2, int y2, int z2)
Set a rectangular region as blocked (wall)
void n_astar_path_free(ASTAR_PATH *path)
Free a path returned by n_astar_find_path.
#define ASTAR_COST_DIAGONAL3D
Default cost for 3D diagonal movement (sqrt(3)*1000)
#define ASTAR_COST_DIAGONAL
Default cost for 2D diagonal movement (sqrt(2)*1000)
void n_astar_grid_set_walkable(ASTAR_GRID *grid, int x, int y, int z, uint8_t walkable)
Set a cell's walkability.
ASTAR_GRID * n_astar_grid_new(int width, int height, int depth)
Create a new grid for A* pathfinding.
#define ASTAR_NODE_NONE
Node has not been visited.
@ ASTAR_HEURISTIC_EUCLIDEAN
straight-line distance
@ ASTAR_HEURISTIC_CHEBYSHEV
max of axis deltas (optimal for 8-dir)
@ ASTAR_HEURISTIC_MANHATTAN
sum of axis deltas (optimal for 4-dir)
Internal node data used during pathfinding.
Grid structure holding walkability, costs, and dimensions.
Binary min-heap (priority queue) for the open list.
Min-heap entry for the open list priority queue.
A single node in the resulting path.
The computed path result.
static void heap_swap(ASTAR_HEAP_NODE *a, ASTAR_HEAP_NODE *b)
static int heap_grow(ASTAR_HEAP *h)
static void heap_free(ASTAR_HEAP *h)
static int grid_index(const ASTAR_GRID *grid, int x, int y, int z)
Convert 3D coordinates to flat array index.
static const int dir2d_diagonal[][2]
static int iabs(int v)
Absolute value for integers.
static const int dir3d_cardinal[][3]
static const int dir3d_diagonal[][3]
static ASTAR_HEAP * heap_new(int capacity)
static void heap_push(ASTAR_HEAP *h, int x, int y, int z, int f)
static ASTAR_PATH * reconstruct_path(ASTAR_CELL *cells, const ASTAR_GRID *grid, int sx, int sy, int sz, int gx, int gy, int gz)
Build path from goal back to start following parent pointers.
static ASTAR_HEAP_NODE heap_pop(ASTAR_HEAP *h)
static const int dir2d_cardinal[][2]
static int in_bounds(const ASTAR_GRID *grid, int x, int y, int z)
Check if coordinates are within grid bounds.
A* Pathfinding API for 2D and 3D grids.