Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_astar.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
36#include "nilorea/n_astar.h"
37#include <string.h>
38#include <math.h>
39
40/*---------------------------------------------------------------------------
41 * Internal helpers
42 *-------------------------------------------------------------------------*/
43
45static inline int grid_index(const ASTAR_GRID* grid, int x, int y, int z) {
46 return (z * grid->height + y) * grid->width + x;
47}
48
50static inline int in_bounds(const ASTAR_GRID* grid, int x, int y, int z) {
51 return x >= 0 && x < grid->width &&
52 y >= 0 && y < grid->height &&
53 z >= 0 && z < grid->depth;
54}
55
57static inline int iabs(int v) {
58 return v < 0 ? -v : v;
59}
60
61/*---------------------------------------------------------------------------
62 * Binary Min-Heap (priority queue)
63 *-------------------------------------------------------------------------*/
64
65static ASTAR_HEAP* heap_new(int capacity) {
66 ASTAR_HEAP* h = (ASTAR_HEAP*)calloc(1, sizeof(ASTAR_HEAP));
67 if (!h) return NULL;
68
69 h->data = (ASTAR_HEAP_NODE*)malloc((size_t)capacity * sizeof(ASTAR_HEAP_NODE));
70 if (!h->data) {
71 free(h);
72 return NULL;
73 }
74 h->size = 0;
75 h->capacity = capacity;
76 return h;
77}
78
79static void heap_free(ASTAR_HEAP* h) {
80 if (!h) return;
81 free(h->data);
82 free(h);
83}
84
85static int heap_grow(ASTAR_HEAP* h) {
86 int new_cap = h->capacity * 2;
87 ASTAR_HEAP_NODE* new_data = (ASTAR_HEAP_NODE*)realloc(
88 h->data, (size_t)new_cap * sizeof(ASTAR_HEAP_NODE));
89 if (!new_data) return 0;
90 h->data = new_data;
91 h->capacity = new_cap;
92 return 1;
93}
94
96 ASTAR_HEAP_NODE tmp = *a;
97 *a = *b;
98 *b = tmp;
99}
100
101static void heap_push(ASTAR_HEAP* h, int x, int y, int z, int f) {
102 if (h->size >= h->capacity) {
103 if (!heap_grow(h)) return;
104 }
105
106 int i = h->size++;
107 h->data[i].x = x;
108 h->data[i].y = y;
109 h->data[i].z = z;
110 h->data[i].f = f;
111
112 /* Sift up */
113 while (i > 0) {
114 int parent = (i - 1) / 2;
115 if (h->data[i].f < h->data[parent].f) {
116 heap_swap(&h->data[i], &h->data[parent]);
117 i = parent;
118 } else {
119 break;
120 }
121 }
122}
123
125 ASTAR_HEAP_NODE top = h->data[0];
126 h->data[0] = h->data[--h->size];
127
128 /* Sift down */
129 int i = 0;
130 for (;;) {
131 int left = 2 * i + 1;
132 int right = 2 * i + 2;
133 int smallest = i;
134
135 if (left < h->size && h->data[left].f < h->data[smallest].f)
136 smallest = left;
137 if (right < h->size && h->data[right].f < h->data[smallest].f)
138 smallest = right;
139
140 if (smallest != i) {
141 heap_swap(&h->data[i], &h->data[smallest]);
142 i = smallest;
143 } else {
144 break;
145 }
146 }
147
148 return top;
149}
150
151/*---------------------------------------------------------------------------
152 * Heuristic Functions
153 *-------------------------------------------------------------------------*/
154
166int n_astar_heuristic(int x1, int y1, int z1, int x2, int y2, int z2, ASTAR_HEURISTIC heuristic) {
167 int dx = iabs(x2 - x1);
168 int dy = iabs(y2 - y1);
169 int dz = iabs(z2 - z1);
170
171 switch (heuristic) {
173 return (dx + dy + dz) * ASTAR_COST_CARDINAL;
174
176 double dist = sqrt((double)(dx * dx + dy * dy + dz * dz));
177 return (int)(dist * ASTAR_COST_CARDINAL);
178 }
179
181 default: {
182 int vals[3] = {dx, dy, dz};
183 /* Sort ascending */
184 if (vals[0] > vals[1]) {
185 int t = vals[0];
186 vals[0] = vals[1];
187 vals[1] = t;
188 }
189 if (vals[1] > vals[2]) {
190 int t = vals[1];
191 vals[1] = vals[2];
192 vals[2] = t;
193 }
194 if (vals[0] > vals[1]) {
195 int t = vals[0];
196 vals[0] = vals[1];
197 vals[1] = t;
198 }
199
200 int cost = vals[2] * ASTAR_COST_CARDINAL;
201 cost += vals[0] * (ASTAR_COST_DIAGONAL3D - ASTAR_COST_CARDINAL);
202 cost += (vals[1] - vals[0]) * (ASTAR_COST_DIAGONAL - ASTAR_COST_CARDINAL);
203 return cost;
204 }
205 }
206}
207
208/*---------------------------------------------------------------------------
209 * Grid Management
210 *-------------------------------------------------------------------------*/
211
219ASTAR_GRID* n_astar_grid_new(int width, int height, int depth) {
220 if (width <= 0 || height <= 0 || depth <= 0) return NULL;
221
222 ASTAR_GRID* grid = (ASTAR_GRID*)calloc(1, sizeof(ASTAR_GRID));
223 if (!grid) return NULL;
224
225 grid->width = width;
226 grid->height = height;
227 grid->depth = depth;
228
229 size_t total = (size_t)width * (size_t)height * (size_t)depth;
230
231 grid->walkable = (uint8_t*)malloc(total);
232 if (!grid->walkable) {
233 free(grid);
234 return NULL;
235 }
236 memset(grid->walkable, 1, total);
237
238 grid->cost = (int*)malloc(total * sizeof(int));
239 if (!grid->cost) {
240 free(grid->walkable);
241 free(grid);
242 return NULL;
243 }
244 for (size_t i = 0; i < total; i++) {
245 grid->cost[i] = ASTAR_COST_CARDINAL;
246 }
247
248 return grid;
249}
250
256 if (!grid) return;
257 free(grid->walkable);
258 free(grid->cost);
259 free(grid);
260}
261
270void n_astar_grid_set_walkable(ASTAR_GRID* grid, int x, int y, int z, uint8_t walkable) {
271 if (!grid || !in_bounds(grid, x, y, z)) return;
272 grid->walkable[grid_index(grid, x, y, z)] = walkable;
273}
274
283uint8_t n_astar_grid_get_walkable(const ASTAR_GRID* grid, int x, int y, int z) {
284 if (!grid || !in_bounds(grid, x, y, z)) return 0;
285 return grid->walkable[grid_index(grid, x, y, z)];
286}
287
296void n_astar_grid_set_cost(ASTAR_GRID* grid, int x, int y, int z, int cost) {
297 if (!grid || !in_bounds(grid, x, y, z)) return;
298 grid->cost[grid_index(grid, x, y, z)] = cost;
299}
300
309int n_astar_grid_get_cost(const ASTAR_GRID* grid, int x, int y, int z) {
310 if (!grid || !in_bounds(grid, x, y, z)) return 0;
311 return grid->cost[grid_index(grid, x, y, z)];
312}
313
325 int x1,
326 int y1,
327 int z1,
328 int x2,
329 int y2,
330 int z2) {
331 if (!grid) return;
332 if (x1 > x2) {
333 int t = x1;
334 x1 = x2;
335 x2 = t;
336 }
337 if (y1 > y2) {
338 int t = y1;
339 y1 = y2;
340 y2 = t;
341 }
342 if (z1 > z2) {
343 int t = z1;
344 z1 = z2;
345 z2 = t;
346 }
347
348 for (int z = z1; z <= z2; z++)
349 for (int y = y1; y <= y2; y++)
350 for (int x = x1; x <= x2; x++)
351 n_astar_grid_set_walkable(grid, x, y, z, 0);
352}
353
354/*---------------------------------------------------------------------------
355 * Path Reconstruction
356 *-------------------------------------------------------------------------*/
357
359static ASTAR_PATH* reconstruct_path(ASTAR_CELL* cells, const ASTAR_GRID* grid, int sx, int sy, int sz, int gx, int gy, int gz) {
360 /* Count path length */
361 int length = 0;
362 int cx = gx, cy = gy, cz = gz;
363 while (cx != sx || cy != sy || cz != sz) {
364 length++;
365 int idx = grid_index(grid, cx, cy, cz);
366 int px = cells[idx].parent_x;
367 int py = cells[idx].parent_y;
368 int pz = cells[idx].parent_z;
369 cx = px;
370 cy = py;
371 cz = pz;
372 if (length > grid->width * grid->height * grid->depth) {
373 return NULL;
374 }
375 }
376 length++;
377
378 ASTAR_PATH* path = (ASTAR_PATH*)calloc(1, sizeof(ASTAR_PATH));
379 if (!path) return NULL;
380
381 path->nodes = (ASTAR_NODE*)malloc((size_t)length * sizeof(ASTAR_NODE));
382 if (!path->nodes) {
383 free(path);
384 return NULL;
385 }
386 path->length = length;
387 path->cost = cells[grid_index(grid, gx, gy, gz)].g;
388
389 int i = length - 1;
390 cx = gx;
391 cy = gy;
392 cz = gz;
393 while (i >= 0) {
394 path->nodes[i].x = cx;
395 path->nodes[i].y = cy;
396 path->nodes[i].z = cz;
397 if (cx == sx && cy == sy && cz == sz) break;
398 int idx = grid_index(grid, cx, cy, cz);
399 int px = cells[idx].parent_x;
400 int py = cells[idx].parent_y;
401 int pz = cells[idx].parent_z;
402 cx = px;
403 cy = py;
404 cz = pz;
405 i--;
406 }
407
408 return path;
409}
410
411/*---------------------------------------------------------------------------
412 * 2D Neighbor Directions
413 *-------------------------------------------------------------------------*/
414
415static const int dir2d_cardinal[][2] = {
416 {0, -1},
417 {0, 1},
418 {1, 0},
419 {-1, 0}};
420static const int dir2d_diagonal[][2] = {
421 {1, -1},
422 {-1, -1},
423 {1, 1},
424 {-1, 1}};
425
426/*---------------------------------------------------------------------------
427 * 3D Neighbor Directions
428 *-------------------------------------------------------------------------*/
429
430static const int dir3d_cardinal[][3] = {
431 {1, 0, 0},
432 {-1, 0, 0},
433 {0, 1, 0},
434 {0, -1, 0},
435 {0, 0, 1},
436 {0, 0, -1}};
437static const int dir3d_diagonal[][3] = {
438 {1, 1, 0},
439 {1, -1, 0},
440 {-1, 1, 0},
441 {-1, -1, 0},
442 {1, 0, 1},
443 {1, 0, -1},
444 {-1, 0, 1},
445 {-1, 0, -1},
446 {0, 1, 1},
447 {0, 1, -1},
448 {0, -1, 1},
449 {0, -1, -1},
450 {1, 1, 1},
451 {1, 1, -1},
452 {1, -1, 1},
453 {1, -1, -1},
454 {-1, 1, 1},
455 {-1, 1, -1},
456 {-1, -1, 1},
457 {-1, -1, -1}};
458
459/*---------------------------------------------------------------------------
460 * A* Core Algorithm
461 *-------------------------------------------------------------------------*/
462
478 int sx,
479 int sy,
480 int sz,
481 int gx,
482 int gy,
483 int gz,
484 int diagonal,
485 ASTAR_HEURISTIC heuristic) {
486 if (!grid) return NULL;
487 if (!in_bounds(grid, sx, sy, sz) || !in_bounds(grid, gx, gy, gz)) return NULL;
488 if (!grid->walkable[grid_index(grid, sx, sy, sz)]) return NULL;
489 if (!grid->walkable[grid_index(grid, gx, gy, gz)]) return NULL;
490
491 /* Trivial case */
492 if (sx == gx && sy == gy && sz == gz) {
493 ASTAR_PATH* path = (ASTAR_PATH*)calloc(1, sizeof(ASTAR_PATH));
494 if (!path) return NULL;
495 path->nodes = (ASTAR_NODE*)malloc(sizeof(ASTAR_NODE));
496 if (!path->nodes) {
497 free(path);
498 return NULL;
499 }
500 path->nodes[0].x = sx;
501 path->nodes[0].y = sy;
502 path->nodes[0].z = sz;
503 path->length = 1;
504 path->cost = 0;
505 return path;
506 }
507
508 int total = grid->width * grid->height * grid->depth;
509 int is_3d = (grid->depth > 1);
510
511 ASTAR_CELL* cells = (ASTAR_CELL*)calloc((size_t)total, sizeof(ASTAR_CELL));
512 if (!cells) return NULL;
513
514 for (int i = 0; i < total; i++) {
515 cells[i].parent_x = -1;
516 cells[i].parent_y = -1;
517 cells[i].parent_z = -1;
518 cells[i].status = ASTAR_NODE_NONE;
519 }
520
521 ASTAR_HEAP* open = heap_new(256);
522 if (!open) {
523 free(cells);
524 return NULL;
525 }
526
527 int si = grid_index(grid, sx, sy, sz);
528 cells[si].g = 0;
529 cells[si].h = n_astar_heuristic(sx, sy, sz, gx, gy, gz, heuristic);
530 cells[si].f = cells[si].h;
531 cells[si].status = ASTAR_NODE_OPEN;
532 heap_push(open, sx, sy, sz, cells[si].f);
533
534 ASTAR_PATH* result = NULL;
535
536 while (open->size > 0) {
537 ASTAR_HEAP_NODE current = heap_pop(open);
538 int cx = current.x, cy = current.y, cz = current.z;
539 int ci = grid_index(grid, cx, cy, cz);
540
541 if (cells[ci].status == ASTAR_NODE_CLOSED)
542 continue;
543
544 if (cx == gx && cy == gy && cz == gz) {
545 result = reconstruct_path(cells, grid, sx, sy, sz, gx, gy, gz);
546 break;
547 }
548
549 cells[ci].status = ASTAR_NODE_CLOSED;
550
551 if (is_3d) {
552 /* 3D: 6 cardinal directions */
553 for (int d = 0; d < 6; d++) {
554 int nx = cx + dir3d_cardinal[d][0];
555 int ny = cy + dir3d_cardinal[d][1];
556 int nz = cz + dir3d_cardinal[d][2];
557 if (!in_bounds(grid, nx, ny, nz)) continue;
558
559 int ni = grid_index(grid, nx, ny, nz);
560 if (!grid->walkable[ni] || cells[ni].status == ASTAR_NODE_CLOSED)
561 continue;
562
563 int move_cost = (grid->cost[ci] + grid->cost[ni]) / 2;
564 int tentative_g = cells[ci].g + move_cost;
565
566 if (cells[ni].status == ASTAR_NODE_NONE || tentative_g < cells[ni].g) {
567 cells[ni].g = tentative_g;
568 cells[ni].h = n_astar_heuristic(nx, ny, nz, gx, gy, gz, heuristic);
569 cells[ni].f = tentative_g + cells[ni].h;
570 cells[ni].parent_x = cx;
571 cells[ni].parent_y = cy;
572 cells[ni].parent_z = cz;
573 cells[ni].status = ASTAR_NODE_OPEN;
574 heap_push(open, nx, ny, nz, cells[ni].f);
575 }
576 }
577
578 /* 3D: 20 diagonal directions */
579 if (diagonal) {
580 for (int d = 0; d < 20; d++) {
581 int ddx = dir3d_diagonal[d][0];
582 int ddy = dir3d_diagonal[d][1];
583 int ddz = dir3d_diagonal[d][2];
584 int nx = cx + ddx;
585 int ny = cy + ddy;
586 int nz = cz + ddz;
587 if (!in_bounds(grid, nx, ny, nz)) continue;
588
589 int ni = grid_index(grid, nx, ny, nz);
590 if (!grid->walkable[ni] || cells[ni].status == ASTAR_NODE_CLOSED)
591 continue;
592
593 /* Corner-cutting check */
594 int blocked = 0;
595 if (ddx != 0 && ddy != 0 && !grid->walkable[grid_index(grid, cx + ddx, cy, cz)])
596 blocked = 1;
597 if (ddx != 0 && ddy != 0 && !grid->walkable[grid_index(grid, cx, cy + ddy, cz)])
598 blocked = 1;
599 if (ddx != 0 && ddz != 0 && !grid->walkable[grid_index(grid, cx + ddx, cy, cz)])
600 blocked = 1;
601 if (ddx != 0 && ddz != 0 && !grid->walkable[grid_index(grid, cx, cy, cz + ddz)])
602 blocked = 1;
603 if (ddy != 0 && ddz != 0 && !grid->walkable[grid_index(grid, cx, cy + ddy, cz)])
604 blocked = 1;
605 if (ddy != 0 && ddz != 0 && !grid->walkable[grid_index(grid, cx, cy, cz + ddz)])
606 blocked = 1;
607 if (blocked) continue;
608
609 int axes = (ddx != 0) + (ddy != 0) + (ddz != 0);
610 int base_cost;
611 if (axes == 3)
612 base_cost = ASTAR_COST_DIAGONAL3D;
613 else
614 base_cost = ASTAR_COST_DIAGONAL;
615
616 int cell_cost = (grid->cost[ci] + grid->cost[ni]) / 2;
617 int move_cost = (base_cost * cell_cost) / ASTAR_COST_CARDINAL;
618 int tentative_g = cells[ci].g + move_cost;
619
620 if (cells[ni].status == ASTAR_NODE_NONE || tentative_g < cells[ni].g) {
621 cells[ni].g = tentative_g;
622 cells[ni].h = n_astar_heuristic(nx, ny, nz, gx, gy, gz, heuristic);
623 cells[ni].f = tentative_g + cells[ni].h;
624 cells[ni].parent_x = cx;
625 cells[ni].parent_y = cy;
626 cells[ni].parent_z = cz;
627 cells[ni].status = ASTAR_NODE_OPEN;
628 heap_push(open, nx, ny, nz, cells[ni].f);
629 }
630 }
631 }
632 } else {
633 /* 2D: 4 cardinal directions */
634 for (int d = 0; d < 4; d++) {
635 int nx = cx + dir2d_cardinal[d][0];
636 int ny = cy + dir2d_cardinal[d][1];
637 if (!in_bounds(grid, nx, ny, 0)) continue;
638
639 int ni = grid_index(grid, nx, ny, 0);
640 if (!grid->walkable[ni] || cells[ni].status == ASTAR_NODE_CLOSED)
641 continue;
642
643 int move_cost = (grid->cost[ci] + grid->cost[ni]) / 2;
644 int tentative_g = cells[ci].g + move_cost;
645
646 if (cells[ni].status == ASTAR_NODE_NONE || tentative_g < cells[ni].g) {
647 cells[ni].g = tentative_g;
648 cells[ni].h = n_astar_heuristic(nx, ny, 0, gx, gy, 0, heuristic);
649 cells[ni].f = tentative_g + cells[ni].h;
650 cells[ni].parent_x = cx;
651 cells[ni].parent_y = cy;
652 cells[ni].parent_z = 0;
653 cells[ni].status = ASTAR_NODE_OPEN;
654 heap_push(open, nx, ny, 0, cells[ni].f);
655 }
656 }
657
658 /* 2D: 4 diagonal directions */
659 if (diagonal) {
660 for (int d = 0; d < 4; d++) {
661 int ddx = dir2d_diagonal[d][0];
662 int ddy = dir2d_diagonal[d][1];
663 int nx = cx + ddx;
664 int ny = cy + ddy;
665 if (!in_bounds(grid, nx, ny, 0)) continue;
666
667 int ni = grid_index(grid, nx, ny, 0);
668 if (!grid->walkable[ni] || cells[ni].status == ASTAR_NODE_CLOSED)
669 continue;
670
671 if (!grid->walkable[grid_index(grid, cx + ddx, cy, 0)] ||
672 !grid->walkable[grid_index(grid, cx, cy + ddy, 0)])
673 continue;
674
675 int cell_cost = (grid->cost[ci] + grid->cost[ni]) / 2;
676 int move_cost = (ASTAR_COST_DIAGONAL * cell_cost) / ASTAR_COST_CARDINAL;
677 int tentative_g = cells[ci].g + move_cost;
678
679 if (cells[ni].status == ASTAR_NODE_NONE || tentative_g < cells[ni].g) {
680 cells[ni].g = tentative_g;
681 cells[ni].h = n_astar_heuristic(nx, ny, 0, gx, gy, 0, heuristic);
682 cells[ni].f = tentative_g + cells[ni].h;
683 cells[ni].parent_x = cx;
684 cells[ni].parent_y = cy;
685 cells[ni].parent_z = 0;
686 cells[ni].status = ASTAR_NODE_OPEN;
687 heap_push(open, nx, ny, 0, cells[ni].f);
688 }
689 }
690 }
691 }
692 }
693
694 heap_free(open);
695 free(cells);
696 return result;
697}
698
699/*---------------------------------------------------------------------------
700 * Path Cleanup
701 *-------------------------------------------------------------------------*/
702
708 if (!path) return;
709 free(path->nodes);
710 free(path);
711}
int f
priority (f = g + h)
Definition n_astar.h:136
int capacity
allocated capacity
Definition n_astar.h:143
int parent_z
parent cell Z
Definition n_astar.h:129
int parent_x
parent cell X (-1 if none)
Definition n_astar.h:127
int x
grid X coordinate
Definition n_astar.h:110
int depth
grid depth (Z axis, 1 for 2D)
Definition n_astar.h:150
int cost
total path cost (x1000 fixed-point)
Definition n_astar.h:119
uint8_t status
ASTAR_NODE_NONE / OPEN / CLOSED.
Definition n_astar.h:130
int width
grid width (X axis)
Definition n_astar.h:148
int f
g + h
Definition n_astar.h:126
ASTAR_NODE * nodes
array of path nodes from start to goal
Definition n_astar.h:117
int y
grid Y coordinate
Definition n_astar.h:111
int height
grid height (Y axis)
Definition n_astar.h:149
int g
cost from start to this cell
Definition n_astar.h:124
int z
grid coordinates
Definition n_astar.h:135
uint8_t * walkable
walkability map: 1=passable, 0=blocked
Definition n_astar.h:151
int * cost
per-cell movement cost multiplier (x1000)
Definition n_astar.h:152
ASTAR_HEAP_NODE * data
heap array
Definition n_astar.h:141
int size
current number of elements
Definition n_astar.h:142
int h
heuristic estimate to goal
Definition n_astar.h:125
int parent_y
parent cell Y
Definition n_astar.h:128
int z
grid Z coordinate (0 for 2D)
Definition n_astar.h:112
int length
number of nodes in the path
Definition n_astar.h:118
#define ASTAR_NODE_OPEN
Node is in the open list.
Definition n_astar.h:89
int n_astar_grid_get_cost(const ASTAR_GRID *grid, int x, int y, int z)
Get a cell's movement cost multiplier.
Definition n_astar.c:309
#define ASTAR_NODE_CLOSED
Node has been fully evaluated.
Definition n_astar.h:91
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.
Definition n_astar.c:477
void n_astar_grid_set_cost(ASTAR_GRID *grid, int x, int y, int z, int cost)
Set a cell's movement cost multiplier.
Definition n_astar.c:296
uint8_t n_astar_grid_get_walkable(const ASTAR_GRID *grid, int x, int y, int z)
Get a cell's walkability.
Definition n_astar.c:283
ASTAR_HEURISTIC
Heuristic function selection for h(n) estimation.
Definition n_astar.h:98
void n_astar_grid_free(ASTAR_GRID *grid)
Free a grid and all its internal data.
Definition n_astar.c:255
#define ASTAR_COST_CARDINAL
Default cost for straight movement (fixed-point x1000)
Definition n_astar.h:80
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.
Definition n_astar.c:166
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)
Definition n_astar.c:324
void n_astar_path_free(ASTAR_PATH *path)
Free a path returned by n_astar_find_path.
Definition n_astar.c:707
#define ASTAR_COST_DIAGONAL3D
Default cost for 3D diagonal movement (sqrt(3)*1000)
Definition n_astar.h:84
#define ASTAR_COST_DIAGONAL
Default cost for 2D diagonal movement (sqrt(2)*1000)
Definition n_astar.h:82
void n_astar_grid_set_walkable(ASTAR_GRID *grid, int x, int y, int z, uint8_t walkable)
Set a cell's walkability.
Definition n_astar.c:270
ASTAR_GRID * n_astar_grid_new(int width, int height, int depth)
Create a new grid for A* pathfinding.
Definition n_astar.c:219
#define ASTAR_NODE_NONE
Node has not been visited.
Definition n_astar.h:87
@ ASTAR_HEURISTIC_EUCLIDEAN
straight-line distance
Definition n_astar.h:100
@ ASTAR_HEURISTIC_CHEBYSHEV
max of axis deltas (optimal for 8-dir)
Definition n_astar.h:101
@ ASTAR_HEURISTIC_MANHATTAN
sum of axis deltas (optimal for 4-dir)
Definition n_astar.h:99
Internal node data used during pathfinding.
Definition n_astar.h:123
Grid structure holding walkability, costs, and dimensions.
Definition n_astar.h:147
Binary min-heap (priority queue) for the open list.
Definition n_astar.h:140
Min-heap entry for the open list priority queue.
Definition n_astar.h:134
A single node in the resulting path.
Definition n_astar.h:109
The computed path result.
Definition n_astar.h:116
static void heap_swap(ASTAR_HEAP_NODE *a, ASTAR_HEAP_NODE *b)
Definition n_astar.c:95
static int heap_grow(ASTAR_HEAP *h)
Definition n_astar.c:85
static void heap_free(ASTAR_HEAP *h)
Definition n_astar.c:79
static int grid_index(const ASTAR_GRID *grid, int x, int y, int z)
Convert 3D coordinates to flat array index.
Definition n_astar.c:45
static const int dir2d_diagonal[][2]
Definition n_astar.c:420
static int iabs(int v)
Absolute value for integers.
Definition n_astar.c:57
static const int dir3d_cardinal[][3]
Definition n_astar.c:430
static const int dir3d_diagonal[][3]
Definition n_astar.c:437
static ASTAR_HEAP * heap_new(int capacity)
Definition n_astar.c:65
static void heap_push(ASTAR_HEAP *h, int x, int y, int z, int f)
Definition n_astar.c:101
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.
Definition n_astar.c:359
static ASTAR_HEAP_NODE heap_pop(ASTAR_HEAP *h)
Definition n_astar.c:124
static const int dir2d_cardinal[][2]
Definition n_astar.c:415
static int in_bounds(const ASTAR_GRID *grid, int x, int y, int z)
Check if coordinates are within grid bounds.
Definition n_astar.c:50
A* Pathfinding API for 2D and 3D grids.