#include #include #include #include #include #include "hexagon.h" /* * 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(struct HL_hex a, struct HL_hex b) { return 1.0; } static double default_heuristic(struct HL_hex a, struct HL_hex 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->open = 0; state->closed = 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_adjhexp; return state; } /* * Reset a state to enpty. */ struct HL_astar *HL_astar_clear(struct HL_astar *s) { struct HL_astar_hex *hex; if (!s) return NULL; while (s->open) { hex = s->open->next; free(s->open); s->open = hex; } while (s->closed) { hex = s->closed->next; free(s->closed); s->closed = hex; } s->open = 0; s->closed = 0; s->nodes = 0; s->pathlen = 0; return s; } void HL_astar_free(struct HL_astar *s) { struct HL_astar_hex *hex; /* free open and closed lists */ while (s->open) { hex = s->open->next; free(s->open); s->open = hex; } while (s->closed) { hex = s->closed->next; free(s->closed); s->closed = hex; } if (s->malloced) { free(s); } } static struct HL_astar_hex *add_open(struct HL_astar *s, struct HL_hex hex) { struct HL_astar_hex *node; #ifdef DEBUG_ASTAR fprintf(stderr, "add to open %d\n", HL_cantor(hex)); #endif if (!s || s->error) { #ifdef DEBUG_ASTAR fprintf(stderr, "returning null\n"); #endif return NULL; } #ifdef DEBUG_ASTAR fprintf(stderr, "mallocing node(%zd)\n", sizeof *node); #endif /* TODO check in open or closed */ node = malloc(sizeof *node); #ifdef DEBUG_ASTAR fprintf(stderr, "add to open %d = %p\n", HL_cantor(hex), node); #endif 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; } 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; } #ifdef DEBUG_ASTAR fprintf(stderr, "Closing hex: (%d,%d)\n", h->hex.x, h->hex.y); #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; return h; } struct HL_astar_hex *in_closed(struct HL_astar *s, struct HL_hex hex) { struct HL_astar_hex *h; if (!s) return 0; for (h = s->closed; h; h = h->next) { if (h->hex.x == hex.x && h->hex.y == hex.y) return h; } return NULL; } struct HL_astar_hex *in_open(struct HL_astar *s, struct HL_hex hex) { struct HL_astar_hex *h; if (!s) return 0; for (h = s->open; h; h = h->next) { if (h->hex.x == hex.x && h->hex.y == hex.y) return h; } return NULL; } 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; } #ifdef DEBUG_ASTAR void dump_list(char *name, struct HL_astar_hex *x) { fprintf(stderr, "%s list: ", name); for(; x; x = x->next) { fprintf(stderr, "(%d,%d)", x->hex.x, x->hex.y); } fprintf(stderr, "\n"); } #endif /* * find a path from start to end. we use A* */ int HL_findpath(struct HL_astar *s, int loops) { int dir; struct HL_hex y; struct HL_astar_hex *node, *x, *yopen; int tent_better; double cost; s->error = 0; if (HL_distance(s->start, s->goal) == 0) 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)", s->start.x, s->start.y); fprintf(stderr, " -> "); fprintf(stderr, "(%d,%d)", s->goal.x, s->goal.y); fprintf(stderr, "\n"); #endif while (s->open) { #ifdef DEBUG_ASTAR dump_list("open", s->open); dump_list("closed", s->closed); #endif x = lowrank(s); #ifdef DEBUG_ASTAR fprintf(stderr, "checking lowest f hex = (%d,%d) f = %f, g = %f, h = %f\n", x->hex.x, x->hex.y, x->f, x->g, x->h ); #endif if (HL_distance(x->hex, s->goal) == 0) { break; } closehex(s, x); int n; for (dir = 0; n = s->neighbor(x->hex, dir, &y),dir<6; dir++) { if (n == 0) continue; /* no hex in that direction */ if (in_closed(s, y)) { continue; } #ifdef DEBUG_ASTAR fprintf(stderr, "checking dir %d, hex (%d, %d)\n", dir, y.x, y.y); #endif cost = x->g + s->metric(x->hex, y); yopen = in_open(s, y); #ifdef DEBUG_ASTAR fprintf(stderr, "checking inopen = %p\n", yopen); #endif if (!yopen) { yopen = add_open(s, y); tent_better = 1; } else if (cost < yopen->g) { tent_better = 1; } else { 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", yopen->hex.x, yopen->hex.y, yopen->f, yopen->g, yopen->h ); #endif } } } /* error no path ? */ if (HL_distance(x->hex, s->goal) != 0) { 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 (HL_distance(yopen->hex, s->start) == 0) s->from = yopen; #ifdef DEBUG_ASTAR fprintf(stderr, "(%p %d %d,%d)", yopen, yopen->hex, yopen->hex.x, yopen->hex.y ); #endif s->pathlen++; }; s->pathlen--; #ifdef DEBUG_ASTAR fprintf(stderr, "\n"); #endif return s->pathlen; }