Skip to content

Instantly share code, notes, and snippets.

@AustinDeric
Last active July 11, 2018 20:54
Show Gist options
  • Save AustinDeric/8c860743ad81cd8c2fe4853376b20adb to your computer and use it in GitHub Desktop.
Save AustinDeric/8c860743ad81cd8c2fe4853376b20adb to your computer and use it in GitHub Desktop.
Wavefront path planner
// Compiled with: g++ -Wall -std=c++14 -pthread
/*
Shortest path on a grid
1. In robotics, a robot often to find a plan (a path or a set of actions) to reach its goal. Write code to determine the shortest path on a grid. Where the grid is square with the length of side N = 10. In one step the robot is only allowed to move to a cell adjacent to its current cell, i.e., robot can move up, down, left, right (not diagonally). The robot can start at a point (0,0) and its goal location (7,9) assuming the row and column values are indexed starting at 0. Your output is to print the sequence of cells that the robot will visit. You can print the row, col value for each cell that the robot visits on its way to the goal.
Bonus Question: Now lets say someone placed an obstacle at (6,7) and (2,2). An obstacle means that the robot can no longer go through that cell. How will your solution change? Implement your solution and print the path.
Objective: Write a function "findShortestPath(const int N, const int startx, const int starty, const int goalx, const int goaly)" that prints out the shortest path on the grid given the start, goal (and obstacle locations).
**Well documented code gets bonus points**
*/
#include <iostream>
#include <vector>
using namespace std;
// Complete this function
/**
I recently did a problem like this for parking in a parking lot optimization using autonomous cars. We used a gradient minimization technique with a cost map. I am going to give that a try. I believe it was similar to this https://www.cs.cmu.edu/~motionplanning/lecture/Chap4-Potential-Field_howie.pdf. This code will use the wavefront method for simplicity.
The code will be documented using doxygen. since this IDE doesn't have a doxygen plugin i will do it manually.
*/
/*
Utility function to print map
*/
void printMap(vector <vector<int>> map)
{
for (unsigned i = 0; i < map.size(); i++)
{
for (unsigned j = 0; j < map[i].size(); j++)
{
cout << map[i][j] << " ";
}
cout << endl;
}
}
void printMap(vector <vector<string>> map)
{
for (unsigned i = 0; i < map.size(); i++)
{
for (unsigned j = 0; j < map[i].size(); j++)
{
cout << map[i][j] << " ";
}
cout << endl;
}
}
void findShortestPath(const int N /*grid size*/, const int startx, const int starty, const int goalx, const int goaly)
{
//create the map
vector <vector<int>> map(N, vector<int>(N));
//set start point (uneeded code becuase map is all zeros but keeping to show method)
map[startx][starty] = 0;
//set end point
int end_weight = 2;
map[goalx][goaly] = end_weight;
// implement the wavefront function using 4-point connectivity (no diagonals)
vector <vector<int>> wavefront(N, vector<int>(N)); //datastructure to track wavefront completeness.
wavefront[goalx][goaly] = 1;
unsigned iteration = 0;
bool wavefront_complete = false;
//index of wavefront - starting location
unsigned wavefront_x = goalx-1;
unsigned wavefront_y = goaly-1;
cout << "starting map:" <<endl;
printMap(map);
cout << "starting wavefront:" << endl;
printMap(wavefront);
cout << "-----------" << endl;
//propogate wavefront
bool mapComplete = false;
while(!mapComplete){
++iteration;
cout << "iteration: " << iteration << endl;
cout << "wavefront_x: " << wavefront_x << endl;
cout << "wavefront_y: " << wavefront_y << endl;
cout << "i: " << goalx-wavefront_x << endl;
for (int i = goalx-wavefront_x; i > 0; --i){//top row
wavefront[wavefront_x][wavefront_y+i] = 1;
map[wavefront_x][wavefront_y+i] = iteration+end_weight;
}
cout << "j " << goaly-wavefront_y << endl;
for (int j = goaly-wavefront_y; j > 0; --j){//left column
wavefront[wavefront_x+j][wavefront_y] = 1;
map[wavefront_x+j][wavefront_y] = iteration+end_weight;
}
wavefront[wavefront_x][wavefront_y] = 1;
map[wavefront_x][wavefront_y] = iteration+end_weight;
if(wavefront_y==0 && wavefront_x==0)
mapComplete = true;
if(wavefront_x>0)
--wavefront_x;
if(wavefront_y>0)
--wavefront_y;
//set obstacles!
map[6][7] = 0;
map[2][2] = 0;
//debugging information
cout << "map:" << endl;
printMap(map);
//print wavefront for debugging purposes
cout << "wavefront:" << endl;
printMap(wavefront);
cout << "-----------" << endl;
}
//now that the maps are created, lets navigate by going to the cell with the smaller non-zero value
bool arrived = false;
vector< pair<int, int> > path;
//start
path.push_back(std::make_pair(startx,starty));
int steps = 0;
while(!arrived){
//look down and right
int down_cell = map[path.back().first+1][path.back().second];
int right_cell = map[path.back().first][path.back().second+1];
cout << "down cell: " << down_cell << endl;
cout << "right cell: " << right_cell << endl;
//go down
if(down_cell < right_cell && down_cell!=0) { //go down
path.push_back(std::make_pair(path.back().first+1, path.back().second));
}
else if(down_cell > right_cell && right_cell!=0) { //go right
path.push_back(std::make_pair(path.back().first, path.back().second+1));
}
else { //down and right are the same so go right
path.push_back(std::make_pair(path.back().first, path.back().second+1));
}
steps++;
cout << "current path:" << endl;
vector <vector<string>> path_map(N, vector<string>(N, "o"));
for (auto current_cell : path)
{
cout << current_cell.first << ", " << current_cell.second << endl;
path_map[startx][starty] = "S"; //start at S
path_map[current_cell.first][current_cell.second] = "-"; //path is a -
path_map[6][7] = "X"; //obstacles are X
path_map[2][2] = "X"; //obstacles are X
if(goalx == current_cell.first && goaly == current_cell.second){
cout << "path map: " << endl;
path_map[goalx][goaly] = "G"; // end at G
printMap(path_map);
arrived = true;
}
}
}
}
int main()
{
// grid size
int N = 10;
// start
int startx = 0;
int starty = 0;
// goal (zero-indexed)
int goalx = 7;
int goaly = 9;
findShortestPath(N, startx, starty, goalx, goaly);
return 0;
}
@AustinDeric
Copy link
Author

------------------------------------------
AustinDeric is running the code
------------------------------------------

prog.cpp: In function 'void findShortestPath(int, int, int, int, int)':
prog.cpp:57:10: warning: unused variable 'wavefront_complete' [-Wunused-variable]
     bool wavefront_complete = false;
          ^~~~~~~~~~~~~~~~~~
starting map:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 2 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
starting wavefront:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 1 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
-----------
iteration: 1
wavefront_x: 6
wavefront_y: 8
i: 1
j 1
map:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 3 3 
0 0 0 0 0 0 0 0 3 2 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
wavefront:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 1 1 
0 0 0 0 0 0 0 0 1 1 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
-----------
iteration: 2
wavefront_x: 5
wavefront_y: 7
i: 2
j 2
map:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 4 4 4 
0 0 0 0 0 0 0 0 3 3 
0 0 0 0 0 0 0 4 3 2 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
wavefront:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 1 1 
0 0 0 0 0 0 0 1 1 1 
0 0 0 0 0 0 0 1 1 1 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
-----------
iteration: 3
wavefront_x: 4
wavefront_y: 6
i: 3
j 3
map:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 5 5 5 5 
0 0 0 0 0 0 5 4 4 4 
0 0 0 0 0 0 5 0 3 3 
0 0 0 0 0 0 5 4 3 2 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
wavefront:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 
0 0 0 0 0 0 1 1 1 1 
0 0 0 0 0 0 1 1 1 1 
0 0 0 0 0 0 1 1 1 1 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
-----------
iteration: 4
wavefront_x: 3
wavefront_y: 5
i: 4
j 4
map:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 6 6 6 6 6 
0 0 0 0 0 6 5 5 5 5 
0 0 0 0 0 6 5 4 4 4 
0 0 0 0 0 6 5 0 3 3 
0 0 0 0 0 6 5 4 3 2 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
wavefront:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 1 1 1 1 1 
0 0 0 0 0 1 1 1 1 1 
0 0 0 0 0 1 1 1 1 1 
0 0 0 0 0 1 1 1 1 1 
0 0 0 0 0 1 1 1 1 1 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
-----------
iteration: 5
wavefront_x: 2
wavefront_y: 4
i: 5
j 5
map:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 7 7 7 7 7 7 
0 0 0 0 7 6 6 6 6 6 
0 0 0 0 7 6 5 5 5 5 
0 0 0 0 7 6 5 4 4 4 
0 0 0 0 7 6 5 0 3 3 
0 0 0 0 7 6 5 4 3 2 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
wavefront:
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 1 1 1 1 1 1 
0 0 0 0 1 1 1 1 1 1 
0 0 0 0 1 1 1 1 1 1 
0 0 0 0 1 1 1 1 1 1 
0 0 0 0 1 1 1 1 1 1 
0 0 0 0 1 1 1 1 1 1 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
-----------
iteration: 6
wavefront_x: 1
wavefront_y: 3
i: 6
j 6
map:
0 0 0 0 0 0 0 0 0 0 
0 0 0 8 8 8 8 8 8 8 
0 0 0 8 7 7 7 7 7 7 
0 0 0 8 7 6 6 6 6 6 
0 0 0 8 7 6 5 5 5 5 
0 0 0 8 7 6 5 4 4 4 
0 0 0 8 7 6 5 0 3 3 
0 0 0 8 7 6 5 4 3 2 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
wavefront:
0 0 0 0 0 0 0 0 0 0 
0 0 0 1 1 1 1 1 1 1 
0 0 0 1 1 1 1 1 1 1 
0 0 0 1 1 1 1 1 1 1 
0 0 0 1 1 1 1 1 1 1 
0 0 0 1 1 1 1 1 1 1 
0 0 0 1 1 1 1 1 1 1 
0 0 0 1 1 1 1 1 1 1 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
-----------
iteration: 7
wavefront_x: 0
wavefront_y: 2
i: 7
j 7
map:
0 0 9 9 9 9 9 9 9 9 
0 0 9 8 8 8 8 8 8 8 
0 0 0 8 7 7 7 7 7 7 
0 0 9 8 7 6 6 6 6 6 
0 0 9 8 7 6 5 5 5 5 
0 0 9 8 7 6 5 4 4 4 
0 0 9 8 7 6 5 0 3 3 
0 0 9 8 7 6 5 4 3 2 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
wavefront:
0 0 1 1 1 1 1 1 1 1 
0 0 1 1 1 1 1 1 1 1 
0 0 1 1 1 1 1 1 1 1 
0 0 1 1 1 1 1 1 1 1 
0 0 1 1 1 1 1 1 1 1 
0 0 1 1 1 1 1 1 1 1 
0 0 1 1 1 1 1 1 1 1 
0 0 1 1 1 1 1 1 1 1 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
-----------
iteration: 8
wavefront_x: 0
wavefront_y: 1
i: 7
j 8
map:
0 10 10 10 10 10 10 10 10 9 
0 10 9 8 8 8 8 8 8 8 
0 10 0 8 7 7 7 7 7 7 
0 10 9 8 7 6 6 6 6 6 
0 10 9 8 7 6 5 5 5 5 
0 10 9 8 7 6 5 4 4 4 
0 10 9 8 7 6 5 0 3 3 
0 10 9 8 7 6 5 4 3 2 
0 10 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
wavefront:
0 1 1 1 1 1 1 1 1 1 
0 1 1 1 1 1 1 1 1 1 
0 1 1 1 1 1 1 1 1 1 
0 1 1 1 1 1 1 1 1 1 
0 1 1 1 1 1 1 1 1 1 
0 1 1 1 1 1 1 1 1 1 
0 1 1 1 1 1 1 1 1 1 
0 1 1 1 1 1 1 1 1 1 
0 1 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
-----------
iteration: 9
wavefront_x: 0
wavefront_y: 0
i: 7
j 9
map:
11 11 11 11 11 11 11 11 10 9 
11 10 9 8 8 8 8 8 8 8 
11 10 0 8 7 7 7 7 7 7 
11 10 9 8 7 6 6 6 6 6 
11 10 9 8 7 6 5 5 5 5 
11 10 9 8 7 6 5 4 4 4 
11 10 9 8 7 6 5 0 3 3 
11 10 9 8 7 6 5 4 3 2 
11 10 0 0 0 0 0 0 0 0 
11 0 0 0 0 0 0 0 0 0 
wavefront:
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 
1 1 0 0 0 0 0 0 0 0 
1 0 0 0 0 0 0 0 0 0 
-----------
down cell: 11
right cell: 11
current path:
0, 0
0, 1
down cell: 10
right cell: 11
current path:
0, 0
0, 1
1, 1
down cell: 10
right cell: 9
current path:
0, 0
0, 1
1, 1
1, 2
down cell: 0
right cell: 8
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
down cell: 8
right cell: 8
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
down cell: 7
right cell: 8
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
2, 4
down cell: 7
right cell: 7
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
2, 4
2, 5
down cell: 6
right cell: 7
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
2, 4
2, 5
3, 5
down cell: 6
right cell: 6
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
2, 4
2, 5
3, 5
3, 6
down cell: 5
right cell: 6
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
2, 4
2, 5
3, 5
3, 6
4, 6
down cell: 5
right cell: 5
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
2, 4
2, 5
3, 5
3, 6
4, 6
4, 7
down cell: 4
right cell: 5
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
2, 4
2, 5
3, 5
3, 6
4, 6
4, 7
5, 7
down cell: 0
right cell: 4
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
2, 4
2, 5
3, 5
3, 6
4, 6
4, 7
5, 7
5, 8
down cell: 3
right cell: 4
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
2, 4
2, 5
3, 5
3, 6
4, 6
4, 7
5, 7
5, 8
6, 8
down cell: 3
right cell: 3
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
2, 4
2, 5
3, 5
3, 6
4, 6
4, 7
5, 7
5, 8
6, 8
6, 9
down cell: 2
right cell: 49
current path:
0, 0
0, 1
1, 1
1, 2
1, 3
1, 4
2, 4
2, 5
3, 5
3, 6
4, 6
4, 7
5, 7
5, 8
6, 8
6, 9
7, 9

@AustinDeric
Copy link
Author

AustinDeric commented Jul 11, 2018

I found a bug and added a path map that displays the map of the path:

G is goal
S is start

  • is path taken
path map: 
S - o o o o o o o o 
o - - - - o o o o o 
o o X o - - o o o o 
o o o o o - - o o o 
o o o o o o - - o o 
o o o o o o o - - o 
o o o o o o o X - - 
o o o o o o o o o G 
o o o o o o o o o o 
o o o o o o o o o o

The code isn't super robust at this point but can be made so with more time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment