Search code examples
c++game-developmentbreadth-first-search

How can I get the pathes between two nodes in a grid?


I would like to make a function, which can give the coordinates of the possible pathes between two elements in a simple matrix. Until now, I have managed to determine, whether there could be a path between the two, however, I am pulling my hair out to determine the coordinate of pathes. I was thinking about a linked list, where the pointers is pointing to the next reachable, neighbouring element. However, I feel, that there should be a much simplier method to do that.


Update 1:

I started to create the solution according to sp2danny comment, but unfortunately, it gives me really weird errors in conncetion with unvisitedNeighbours:

unvisitedNeighbours[0].pMain=error: type 'std::queue<node,std::deque<node,std::allocator > >' does not provide a subscript operator

and there is an another error:

this->parent->parent->parent.parent.parent= error: invalid use of 'this' outside of a non-static member function

the errors happen just after pushing the src element to the queue.

#include <vector>
#include <iostream>
#include <queue>

using namespace std;

#define row 5
#define col 5

struct node {
    pair<int, int> pMain;
    pair<int, int> parent;
    int index;

    node(pair<int, int> _p, pair<int, int> _parent, int _index) {
        pMain = _p;
        parent = _parent;
        int index = _index;
    }
};

// to find the path from
// top left to bottom right
bool isPath(int arr[row][col]) {
    // directions
    int dir[4][2] = {{0,  1},{0,  -1},{1,  0},{-1, 0}};

    // queue
    queue<node> unvisitedNeighbours;

    // insert the top right corner.

    node Node(node(make_pair(0, 0), make_pair(-1, -1), 0));
    unvisitedNeighbours.emplace(Node);

    std::vector<node> pathes;
    pathes.push_back(Node);

    // until queue is empty
    while (!unvisitedNeighbours.empty()) {
        node p = unvisitedNeighbours.front();
        unvisitedNeighbours.pop();

        // mark as visited
        arr[p.pMain.first][p.pMain.second] = -1;

        // destination is reached.
        if (p.pMain == make_pair(row - 1, col - 1)) {
            int index = p.index;
            while (index != 0) {
                std::cout << pathes.at(index).pMain.first << " " << pathes.at(index).pMain.second << endl;
                index = pathes.at(index).index;
            }
            return true;
        }

        // check all four directions
        for (int i = 0; i < 4; i++) {
            // using the direction array
            int a = p.pMain.first + dir[i][0];
            int b = p.pMain.second + dir[i][1];

            // not blocked and valid
            if (arr[a][b] != -1 && a >= 0 && b >= 0
                && a < row && b < col) {
                pathes.push_back(node(make_pair(a, b), p.pMain, pathes.size() - 1));
                unvisitedNeighbours.push(node(make_pair(a, b), p.pMain, pathes.size() - 1));

            }
        }
    }
    return false;
}

// Driver Code
int main() {
    // Given array
    int arr[row][col] = {{0,  0, 0,  -1, 0},
                         {-1, 0, 0,  -1, -1},
                         {0,  0, 0,  -1, 0},
                         {-1, 0, 0,  0,  0},
                         {0,  0, -1, 0,  0}};

    // path from arr[0][0] to arr[row][col]
    if (isPath(arr))
        cout << "Yes";
    else
        cout << "No";

    return 0;
}

Solution

  • Basically, you're trying to find the shortest path of an unweighted graph. Here the graph is a 2D grid.

    To print the path, you just need to keep track of the nodes and their parent nodes when you're visiting them.

    Here's a sample code:

    #include<iostream>
    #include<vector>
    #include<queue>
    #include<map>
    using namespace std;
    
    int dirX[] = {-1, 0, 0, 1};
    int dirY[] = {0, -1, 1, 0};
    
    bool IsValid(int& r, int& c, int &totalRow, int& totalCol)
    {
        return (r >= 0 && r < totalRow && c >= 0 && c < totalCol);
    }
    
    int BFS(vector<vector<int>>& grid, pair<int, int> source, pair<int, int> destination, map<pair<int, int>, pair<int, int>>& parent)
    {
        int totalRow = grid.size();
        int totalCol = grid[0].size();
        vector<vector<int>> visited(totalRow, vector<int> (totalCol, 0));
        vector<vector<int>> level(totalRow, vector<int> (totalCol, 0));
        queue<pair<int, int>> q;
        q.push(source);
        parent[source] = { -1, -1 };
    
        while (!q.empty())
        {
            pair<int, int> curr = q.front();
            q.pop();
            visited[curr.first][curr.second] = true;
    
            for (int d = 0; d < 4; d++)
            {
                int nextR = curr.first + dirX[d];
                int nextC = curr.second + dirY[d];
                if (IsValid(nextR, nextC, totalRow, totalCol) && grid[nextR][nextC] != -1 && !visited[nextR][nextC])
                {
                    level[nextR][nextC] = 1 + level[curr.first][curr.second];
                    visited[nextR][nextC] = true;
                    parent[{nextR, nextC}] = curr;
                    q.push({nextR, nextC});
    
                    if (make_pair(nextR, nextC) == destination)
                    {
                        return level[nextR][nextC];
                    }
                }
            }
        }
    
        return -1;
    }
    
    void PrintShortestPath(vector<vector<int>> &grid, pair<int, int> source, pair<int, int> destination)
    {
        map<pair<int, int>, pair<int, int>> parent;
        int shortestDistance = BFS(grid, source, destination, parent);
        if (shortestDistance == -1)
        {
            cout << "No path available!" << endl;
        }
        else
        {
            vector<pair<int, int>> path;
            pair<int, int> currentNode = destination;
            path.push_back(destination);
            cout << "Shortest distance: " << shortestDistance << endl;
    
            while (parent[currentNode] != make_pair(- 1, -1))
            {
                path.push_back(parent[currentNode]);
                currentNode = parent[currentNode];
            }
    
            for (auto &curr : path)
            {
                cout << curr.first << " " << curr.second << endl;
            }
        }
    }
    
    int main()
    {
        vector<vector<int>> grid
        {
            {0,  0, 0,  -1, 0},
            {-1, 0, 0,  -1, -1},
            {0,  0, 0,  -1, 0},
            {-1, 0, 0,  0,  0},
            {0,  0, -1, 0,  0}
        };
    
        PrintShortestPath(grid, { 0, 0 }, { 4, 4 });
    }
    

    Note: If you search in google "Print shortest path of an unweighted graph", you'll be able to find more about it.

    About the error, you're getting in your code

    In the constructor, you've written int index = _index. Due to this, the correct index value was not assigned.

    So at the time of accessing pathes.at(index), the error occurred.

    node(pair<int, int> _p, pair<int, int> _parent, int _index)
    {
        pMain = _p;
        parent = _parent;
        index = _index;
    }