50 return x >= 0 && x < grid->
width &&
51 y >= 0 && y < grid->
height &&
52 z >= 0 && z < grid->
depth;
79static inline int iabs(
int v) {
80 return v < 0 ? -v : v;
109 if (!new_data)
return 0;
134 int parent = (i - 1) / 2;
151 int left = 2 * i + 1;
152 int right = 2 * i + 2;
155 if (left < h->size && h->
data[left].
f < h->
data[smallest].
f)
157 if (right < h->size && h->
data[right].
f < h->
data[smallest].
f)
185 int dx =
iabs(x2 - x1);
186 int dy =
iabs(y2 - y1);
187 int dz =
iabs(z2 - z1);
194 double dist = sqrt((
double)(dx * dx + dy * dy + dz * dz));
200 int vals[3] = {dx, dy, dz};
202 if (vals[0] > vals[1]) {
207 if (vals[1] > vals[2]) {
212 if (vals[0] > vals[1]) {
236 if (width <= 0 || height <= 0 || depth <= 0)
return NULL;
239 if (!grid)
return NULL;
245 size_t total = (size_t)width * (
size_t)height * (size_t)depth;
247 grid->
walkable = (uint8_t*)malloc(total);
254 grid->
cost = (
int*)malloc(total *
sizeof(
int));
260 for (
size_t i = 0; i < total; i++) {
287 if (!grid || !
in_bounds(grid, x, y, z))
return;
300 if (!grid || !
in_bounds(grid, x, y, z))
return 0;
313 if (!grid || !
in_bounds(grid, x, y, z))
return;
326 if (!grid || !
in_bounds(grid, x, y, z))
return 0;
364 for (
int z = z1; z <= z2; z++)
365 for (
int y = y1; y <= y2; y++)
366 for (
int x = x1; x <= x2; x++)
376 int cx = gx, cy = gy, cz = gz;
377 while (cx != sx || cy != sy || cz != sz) {
386 if (length > box->
bw * box->
bh * box->
bd) {
393 if (!path)
return NULL;
411 if (cx == sx && cy == sy && cz == sz)
break;
494 if (!grid)
return NULL;
500 if (sx == gx && sy == gy && sz == gz) {
502 if (!path)
return NULL;
516 int is_3d = (grid->
depth > 1);
537 int lo = (sx < gx) ? sx : gx, hi = (sx > gx) ? sx : gx;
541 if (hi > grid->
width - 1) hi = grid->
width - 1;
542 box.
bw = hi - box.
minx + 1;
544 lo = (sy < gy) ? sy : gy;
545 hi = (sy > gy) ? sy : gy;
550 box.
bh = hi - box.
miny + 1;
553 lo = (sz < gz) ? sz : gz;
554 hi = (sz > gz) ? sz : gz;
558 if (hi > grid->
depth - 1) hi = grid->
depth - 1;
559 box.
bd = hi - box.
minz + 1;
566 size_t btotal = (size_t)box.
bw * (
size_t)box.
bh * (size_t)box.
bd;
568 if (!cells)
return NULL;
579 cells[si].
f = cells[si].
h;
581 heap_push(open, sx, sy, sz, cells[si].f);
585 while (open->
size > 0) {
587 int cx = current.
x, cy = current.
y, cz = current.
z;
594 if (cx == gx && cy == gy && cz == gz) {
603 for (
int d = 0; d < 6; d++) {
607 if (!
in_box(&box, nx, ny, nz))
continue;
614 int move_cost = (grid->
cost[ci] + grid->
cost[ni]) / 2;
615 int tentative_g = cells[lci].
g + move_cost;
617 if (cells[lni].status ==
ASTAR_NODE_NONE || tentative_g < cells[lni].g) {
618 cells[lni].
g = tentative_g;
620 cells[lni].
f = tentative_g + cells[lni].
h;
625 heap_push(open, nx, ny, nz, cells[lni].f);
631 for (
int d = 0; d < 20; d++) {
638 if (!
in_box(&box, nx, ny, nz))
continue;
659 if (blocked)
continue;
661 int axes = (ddx != 0) + (ddy != 0) + (ddz != 0);
668 int cell_cost = (grid->
cost[ci] + grid->
cost[ni]) / 2;
670 int tentative_g = cells[lci].
g + move_cost;
672 if (cells[lni].status ==
ASTAR_NODE_NONE || tentative_g < cells[lni].g) {
673 cells[lni].
g = tentative_g;
675 cells[lni].
f = tentative_g + cells[lni].
h;
680 heap_push(open, nx, ny, nz, cells[lni].f);
686 for (
int d = 0; d < 4; d++) {
689 if (!
in_box(&box, nx, ny, 0))
continue;
696 int move_cost = (grid->
cost[ci] + grid->
cost[ni]) / 2;
697 int tentative_g = cells[lci].
g + move_cost;
699 if (cells[lni].status ==
ASTAR_NODE_NONE || tentative_g < cells[lni].g) {
700 cells[lni].
g = tentative_g;
702 cells[lni].
f = tentative_g + cells[lni].
h;
707 heap_push(open, nx, ny, 0, cells[lni].f);
713 for (
int d = 0; d < 4; d++) {
718 if (!
in_box(&box, nx, ny, 0))
continue;
729 int cell_cost = (grid->
cost[ci] + grid->
cost[ni]) / 2;
731 int tentative_g = cells[lci].
g + move_cost;
733 if (cells[lni].status ==
ASTAR_NODE_NONE || tentative_g < cells[lni].g) {
734 cells[lni].
g = tentative_g;
736 cells[lni].
f = tentative_g + cells[lni].
h;
741 heap_push(open, nx, ny, 0, cells[lni].f);
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_SEARCH_MARGIN
Search-window margin (in cells) added around the bounding box of the start and goal.
#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.
int bd
window dimensions in cells
static int box_index(const ASTAR_BOX *b, int x, int y, int z)
Flat index into the window-local ASTAR_CELL array.
static void heap_swap(ASTAR_HEAP_NODE *a, ASTAR_HEAP_NODE *b)
static int in_box(const ASTAR_BOX *b, int x, int y, int z)
Check if coordinates fall inside the search window.
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 ASTAR_PATH * reconstruct_path(ASTAR_CELL *cells, const ASTAR_BOX *box, int sx, int sy, int sz, int gx, int gy, int gz)
Build path from goal back to start following parent pointers.
static void heap_push(ASTAR_HEAP *h, int x, int y, int z, int f)
static ASTAR_HEAP_NODE heap_pop(ASTAR_HEAP *h)
static const int dir2d_cardinal[][2]
int minz
window origin (absolute grid coords)
static int in_bounds(const ASTAR_GRID *grid, int x, int y, int z)
Check if coordinates are within grid bounds.
Search window: the bounding box of (start, goal) expanded by ASTAR_SEARCH_MARGIN and clamped to the g...
A* Pathfinding API for 2D and 3D grids.