X-Git-Url: https://pd.if.org/git/?p=hexagon;a=blobdiff_plain;f=astar.c;h=cabe5d08c4c36dec8d0bf86490fe5e2424e4a2a2;hp=37bc404ac39a2486496df0ac1ad7d91b03390d23;hb=5c48a06bc8649958e69013c53e925b83398ebc74;hpb=67fdcec57ab31e7a972ed624ab5f6df866313206 diff --git a/astar.c b/astar.c index 37bc404..cabe5d0 100644 --- a/astar.c +++ b/astar.c @@ -4,158 +4,292 @@ #include #include #include +#include -struct astar_hex { - int hex; - int f, g, h; - int parent; -}; - -struct astar { - int open, closed; /* how many in open or closed */ - int closedroom, openroom; /* how much is malloced */ - struct astar_hex *openlist; - struct astar_hex *closedlist; - int known, index; - int error; -}; - -static void astar_init(struct astar *state) { +/* + * TODO use binary heap for the openlist and closed list. + * open list should be a min-heap on the f value + * closed list is just tested for existence, so a sort on + * the hex id is fine + */ + +#if 0 +#define DEBUG_ASTAR 1 +#endif + +static double default_metric(int a, int b) { + return 1.0; +} + +static double default_heuristic(int a, int b) { + return (double)HL_distance(a, b); +} + +struct HL_astar *HL_astar_init(struct HL_astar *state) { + if (!state) { + state = malloc(sizeof *state); + if (!state) return NULL; + state->malloced = 1; + } + state->sets = 0; state->open = 0; state->closed = 0; - state->closedroom = 0; - state->openroom = 0; - state->openlist = 0; - state->closedlist = 0; + state->nodes = 0; + state->allocated = 0; state->error = 0; state->known = 0; state->index = 0; + state->pathlen = 0; + + state->metric = default_metric; + state->heuristic = default_heuristic; + state->neighbor = HL_adjacent_hex; - return; + return state; } -static void removeopen(struct astar *s, int hex) { - int index; +/* + * Reset a state to enpty. Doesn't free memory, so it + * can be reused. + */ +struct HL_astar *HL_astar_clear(struct HL_astar *s) { + if (!s) return NULL; - if (s->error) return; - if (s->open == 0) return; + s->open = 0; + s->closed = 0; + s->nodes = 0; + s->pathlen = 0; - if (s->known != hex) { - for (index = 0; index < s->open; index++) { - if (s->openlist[index].hex == hex) break; - } - } else { - index = s->index; - s->known = 0; s->index = 0; + return s; +} + +void HL_astar_free(struct HL_astar *s) { + free(s->sets); + if (s->malloced) { + free(s); } - s->openlist[index] = s->openlist[open]; - s->open--; } -static void addopen(struct astar *s, int hex) { - struct astar_hex *mem; - if (s->error) return; +static struct HL_astar_hex *add_open(struct HL_astar *s, int hex) { + struct HL_astar_hex *node; + + if (!s || s->error) return NULL; + + /* TODO check in open or closed */ - if (s->open + 1 > s->openroom) { - mem = realloc(s->openlist, s->openroom * 2 * sizeof *mem); - if (mem == NULL) { - s->error = errno; - return; + if (s->nodes == s->allocated) { + int newalloc; + struct HL_astar_hex *alloc; + if (s->allocated) { + newalloc = s->allocated * 2; + } else { + newalloc = 16; } - state->openlist = mem; - s->openroom = s->openroom * 2; + alloc = realloc(s->sets, newalloc * sizeof *node); + if (!alloc) { + s->error = 1; + return NULL; + } + s->sets = alloc; + s->allocated = newalloc; } - s->openlist[s->open].hex = hex; - s->openlist[s->open].f = 0; - s->openlist[s->open].g = 0; - s->openlist[s->open].h = 0; - s->known = hex; - s->index = s->open; - s->open = s->open + 1; + node = &s->sets[s->nodes++]; + + node->hex = hex; + node->g = 0.0; + node->f = 0.0; + node->h = 0.0; + node->open = 1; + node->parent = 0; + if (s->open) s->open->prev = node; + node->next = s->open; + node->prev = NULL; + s->open = node; + + return node; } -int lowrank(struct astar *s) { - int f; - int hex; - int i; +static struct HL_astar_hex *closehex(struct HL_astar *s, struct HL_astar_hex *h) { + if (!s || s->error) return NULL; + if (!h) { + s->error = 1; + return NULL; + } - if (!s || s->open = 0) return 0; +#ifdef DEBUG_ASTAR + fprintf(stderr, "Closing hex: (%d,%d)\n", HL_cantor_x(h->hex), HL_cantor_y(h->hex)); +#endif + h->open = 0; + if (h->prev) h->prev->next = h->next; + if (h->next) h->next->prev = h->prev; + if (s->closed) s->closed->prev = h; + if (s->open == h) s->open = h->next; + h->next = s->closed; + s->closed = h; + h->prev = NULL; - hex = s->openlist[0].hex; - f = s->openlist[0].f; + return h; +} - for(i=1; i < s->open; i++) { - if (s->openlist[i].f < f) { - hex = s->openlist[i].hex; - f = s->openlist[i].f; - } - /* TODO check s->openlist[i].tiebreak */ +struct HL_astar_hex *in_closed(struct HL_astar *s, int hex) { + struct HL_astar_hex *h; + if (!s) return 0; + + for(h = s->closed; h; h = h->next) { + if (h->hex == hex) return h; } + return NULL; +} + +struct HL_astar_hex *in_open(struct HL_astar *s, int hex) { + struct HL_astar_hex *h; + if (!s) return 0; - return hex; + for(h = s->open; h; h = h->next) { + if (h->hex == hex) return h; + } + return NULL; } -static int default_metric(int a, int b) { - return 1; +static struct HL_astar_hex *lowrank(struct HL_astar *s) { + struct HL_astar_hex *n, *r; + + if (!s || s->open == 0) return 0; + + r = s->open; + for(n = r; n; n = n->next) { + if (n->f < r->f) { + r = n; + } + } + + return r; } /* * find a path from start to end. we use A* */ -int HL_findpath(int start, int end, int psize, int *path, - double (*metric)(int,int)) { - int neighbors[6]; - int count; - int cur = 0; - struct astar state; - int neighbor; - - if (start == end) return 0; - if (!metric) metric = default_metric; - - astar_init(&state); - addopen(&state, start); - - while (1) { - cur = lowrank(&state); - if (cur == end) { +int HL_findpath(struct HL_astar *s, int loops) { + int dir, y; + struct HL_astar_hex *node, *x, *yopen; + int tent_better; + double cost; + + s->error = 0; + + if (s->start == s->goal) return 0; + + node = add_open(s, s->start); + node->g = 0.0; + node->h = s->heuristic(s->start, s->goal); + node->f = node->h; + +#ifdef DEBUG_ASTAR + fprintf(stderr, "A* findpath: "); + fprintf(stderr, "(%d,%d)", HL_cantor_x(s->start), HL_cantor_y(s->start)); + fprintf(stderr, " -> "); + fprintf(stderr, "(%d,%d)", HL_cantor_x(s->goal), HL_cantor_y(s->goal)); + fprintf(stderr, "\n"); +#endif + + while (s->open) { + +#ifdef DEBUG_ASTAR + fprintf(stderr, "open list: "); + for(x = s->open; x; x = x->next) { + fprintf(stderr, "(%d,%d)", HL_cantor_x(x->hex), HL_cantor_y(x->hex)); + } + fprintf(stderr, "\n"); + fprintf(stderr, "closed list: "); + for(x = s->closed; x; x = x->next) { + fprintf(stderr, "(%d,%d)", HL_cantor_x(x->hex), HL_cantor_y(x->hex)); + } + fprintf(stderr, "\n"); +#endif + + x = lowrank(s); +#ifdef DEBUG_ASTAR + fprintf(stderr, "checking lowest f hex = (%d,%d) f = %f, g = %f, h = %f\n", + HL_cantor_x(x->hex), HL_cantor_y(x->hex), + x->f, x->g, x->h + ); +#endif + if (x->hex == s->goal) { break; } - addclosed(&state, cur); - count = HL_hexes_at_range(cur->hex, 1, neighbors, 6); - for (i=0;ineighbor(x->hex, dir); dir++) { + if (in_closed(s, y)) { + continue; + } + + cost = x->g + s->metric(x->hex, y); + + yopen = in_open(s, y); + if (! yopen) { + yopen = add_open(s, y); + + tent_better = 1; + } else if (cost < yopen->g) { + tent_better = 1; } else { - abort(); + tent_better = 0; + } + + if (tent_better) { + if (yopen->parent) { + if (x->f < yopen->parent->f) { + yopen->parent = x; + } + } else { + yopen->parent = x; + } + yopen->g = cost; + yopen->h = s->heuristic(y, s->goal); + yopen->f = yopen->g + yopen->h; +#ifdef DEBUG_ASTAR + fprintf(stderr, "maybe better hex = (%d,%d) f = %f, g = %f, h = %f\n", + HL_cantor_x(yopen->hex), HL_cantor_y(yopen->hex), + yopen->f, yopen->g, yopen->h + ); +#endif } } } - count = 0; - while (parent(&state, cur) != start) { - count++; - cur = parent(&state, cur); - } - cur = end; - while (parent(&state, cur) != start) { - path[count--] = cur; - cur = parent(&state, cur); + /* error no path ? */ + if (x->hex != s->goal) { + s->error = 1; + return 0; } +#ifdef DEBUG_ASTAR + fprintf(stderr, "found path: "); +#endif + + /* reconstruct path */ + s->to = x; + x->next = 0; + for (s->pathlen = 0, yopen = x; yopen; yopen = yopen->parent) { + if (yopen->parent) { + yopen->parent->next = yopen; + } + if (yopen->hex == s->start) s->from = yopen; +#ifdef DEBUG_ASTAR + fprintf(stderr, "(%p %d %d,%d)", + yopen, yopen->hex, + HL_cantor_x(yopen->hex), + HL_cantor_y(yopen->hex) + ); +#endif + s->pathlen++; + }; + s->pathlen--; + +#ifdef DEBUG_ASTAR + fprintf(stderr, "\n"); +#endif + + return s->pathlen; }