summaryrefslogblamecommitdiff
path: root/src/astar.cpp
blob: f3e8acb0ff7cd0ee715857205b13fde76dff6df1 (plain) (tree)
1
2
3
4
5
6
7
8
9
                  
 
                      

                      


                      
                         
                           
                      
                          
 
                        
                                                    
































                                                                              
                      

                         


               


                            
                                



                        
                    


                





                                                                              

                                                                  


                                                                        

                                                                       



                    

                                                   














                                                                         

                                                                         
                              
 



                                                                             
 

                                                                  

                                                                              

                                       
                                                    

                                                  
                                            
                                            
 

                                                             
 








                                                                            

                  

                                                                              
                
















                                                                             


                     

                                                                        


                                              





                                                        

 






                                                                               
 

                                                                    








                                                                               

                                                             
                                                            
                                                  



                                                                                                                                                         

                                                                     
                                                                                                                                           
                                                                  
                                     

                                                               
                                                              
                                                                                                                                           

                                                                     
                                                                                                                                           
                                                                  


                                     














                                                                               
                                                                     
















































                                                                               
                                                                   











                                                                            
                                                                     

































                                                                               
                                                                       





























                                                                                
                                                          











                                                                                                            
                                                                               



















                                                                              
                             




                                                                    
                         
                  

         


                                                 

                                  



                                                                          

                                 

                                                      


                                          
                                         
                                        

                                             

                                                          
                                                             

                                                                           



                                                                               
                                  

                                      
            

                                            

                                                

                                                      

                                          



                                                                     



                                            
                                           












                                                                       
 
                   
     
 

                     

 




                                                              
                                     

                                                
                                        


                            
 
#include "astar.h"

#define MAP_WIDTH  200
#define MAP_HEIGHT 200

#define WALKABLE     0
#define NOT_WALKABLE 1

// path-related constants
const int numberPeople = 1;
int onClosedList = 10;
const int notfinished = 0;

// Declare needed arrays
//char tiledMap.getPathWalk [MAP_WIDTH][MAP_HEIGHT];

/** 1 dimensional array holding ID# of open list items */
int openList[MAP_WIDTH * MAP_HEIGHT + 2];

/**
 * 2 dimensional array used to record whether a cell is on the open list or
 * on the closed list
 */
int whichList[MAP_WIDTH + 1][MAP_HEIGHT + 1];

/** 1d array stores the x location of an item on the open list */
int openX[MAP_WIDTH * MAP_HEIGHT + 2];

/** 1d array stores the y location of an item on the open list */
int openY[MAP_WIDTH * MAP_HEIGHT + 2];

/** 2d array to store parent of each cell (x) */
int parentX[MAP_WIDTH + 1][MAP_HEIGHT + 1];

/** 2d array to store parent of each cell (y) */
int parentY[MAP_WIDTH + 1][MAP_HEIGHT + 1];

/** 1d array to store F cost of a cell on the open list */
int F_cost[MAP_WIDTH * MAP_HEIGHT + 2];

/** 2d array to store G_cost cost for each cell */
int G_cost[MAP_WIDTH + 1][MAP_HEIGHT + 1];

/** 1d array to store H cost of a cell on the open list */
int H_cost[MAP_WIDTH * MAP_HEIGHT + 2];

int pathLength;     /**< length of the FOUND path for critter */
int pathLocation;   /**< current position along the chosen path for critter */
int* path_bank = NULL;

// Path reading variables
int pathStatus;
int xPath;
int yPath;

/** Initialize pathfinder */
void pathfinder_init() {
    path_bank = (int*)malloc(4);
}

/** Exit pathfinder */
void pathfinder_exit() {
    free(path_bank);
}

/** Find path */
PATH_NODE *find_path(int pathfinderID, int s_x, int s_y, int e_x, int e_y)
{
    int onOpenList = 0, parentXval = 0, parentYval = 0;
    int a = 0, b = 0, m = 0, u = 0, v = 0, temp = 0, corner = 0;
    int numberOfOpenListItems = 0, addedGCost = 0, tempG = 0, path = 0, x = 0;
    int y = 0, tempx, pathX, pathY, cellPosition, newOpenListItemID = 0;

    // If starting location and target are in the same location...
    if (s_x == e_x && s_y == e_y && pathLocation > 0) return NULL;
    else if (s_x == e_x && s_y == e_y && pathLocation == 0) return NULL;

    // If dest tile is not walkable, return that it's a NOT_FOUND path.
    if (!tiledMap.getWalk(e_x, e_y)) {
        xPath = s_x;
        yPath = s_y;
        return NULL;
    }

    // Reset some variables that need to be cleared
    for (x = 0; x < MAP_WIDTH; x++) {
        for (y = 0; y < MAP_HEIGHT; y++) {
            whichList[x][y] = 0;
        }
    }

    // Changing the values of onOpenList and onClosed list is faster than
    // redimming whichList() array
    onClosedList = 2;
    onOpenList = 1;
    pathLength = NOT_STARTED;
    pathLocation = NOT_STARTED;

    // Reset starting square's G_cost value to 0
    G_cost[s_x][s_y] = 0;

    // Add the starting location to the open list of tiles to be checked.
    numberOfOpenListItems = 1;

    // Assign it as the top (and currently only) item in the open list, which
    // is maintained as a binary heap (explained below)
    openList[1] = 1;
    openX[1] = s_x ; openY[1] = s_y;

    // Do the following until a path is FOUND or deemed NOT_FOUND.
    do {
        // If the open list is not empty, take the first cell off of the list.
        // This is the lowest F cost cell on the open list.
        if (numberOfOpenListItems != 0)
        {
            // Pop the first item off the open list.

            // Record cell coordinates of the item
            parentXval = openX[openList[1]];
            parentYval = openY[openList[1]];

            // Add the item to the closed list
            whichList[parentXval][parentYval] = onClosedList;

            // Open List = Binary Heap: Delete this item from the open list,
            // which
            // Reduce number of open list items by 1
            numberOfOpenListItems = numberOfOpenListItems - 1;

            // Delete the top item in binary heap and reorder the heap, with
            // the lowest F cost item rising to the top.
            // Move the last item in the heap up to slot #1
            openList[1] = openList[numberOfOpenListItems + 1];
            v = 1;

            // Repeat the following until the new item in slot #1 sinks to its
            // proper spot in the heap.
            do {
                u = v;
                // If both children exist
                if (2 * u + 1 <= numberOfOpenListItems) {
                    // Check if the F cost of the parent is greater than each
                    // child.
                    // Select the lowest of the two children.
                    if (F_cost[openList[u]] >= F_cost[openList[2 * u]])
                        v = 2 * u;
                    if (F_cost[openList[v]] >= F_cost[openList[2 * u + 1]])
                        v = 2 * u + 1;
                } else {
                    // If only child #1 exists
                    if (2 * u <= numberOfOpenListItems) {
                        // Check if the F cost of the parent is greater than
                        // child #1
                        if (F_cost[openList[u]] >= F_cost[openList[2 * u]])
                            v = 2 * u;
                    }
                }

                if (u != v) {
                    // If parent's F is > one of its children, swap them
                    temp = openList[u];
                    openList[u] = openList[v];
                    openList[v] = temp;
                }
                else {
                    // Otherwise, exit loop
                    break;
                }
            } while (u != v); // Reorder the binary heap


            //  Check the adjacent squares. (Its "children" -- these path
            //  children are similar, conceptually, to the binary heap children
            //  mentioned above, but don't confuse them. They are different.
            //  Path children are portrayed in Demo 1 with grey pointers
            //  pointing toward their parents.) Add these adjacent child
            //  squares to the open list for later consideration if appropriate
            //  (see various if statements below).

            for (b = parentYval - 1; b <= parentYval + 1; b++) {
                for (a = parentXval - 1; a <= parentXval + 1; a++) {
                    //  If not off the map (do this first to avoid array
                    //  out-of-bounds errors)
                    if (a != -1 && b != -1 &&
                            a != MAP_WIDTH && b != MAP_HEIGHT)
                    {
                        // If not already on the closed list (items on the
                        // closed list have already been considered and can now
                        // be ignored).
                        if (whichList[a][b] != onClosedList) {
                            // If not a wall/obstacle square.
                            if (tiledMap.getWalk(a, b)) {
                                //  Don't cut across corners
                                corner = WALKABLE;

                                if (a == parentXval - 1) {
                                    if (b == parentYval - 1) {
                                        if (!tiledMap.getWalk(parentXval - 1, parentYval) || !tiledMap.getWalk(parentXval, parentYval - 1)) // cera slash
                                            corner = NOT_WALKABLE;
                                    } else if (b == parentYval + 1) {
                                        if (!tiledMap.getWalk(parentXval, parentYval + 1) || !tiledMap.getWalk(parentXval - 1, parentYval))
                                            corner = NOT_WALKABLE;
                                    }
                                }
                                else if (a == parentXval + 1) {
                                    if (b == parentYval - 1) {
                                        if (!tiledMap.getWalk(parentXval, parentYval - 1) || !tiledMap.getWalk(parentXval + 1, parentYval))
                                            corner = NOT_WALKABLE;
                                    } else if (b == parentYval + 1) {
                                        if (!tiledMap.getWalk(parentXval + 1, parentYval) || !tiledMap.getWalk(parentXval, parentYval + 1))
                                            corner = NOT_WALKABLE;
                                    }
                                }

                                if (corner == WALKABLE) {
                                    // If not already on the open list, add it
                                    // to the open list.
                                    if (whichList[a][b] != onOpenList) {
                                        // Create a new open list item in the
                                        // binary heap.
                                        // Each new item has a unique ID #
                                        newOpenListItemID += 1;
                                        m = numberOfOpenListItems + 1;
                                        // Place the new open list item
                                        // (actually, its ID#) at the bottom of
                                        // the heap
                                        openList[m] = newOpenListItemID;
                                        // Record the x and y coordinates of
                                        // the new item
                                        openX[newOpenListItemID] = a;
                                        openY[newOpenListItemID] = b;

                                        // Figure out its G_cost cost
                                        if (abs(a - parentXval) == 1 &&
                                                abs(b - parentYval) == 1)
                                        {
                                            // Cost of going to diagonal
                                            // squares.
                                            addedGCost = 14;
                                        }
                                        else {
                                            // Cost of going to non-diagonal
                                            // squares.
                                            addedGCost = 10;
                                        }

                                        G_cost[a][b] =
                                            G_cost[parentXval][parentYval] +
                                            addedGCost;

                                        // Figure out its H and F costs and
                                        // parent.
                                        H_cost[openList[m]] =
                                            10 * (abs(a - e_x) + abs(b - e_y));
                                        F_cost[openList[m]] =
                                            G_cost[a][b] + H_cost[openList[m]];
                                        parentX[a][b] = parentXval;
                                        parentY[a][b] = parentYval;

                                        // Move the new open list item to the
                                        // proper place in the binary heap.
                                        // Starting at the bottom, successively
                                        // compare to parent items, swapping as
                                        // needed until the item finds its
                                        // place in the heap or bubbles all the
                                        // way to the top (if it has the lowest
                                        // F cost).

                                        // While item hasn't bubbled to the
                                        // top (m = 1)
                                        while (m != 1) {
                                            // Check if child's F cost is <
                                            // parent's F cost. If so, swap
                                            // them.
                                            if (F_cost[openList[m]] <=
                                                    F_cost[openList[m / 2]])
                                            {
                                                temp = openList[m / 2];
                                                openList[m / 2] = openList[m];
                                                openList[m] = temp;
                                                m = m / 2;
                                            }
                                            else {
                                                break;
                                            }
                                        }

                                        // Add one to the number of items in
                                        // the heap.
                                        numberOfOpenListItems += 1;
                                        // Change whichList to show that the
                                        // new item is on the open list.
                                        whichList[a][b] = onOpenList;
                                    } else {
                                        // If whichList(a,b) = onOpenList

                                        // If adjacent cell is already on the
                                        // open list, check to see if this path
                                        // to that cell from the starting
                                        // location is a better one.  If so,
                                        // change the parent of the cell and
                                        // its G_cost and F costs.  Figure out
                                        // the G_cost cost of this possible new
                                        // path

                                        if (abs(a - parentXval) == 1 &&
                                                abs(b - parentYval) == 1)
                                        {
                                            // Cost of going to diagonal tiles
                                            addedGCost = 14;
                                        }
                                        else {
                                            // Cost of going to non-diagonal
                                            // tiles
                                            addedGCost = 10;
                                        }

                                        tempG = G_cost[parentXval][parentYval]
                                            + addedGCost;

                                        // If this path is shorter (G_cost cost
                                        // is lower) then change the parent
                                        // cell, G_cost cost and F cost.
                                        if (tempG < G_cost[a][b]) {
                                            // If G_cost cost is less,
                                            // change the square's parent
                                            parentX[a][b] = parentXval;
                                            parentY[a][b] = parentYval;
                                            // Change the G_cost cost
                                            G_cost[a][b] = tempG;

                                            // Because changing the G_cost cost
                                            // also changes the F cost, if the
                                            // item is on the open list we need
                                            // to change the item's recorded F
                                            // cost and its position on the
                                            // open list to make sure that we
                                            // maintain a properly ordered open
                                            // list.

                                            // Look for the item in the heap
                                            for (int x = 1;
                                                    x <= numberOfOpenListItems;
                                                    x++)
                                            {
                                                if (openX[openList[x]] == a &&
                                                        openY[openList[x]] == b)
                                                {
                                                    // Item FOUND
                                                    // Change the F cost
                                                    F_cost[openList[x]] =
                                                        G_cost[a][b] +
                                                        H_cost[openList[x]];

                                                    // See if changing the F
                                                    // score bubbles the item
                                                    // up from it's current
                                                    // location in the heap
                                                    m = x;

                                                    // While item hasn't
                                                    // bubbled to the top
                                                    // (m = 1)
                                                    while (m != 1) {
                                                        // Check if child is <
                                                        // parent. If so, swap
                                                        // them.
                                                        if (F_cost[openList[m]] < F_cost[openList[m / 2]]) {
                                                            temp = openList[m / 2];
                                                            openList[m / 2] =
                                                                openList[m];
                                                            openList[m] = temp;
                                                            m = m / 2;
                                                        }
                                                        else {
                                                            break;
                                                        }
                                                    }
                                                    //Exit for x = loop
                                                    break;
                                                } // If openX(openList(x)) = a
                                            } // F x = 1 To nrOfOpenListItems
                                        } // If tempG < G_cost(a, b)
                                    } // else If whichList(a, b) = onOpenList
                                } // If not cutting a corner
                            } // If not a wall/obstacle square.
                        } // If not already on the closed list
                    } // If not off the map
                } // for (a = parentXval - 1; a <= parentXval + 1; a++)
            } // for (b = parentYval - 1; b <= parentYval + 1; b++)
        } else { // if (numberOfOpenListItems != 0)
            // If open list is empty then there is no path.
            path = NOT_FOUND;
            break;
        }

        // If target is added to open list then path has been FOUND.
        if (whichList[e_x][e_y] == onOpenList) {
            path = FOUND;
            break;
        }

    }
    // Do until path is FOUND or deemed NOT_FOUND
    while (path != FOUND && path != NOT_FOUND);

    // Save the path if it exists.
    if (path == FOUND)
    {
        // Working backwards from the target to the starting location by
        // checking each cell's parent, figure out the length of the path.
        pathX = e_x; pathY = e_y;
        do {
            // Look up the parent of the current cell.
            tempx = parentX[pathX][pathY];
            pathY = parentY[pathX][pathY];
            pathX = tempx;

            // Figure out the path length
            pathLength = pathLength + 1;
        }
        while (pathX != s_x || pathY != s_y);

        // Resize the data bank to the right size in bytes
        path_bank = (int*)realloc(path_bank, pathLength * 8);

        // Now copy the path information over to the databank. Since we are
        // working backwards from the target to the start location, we copy the
        // information to the data bank in reverse order. The result is a
        // properly ordered set of path data, from the first step to the last.

        pathX = e_x ; pathY = e_y;
        // Start at the end
        cellPosition = pathLength * 2;
        do {
            // Work backwards 2 integers
            cellPosition = cellPosition - 2;
            path_bank[cellPosition] = pathX;
            path_bank[cellPosition + 1] = pathY;
            // Look up the parent of the current cell.
            tempx = parentX[pathX][pathY];
            pathY = parentY[pathX][pathY];
            pathX = tempx;
            // If we have reached the starting square, exit the loop.
        }
        while (pathX != s_x || pathY != s_y);

        PATH_NODE *ret = NULL, *temp = NULL;
        pathLocation = 1;
        ret = new PATH_NODE(s_x, s_y);
        temp = ret;
        while (pathLocation < pathLength) {
            temp->next = new PATH_NODE(
                    path_bank[pathLocation * 2 - 2],
                    path_bank[pathLocation * 2 - 1]);
            if (temp->next == NULL) throw "Unable to create path node";
            temp = temp->next;
            pathLocation++;
        }
        if (temp != NULL) {
            temp->next = new PATH_NODE(e_x, e_y);
        }
        else {
            throw "Null reference";
        }

        return ret;
    }

    // Path not found
    return NULL;
}

void ReadPath(int pathfinderID)
{
    // If a path exists, read the path data from the pathbank.
    // Set pathLocation to 1st step
    pathLocation = 1;
    while (pathLocation<pathLength) {
        int a = path_bank[pathLocation * 2 - 2];
        int b = path_bank[pathLocation * 2 - 1];
        pathLocation = pathLocation + 1;
        // Draw dotted path
        whichList[a][b] = 3;
    }
}