#include #include #include #include #include struct cell_s { int blocked:1; int closed:1; int opened:1; int route:1; int cost; int prevx, prevy; }; struct node_s { int x, y; int totalcost; struct node_s *next; }; typedef struct { int sizex, sizey; int startx, starty; int targetx, targety; struct cell_s *cells; struct node_s *open; } ASTAR_ARRAY; #define CELL(array, x, y) ((array)->cells[(y)*((array)->sizex)+(x)]) ASTAR_ARRAY *astar_create(size_t sizex, size_t sizey) { ASTAR_ARRAY *array; if(!(array=calloc(1, sizeof(ASTAR_ARRAY)))) return NULL; if(!(array->cells=calloc(sizex*sizey, sizeof(struct cell_s)))) { free(array); return NULL; } array->sizex=sizex; array->sizey=sizey; array->startx=0; array->starty=0; array->targetx=0; array->targety=0; return array; } void astar_destroy(ASTAR_ARRAY *array) { struct node_s *node, *next; node=array->open; while(node) { next=node->next; free(node); node=next; } free(array->cells); free(array); } void astar_setpoints(ASTAR_ARRAY *array, int startx, int starty, int targetx, size_t targety) { array->startx=startx; array->starty=starty; array->targetx=targetx; array->targety=targety; } void astar_block(ASTAR_ARRAY *array, int firstx, int firsty, int lastx, int lasty, int blocked) { int x, y; for(y=firsty; y<=lasty; ++y) { for(x=firstx; x<=lastx; ++x) { CELL(array, x, y).blocked=blocked; } } } static int astar_targetcost(ASTAR_ARRAY *array, int x, int y) { /* return 0;*/ return 10*(abs(x-array->targetx)+abs(y-array->targety)); } static struct node_s *astar_opencell(ASTAR_ARRAY *array, int x, int y, int cost, int prevx, int prevy) { struct node_s *node, *nextnode=array->open, *prevnode=NULL; if(x<0 || x>=array->sizex || y<0 || y>=array->sizey) return NULL; if(!CELL(array, x, y).blocked && !CELL(array, x, y).closed && !CELL(array, x, y).opened) { /* Create and populate node */ if(!(node=malloc(sizeof(struct node_s)))) return NULL; node->x=x; node->y=y; node->totalcost=cost+astar_targetcost(array, x, y); /* Update cell info. */ CELL(array, x, y).cost=cost; CELL(array, x, y).prevx=prevx; CELL(array, x, y).prevy=prevy; CELL(array, x, y).opened=1; /* Insert node in open list */ while(nextnode && node->totalcost>nextnode->totalcost) { prevnode=nextnode; nextnode=nextnode->next; } if(nextnode) node->next=nextnode; else node->next=NULL; if(prevnode) prevnode->next=node; else array->open=node; return node; } else if(costopen; while(node && (node->x!=x || node->y!=y)) { prevnode=node; node=node->next; } assert(node); /* Remove node */ if(prevnode) prevnode->next=node->next; else array->open=node->next; free(node); /* Reopen */ astar_opencell(array, x, y, cost, prevx, prevy); return node; } return NULL; } static void astar_openadjacent(ASTAR_ARRAY *array, int x, int y, int cost) { if(x-CELL(array, x, y).prevx!=0) { /* Extra cost from x to y dir */ astar_opencell(array, x-1, y, cost+10, x, y); astar_opencell(array, x, y-1, cost+15, x, y); astar_opencell(array, x, y+1, cost+15, x, y); astar_opencell(array, x+1, y, cost+10, x, y); } else if(y-CELL(array, x, y).prevy!=0) { /* Extra cost from y to x dir */ astar_opencell(array, x-1, y, cost+15, x, y); astar_opencell(array, x, y-1, cost+10, x, y); astar_opencell(array, x, y+1, cost+10, x, y); astar_opencell(array, x+1, y, cost+15, x, y); } else { /* Start pos., no extra cost */ printf("Initial\n"); astar_opencell(array, x-1, y, cost+10, x, y); astar_opencell(array, x, y-1, cost+10, x, y); astar_opencell(array, x, y+1, cost+10, x, y); astar_opencell(array, x+1, y, cost+10, x, y); } } static int astar_closecell(ASTAR_ARRAY *array, struct node_s *node) { CELL(array, node->x, node->y).closed=1; CELL(array, node->x, node->y).opened=0; if(array->open==node) { array->open=node->next; free(node); return 0; } else return -1; } int astar(ASTAR_ARRAY *array) { struct node_s *best; int x, y; struct cell_s *cell; int n=0; /* Find target */ best=astar_opencell(array, array->startx, array->starty, 0, array->startx, array->starty); while(best && (best->x!=array->targetx || best->y!=array->targety)) { astar_openadjacent(array, best->x, best->y, CELL(array, best->x, best->y).cost); astar_closecell(array, best); best=array->open; if(++n>100000) return -1; } if(!best) return -1; /* Find way back (And mark as "route") */ x=best->x; y=best->y; while(x!=array->startx || y!=array->starty) { cell=&CELL(array, x, y); cell->route=1; x=cell->prevx; y=cell->prevy; } return 0; } void astar_print(ASTAR_ARRAY *array) { struct cell_s *cell; int x, y; printf(" "); for(x=0; xsizex; ++x) { printf(" %2d ", x); } putchar('\n'); for(y=0; ysizey; ++y) { printf("%2d: ", y); for(x=0; xsizex; ++x) { cell=&CELL(array, x, y); if(x==array->startx && y==array->starty) printf("SSSS"); else if(x==array->targetx && y==array->targety) printf("TTTT"); else if(cell->route) printf(" ** "); else if(cell->blocked) printf("####"); else if(cell->closed) printf(" CC "); else if(cell->opened) printf(" OO "); else printf(" "); } putchar('\n'); } /* if(array->open) printf("Best open cell: %d,%d, totalcost=%d\n", array->open->x, array->open->y, array->open->totalcost);*/ printf("Cost: %d\n", CELL(array, array->targetx, array->targety).cost); } int main() { ASTAR_ARRAY *array; if(!(array=astar_create(24, 24))) { fprintf(stderr, "ERROR: astar_create() failed\n"); exit(EXIT_FAILURE); } /* Block areas */ astar_block(array, 3, 7, 11, 20, 1); astar_block(array, 11, 10, 22, 20, 1); astar_block(array, 1, 15, 12, 22, 1); /* Unblock areas (make holes) */ astar_block(array, 12, 10, 13, 16, 0); astar_block(array, 5, 16, 12, 16, 0); astar_block(array, 5, 17, 5, 22, 0); astar_block(array, 9, 7, 9, 15, 0); /* Set start and target points */ astar_setpoints(array, 7, 2, 15, 22); /* Do it */ if(astar(array)==-1) { fprintf(stderr, "ERROR: Did not find a route to target\n"); astar_print(array); return EXIT_FAILURE; } /* And... print */ astar_print(array); astar_destroy(array); return EXIT_SUCCESS; }