The problem I am facing is the following:
I have a function based on the BFS search algorithm that I use in a NxM grid, the mission of this function is to return the following Direction from a set of possible Directions = {Up, Down, Left , Right} (No diagonal moves!)to which a player has to move, so that in each "round / frame" where there is a type of item of a game (For example, in this specific case, a bazooka) is closer to the item.
To address the problem, I have created a Map class made of vector <vector <Cell> > where vector is from the standard library and Cell is what the grid is made of and has some consulting methods on what is in one of the NxM cells (if there is a building, an enemy, a Bazooka, etc.)
So, for implementing a solution for this, I made a struct TrackingBFS to reconstruct the path of the BFS search:
struct TrackingBFS {
pair <int,int> anterior;
bool visited;
};
And this is the BFS search implementation:
//Pre: origen is a valid position on the grid where the player is
//Post:Returns a pair of bool and a direction to the closest bazooka. If we have access to a bazooka, then we will return a pair (true,Dir) where Dir is a direction to take to be closer to the bazooka else a pair (false, Dir) where dir is just the same direction as origen.
pair <bool,Dir> direction_to_closest_gun (const Pos& origen) {
//R = board_rows() C = board_cols()
//m = mapa
//sr,sc = origin positions
int sr = origen.i;
int sc = origen.j;
//Variables para mantener tracking de los pasos cogidos
queue <int> rq; //Queue of x-row coordinates
queue <int> cq; //Queue of y-row coordinates
int move_count = 0; //Number of steps
int nodes_left_in_layer = 1; //How many nodes we need to de-queue before doing a step
int nodes_in_next_layer = 0; //How many nodes we add to the expansio of the BFS so we can upgrade nodes_left_in_layer in the next iteration
bool arma_mejor_encontrada = false;
//Visited is a MxN matrix of TrackingBFS that for every i,j stores the following information:
//If we visited the cell at position visited [i][j], then the TrackingBFS struct will be visited = true and last_node = (k,l) where k and l are the components of the LAST cell on the grid we visited in the BFS search.
//Else if we didn't visited the cell at [i][j], the TrackingBFS struct will be visited = true and last_node (-1,-1).
TrackingBFS aux;
aux.last_node = make_pair(-1,-1);
aux.visited = false;
//We create a Matrix of TrackingBFS named visited of NxM filled with the unvisited cells
vector < vector <TrackingBFS> > visited (board_rows(), vector <TrackingBFS> (board_cols(),aux));
//--------------------------------SOLVE---------------------------------
rq.push(sr);
cq.push(sc);
visited[sr][sc].visited = true;
visited[sr][sc].last_node = make_pair(sr,sc);
int xfound;
int yfound;
while (!rq.empty()) {
int r = rq.front();
int c = cq.front();
rq.pop();
cq.pop();
if (mapa[r][c].weapon == Bazooka) {
arma_mejor_encontrada = true;
xfound = r;
yfound = c;
break;
}
//Explore neighbours
Pos p (r,c);
for (Dir d : dirs) {
Pos searching = p + d;
int rr = searching.i;
int cc = searching.j;
//If the position we are searching is out of range or it's been visited before or there is a obstacle then continue
if (!pos_ok(searching) or visited[rr][cc].visited or mapa[rr][cc].type == Building or mapa[rr][cc].resistance != -1 or mapa[rr][cc].id != -1) {
//NADA
}
//Else we add the visited node to the queue, and fill the information on visited[rr][cc] with the node we are coming and mark it as visited
else {
rq.push(rr);
cq.push(cc);
visited[rr][cc].visited = true;
visited[rr][cc].last_node = make_pair (r,c);
nodes_in_next_layer++;
}
}
nodes_left_in_layer--;
if (nodes_left_in_layer == 0) {
nodes_left_in_layer = nodes_in_next_layer;
nodes_in_next_layer = 0;
move_count++;
}
}
//If we found the Bazooka
if (arma_mejor_encontrada) {
//Return the pair (true,next direction of the player at position (sr,sc) to approach the bazooka)
return make_pair(arma_mejor_encontrada, reconstruir_camino(visited,xfound,yfound,sr,sc));
}
else {
//If there is no bazooka we return (false,Up (this second component is meaningless))
return make_pair(arma_mejor_encontrada, Up);
}
}
The reconstruir_camino (recosntruct_path in english) implementation:
//This function is made to reconstruct the path from where we found de bazooka (x,y) to where our player is (ox,oy), whe are only interested in the next position of because this is run each frame, so we are constantly refreshing the state of the map.
Dir reconstruir_camino(const vector < vector <TrackingBFS> >& visited, const int& x, const int& y, const int& ox, const int& oy) {
//In v we save the pair of coordinates of our path, this was done only for debuging and in debug_matriz_tracking(visited) is also only for debuging
vector <pair<int,int>> path;
debug_matriz_tracking(visited);
//
int i = visited[x][y].last_node.first;
int j = visited[x][y].last_node.second;
while (not (i == ox and j == oy)) { //While the next node is not iqual as the original node we started de search (The one where our player is)
path.push_back(make_pair(i,j)); //Just for debuging
i = visited[i][j].last_node.first;
j = visited[i][j].last_node.second;
}
//So now in path we have the start and end nodes of every edge on the path
int siguiente_x = path.back().first;
int siguiente_y = path.back().second;
debug_camino(path);
return direccion_contiguos(ox,oy,siguiente_x,siguiente_y);
}
And direccion_contiguos (contiguous direction / relative direction in english) implementation:
//Returns the RELATIVE direction to go if we want to go from (ox, oy) to (dx, dy) being these two contiguous positions, that is, (dx, dy) is in Up, Down, Left or Right with respect to (ox, oy) IT WORKS FINE, NOTHING WRONG WITH THIS DON'T TOUCH
Dir direccion_contiguos (const int& ox, const int& oy, const int& dx, const int& dy) {
Pos o (ox,oy);
Pos d (dx,dy);
if (o + Up == d) {
return Up;
}
else if (o + Down == d){
return Down;
}
else if (o + Left == d) {
return Left;
}
else if (o + Right == d) {
return Right;
}
return Down;
}
So now in visited, we have the information to reconstruct the path, in fact I debuged it (it's kinda messy i know, sorry), in a visual way so this is what I got for a Player in origen = (7,10) and bazooka at position = (4,11):
[Imgur link of the Visual representation of the Matrix for reconstructing the path from origin to bazooka][1]
To read this image, at the top and left there are the coordinates of every cell of the visited matrix, the ones with green font, are the ones that have been visited, and they store THE NEXT cell/vertex of the path, and the ones with (-1,-1) are the ones that have not been visited by the BFS algorithm and thus they don't have any previous node and are in white.
So, NICE! It seems to work, at least the visited matrix.
My problem is when I debug the vector of edges/directions of the graph/grid, this is what I used in the example of the image:
void debug_camino(const vector <pair<int,int>>& v) {
cerr << "----------------------CAMINO-------------------- DEBUG_CAMINO" << endl;
for (long unsigned int i = 0; i < v.size(); ++i) {
cerr << "(" << v[i].first << "," << v[i].second << "),";
}
cerr << endl;
}
And when I executed the program, this is the path that I got with debug_camino():
If you see the image attached you can see that that's almost the path but not quite yet.
(4,12),(4,13),(4,14),(3,15),(3,16),(4,16),(5,16),(6,16),(7,15),(7,14),(7,13),(7,12),(7,11)
These ones bolded are not real (even valid because they are diagonal moves) reconstructions of the path and I don't really know WHY this is happening, but it's provoking my player to not following right path, and I want to fix the error and I'm kinda desperate because I don't really know where the error is and I've been trying for days :( ! I hope somebody can help me with this. Thanks for reading all this and sorry if the code is in some parts in Spanish or if it's not all that readable.
[1]: https://i.stack.imgur.com/vZ2Go.png
Okay, I actually managed to fix this error, i was overwriting the i variable so that was causing the error.
//This function is made to reconstruct the path from where we found de bazooka (x,y) to where our player is (ox,oy), whe are only interested in the next position of because this is run each frame, so we are constantly refreshing the state of the map.
Dir reconstruir_camino(const vector < vector <TrackingBFS> >& visited, const int& x, const int& y, const int& ox, const int& oy) {
//In v we save the pair of coordinates of our path, this was done only for debuging and in debug_matriz_tracking(visited) is also only for debuging
vector <pair<int,int>> path;
debug_matriz_tracking(visited);
//
int i = visited[x][y].last_node.first;
int j = visited[x][y].last_node.second;
while (not (i == ox and j == oy)) { //While the next node is not iqual as the original node we started de search (The one where our player is)
path.push_back(make_pair(i,j)); //Just for debuging
**i** = visited[i][j].last_node.first;
j = visited[**i**][j].last_node.second;
}
//So now in path we have the start and end nodes of every edge on the path
int siguiente_x = path.back().first;
int siguiente_y = path.back().second;
debug_camino(path);
return direccion_contiguos(ox,oy,siguiente_x,siguiente_y);
}
It's already fixed
Related
I have been working on a two dimensional Divide and Conquer algorithm written in c++, a language I am rather new to. I used this page to base my algorithm on. The Y-axis is inverted in my example images.
I create a grid with a chosen width and height. The program then selects a random coordinate within each cell, these are the points for the Delaunay Triangulation.
The algorithm works as follows
Sort the coordinates in ascending order on the x-axis (similar x values; sort on y)
Divide into subgroups, until each group contains no more than 3 coordinates. Connect each coordinate with a line (I call them 'Edges'). This is in a binary tree structure. The Leaves contain a vector of these edges.
Recursively merge the leaves upwards
# Select the starting edge: select the coordinates, L and R, with the lowest y value from the left and right subgroup. If the edge L-R does not intersect with any edges of either group: select that edge. Else, select the next coordinate. (If the L-R edge intersects with an edge from the left subgroup select the next left coordinate. Ditto with the right group)
Based on the starting edge ('base' in my code) select the next candidate from both groups, left_C and right_C (the following is the left subgroup selection process, the right subgroup is the same but only mirrored):
Select the neighboring coordinates: all coordinates that have a direct connection to the base edge
Sort the neighbors by increasing clockwise angles.
# The candidate selection process: Select first (i = 0) coordinate as the candidate C, the second (i + 1) as the next candidate nC.
Check if the angle between the L-R and C is bigger than 180° or pi. If so: no candidate is selected, else move on to the next condition.
& Check if, the nC is within the circumcircle defined by triangle L-R-C (distance between circumcircle center and nC < radius circumcircle). delete the edge defined by L-C. nC becomes the new candidate C, the candidate with the next highest angle becomes nC, repeat check.
return candidate at the position i
If neither left nor right groups return a candidate, then the merge is complete.
Else if only one group returns a candidate, obviously that is the to-be-used candidate
Else both subgroups return a candidate, then select the candidate where the circumcircle defined by it and the base edge does not contain the other.
A triangle is created using the selected candidate and the base edge: an edge is created between the candidate and base-coordinate from the other subgroup ( eg. candidate of the left-subgroup: R-left_C ). This becomes the base edge for the next iteration.
iterate until the merge is complete (neither subgroup offers a candidate)
And so the tree is merged recursively.
So TL;DR.
The issue that I am having is two steps of the D&C algorithm coming into conflict. the two are the selection of the starting base edge (see step #) and the selection of the candidate coordinates (see step # and &)
The bug (read: feature) occurs with this orientation of the subgroups:
Animated Gif
It skips one of the coordinates of the right group in the first merger. This is because the algorithm specifies that you take the lowest y values of both groups that do not intersect with any other edges as the base edge. Then all the right group candidates, which are attached to the base edge, are selected with their counter-clockwise angle, the left with the clockwise angles. This skips one coordinate. leading to the following result.
I have no idea how to solve this.
Here is my code:
triangulate():
Node* Branch::triangulate() {
if (l != nullptr && r == nullptr) {
return l;
}
else {
// recursive merge
Node* left = l->triangulate();
Node* right = r->triangulate();
vector<Edge> left_edges = left->getEdges();
vector<Edge> right_edges = right->getEdges();
Edge base = selectBase(left_edges, right_edges);
bool merge = true;
vector<Edge> mergedEdges;
vector<Edge> newEdges;
newEdges.push_back(base);
while (merge) {
Coordinate left_candidate = selectCandidateFromSubgroup(left_edges, base, true);
Coordinate right_candidate = selectCandidateFromSubgroup(right_edges, base, false);
Coordinate null(DBL_MIN, DBL_MIN);
if (left_candidate == null && right_candidate == null) {
merge = false;
}
else {
Edge new_base;
if (right_candidate == null) {
new_base = Edge(left_candidate, *base.p2());
}
else if (left_candidate == null) {
new_base = Edge(*base.p1(), right_candidate);
}
else {
double left_a, left_b, left_c,
base_a, base_b, base_c;
functionFromEdge(base, base_a, base_b, base_c);
Edge left_edge(left_candidate, *base.p2());
functionFromEdge(left_edge, left_a, left_b, left_c);
perpendicularBisector(base, base_a, base_b, base_c);
perpendicularBisector(left_edge, left_a, left_b, left_c);
Coordinate left_circumcircle_center = intersection(left_a, left_b, left_c,
base_a, base_b, base_c);
new_base = (Coordinate::distance(left_circumcircle_center, left_candidate)
< Coordinate::distance(left_circumcircle_center, right_candidate))
? left_edge : Edge(*base.p1(), right_candidate);
}
newEdges.push_back(new_base);
base = new_base;
}
}
mergedEdges.reserve(newEdges.size() + left_edges.size() + right_edges.size());
for (Edge e : left_edges) {
mergedEdges.push_back(e);
}
for (Edge e : right_edges) {
mergedEdges.push_back(e);
}
for (Edge e : newEdges) {
mergedEdges.push_back(e);
}
return new Leaf(mergedEdges,i);
}
}
selectBase():
vector<Coordinate> left_points = extractCoordinates(left);
vector<Coordinate> right_points = extractCoordinates(right);
Edge* out = nullptr;
bool found = false;
int i = 0;
int j = 0;
while (!found) {
Coordinate l = left_points.at(i), r = right_points.at(j);
Edge base(l, r);
out = &base;
for (Edge e : left) {
if (out!= nullptr && e.intersects(base)) {
//cout << e << " intersects with " << *out << " i++" << endl;
i++;
out = nullptr;
}
if (out != nullptr && left_points.size() ==2 && (*e.p1() == *base.p1() || *e.p2() == *base.p1())) {
Coordinate other = (*e.p1() == *base.p1()) ? *e.p2() : *e.p1();
if (Angle::calcClockAngle(l, r, other, true).rad() > M_PI) {
out = nullptr; i++;
}
}
}
for (Edge e : right) {
if (out != nullptr && e.intersects(base)) {
//cout << e << " intersects with " << *out << " j++" << endl;
j++;
out = nullptr;
}
if (out != nullptr && right_points.size() == 2 && (*e.p1() == *base.p2() || *e.p2() == *base.p2())) {
Coordinate other = (*e.p1() == *base.p2()) ? *e.p2() : *e.p1();
if (Angle::calcClockAngle(r, l, other, false).rad() > M_PI) {
out = nullptr; j++;
}
}
}
if (out != nullptr) found = true;
}
return Edge(left_points.at(i), right_points.at(j));
}
In the above function I tried to make a check that checks if there is a skipped point, by checking in subgroups of size 2 if there is an angle larger than pi.
It worked for smaller groups. But the issue still occurs.
selectCandidateFromSubgroup:
Coordinate selectCandidateFromSubgroup(vector<Edge>& es, Edge& base, bool left) {
// select neighboring edges from base
Coordinate* shared_point = (left)? base.p1() : base.p2();
Coordinate* other_point = (left)? base.p2() : base.p1();
vector<Coordinate> candidates;
for (Edge e : es) {
if (*shared_point == *e.p2() || *shared_point == *e.p1()) {
Coordinate c = (*shared_point == *e.p2()) ? *e.p1() : *e.p2();
candidates.push_back(c);
}
}
if (candidates.size() != 0) {
algorithm::quicksort(candidates, 0, candidates.size() -1,
[&shared_point,&other_point,&left](Coordinate a, Coordinate b) {
double angle = Angle::calcClockAngle(*shared_point, *other_point, a, left).getDegrees();
double angle2 = Angle::calcClockAngle(*shared_point, *other_point, b, left).getDegrees();
return (angle < angle2 || (angle == angle2 && a.y() < b.y()));
});
double base_a, base_b, base_c;
functionFromEdge(base, base_a, base_b, base_c);
perpendicularBisector(base, base_a, base_b, base_c);
Coordinate* candidate=nullptr;
Coordinate* next_candidate = nullptr;
int i = 0;
bool checking_candidates = true;
while (checking_candidates) {
candidate = &candidates.at(i);
double angle = Angle::calcClockAngle(*shared_point,
*other_point,
*candidate,
left).rad();
if (angle < M_PI) {
Edge candidate_edge;
int cei = 0;
int j = 0;
for (Edge e : es) {
if ((*e.p1() == *shared_point && *e.p2() == *candidate) || (*e.p2() == *shared_point && *e.p1() == *candidate)) {
candidate_edge = e;
cei = j;
break;
}
j++;
}
if (i + 1 == candidates.size()) return *candidate;
next_candidate= &candidates.at(i+1);
double candidate_edge_a, candidate_edge_b, candidate_edge_c;
functionFromEdge(candidate_edge, candidate_edge_a,
candidate_edge_b, candidate_edge_c);
perpendicularBisector(candidate_edge, candidate_edge_a,
candidate_edge_b, candidate_edge_c);
Coordinate circumcicle_center = intersection(base_a, base_b, base_c,
candidate_edge_a, candidate_edge_b, candidate_edge_c);
double radius = Coordinate::distance(circumcicle_center, *shared_point);
double distance_center_nCandidate = Coordinate::distance(circumcicle_center, *next_candidate);
if (radius < distance_center_nCandidate) {
return *candidate;
}
else {
i++;
es.erase(es.begin() + cei);
}
}
else {
return Coordinate(DBL_MIN, DBL_MIN);
}
}
}
return Coordinate(DBL_MIN, DBL_MIN);
}
extractCoordinates(vector) extracts and sorts all coordiates on the y axis ascendingly.
functionFromEdge(Edge,double a,double b,double c) takes an edge and makes a functional representation of the lines like 'ax+by=c', which is used by
perpendicularBisector(Edge, double a, double b, double c) converts the a, b, and c values to represent the function of the perpendicular bisector of the Edge so that the algorithm can calulate the
intersection(double a1, double b1, double c1, double a2, double b2, double c2) of two functions.
the calcClockAngle(Coordinate a, Coordinate b, Coordinate c, bool clockwise) calulates the angle in a bewteen the vectors |AB| and |AC|, depending on the clockwise boolean, it calculates the cloclwise or counter clockwise angle.
If I forgot something please let me know.
Sorry for the long question. but I am seriously at a loss here.
I get results like this:
Given a grid of size R X C and startPosition and endPosition of a person in a grid composed of zero and one. Now I want to find path from start position to end position, and also trace the marked path on grid by labelling them 2. If path is not possible, I need to tell that path is not possible. So I had written this logic :
vector<vector<int>> solution;
void printSolution(vector<vector<int>> grid)
{
for (int i=0;i<grid.size();i++)
{
for (int j=0;j<grid[i].size();j++)
cout<<grid[i][j]<<" ";
cout<<endl;
}
}
bool isSafe(vector<vector<int>> grid, pair<int,int> position)
{
if(position.first >= 0 && position.first < grid.size() &&
position.second >= 0 && position.second < grid[0].size() &&
grid[position.first][position.second] == 0)
return true;
return false;
}
bool checkPath(vector<vector<int>> grid,pair<int,int> currentPosition,pair<int,int> endPosition){
if(currentPosition == endPosition)
{
solution[currentPosition.first][currentPosition.second] = 2;
return true;
}
if(isSafe(grid,currentPosition) == true)
{
solution[currentPosition.first][currentPosition.second] = 2;
if (checkPath(grid, make_pair(currentPosition.first+1,currentPosition.second),endPosition) == true)
return true;
if (checkPath(grid, make_pair(currentPosition.first-1,currentPosition.second),endPosition) == true)
return true;
if (checkPath(grid, make_pair(currentPosition.first,currentPosition.second+1),endPosition) == true)
return true;
if (checkPath(grid, make_pair(currentPosition.first,currentPosition.second-1),endPosition) == true)
return true;
solution[currentPosition.first][currentPosition.second] = 0;
return false;
}
return false;
}
bool solver(vector<vector<int>> grid,pair<int,int> startPosition,pair<int,int> endPosition,int R,int C){
solution.resize(R,vector<int>(C));
bool isPath = checkPath(grid,startPosition,endPosition);
printSolution(solution);
if(isPath==false){
cout<<"No Path Found"<<endl;
return false;
}
else{
cout<<"Path Found"<<endl;
return true;
}
}
This code is giving segmentation error. Please help to find it. I am stuck for almost a whole day to find where it is present.
So help me correct the logic of code. Assume I have following fields :
int R,C;
int grid[R][C];
pair<int,int> startPosition;
pair<int,int> endPosition;
This recursion is running infinite. i Checked for a simple case with startPosition as (1,0) and endPosition as (2,2) , R=3 and C=3 and grid as :
1 1 1
0 0 1
1 0 0
First, I tried with BFS, then start to make recursive solution.
When implementing a BFS path finding algorithm, it helps to have a shadow array that keeps track of the distance from the origin for all visited nodes.
int distance[R][C];
Initially, set all of the distances to -1 to indicate that the location has not been visited. Then set the distance at the origin to 0, and push the origin onto the stack.
While the stack is not empty, pop an item from the stack. For each adjacent location (that hasn't been visited) set the distance for that location, and push that location.
When you reach the end position, you can find the path by working backwards. Knowing the distance at the end position, the previous point on the path is an adjacent position that has been visited, and has a lower distance.
If I have the following graph:
Marisa Mariah
\ /
Mary---Maria---Marian---Maryanne
|
Marley--Marla
How should be Depth First Search function be implemented such that I get the output if "Mary" is my start point ?
Mary
Maria
Marisa
Mariah
Marian
Maryanne
Marla
Merley
I do realize that the number of spaces equal to depth of the vertex( name ) but I don't how to code that. Following is my function:
void DFS(Graph g, Vertex origin)
{
stack<Vertex> vertexStack;
vertexStack.push(origin);
Vertex currentVertex;
int currentDepth = 0;
while( ! vertexStack.empty() )
{
currentVertex = vertexStack.top();
vertexStack.pop();
if(currentVertex.visited == false)
{
cout << currentVertex.name << endl;
currentVertex.visited = true;
for(int i = 0; i < currentVertex.adjacencyList.size(); i++)
vertexStack.push(currentVertex.adjacencyList[i]);
}
}
}
Thanks for any help !
Just store the node and its depth your stack:
std::stack<std::pair<Vertex, int>> vertexStack;
vertexStack.push(std::make_pair(origin, 0));
// ...
std::pair<Vertex, int> current = vertexStack.top();
Vertex currentVertex = current.first;
int depth = current.second;
If you want to get fancy, you can extra the two values using std::tie():
Vertex currentVertex;
int depth;
std::tie(currentVertex, depth) = vertexStack.top();
With knowing the depth you'd just indent the output appropriately.
The current size of your stack is, BTW, unnecessarily deep! I think for a complete graph it may contain O(N * N) elements (more precisely, (N-1) * (N-2)). The problem is that you push many nodes which may get visited.
Assuming using an implicit stack (i.e., recursion) is out of question (it won't work for large graphs as you may get a stack overflow), the proper way to implement a depth first search would be:
push the current node and edge on the stack
mark the top node visited and print it, using the stack depth as indentation
if there is no node
if the top nodes contains an unvisited node (increment the edge iterator until such a node is found) go to 1.
otherwise (the edge iterator reached the end) remove the top node and go to 3.
In code this would look something like this:
std::stack<std::pair<Node, int> > stack;
stack.push(std::make_pair(origin, 0));
while (!stack.empty()) {
std::pair<Node, int>& top = stack.top();
for (; top.second < top.first.adjacencyList.size(); ++top.second) {
Node& adjacent = top.first.adjacencyList[top.second];
if (!adjacent.visited) {
adjacent.visted = true;
stack.push(std::make_pair(adjacent, 0));
print(adjacent, stack.size());
break;
}
}
if (stack.top().first.adjacencyList.size() == stack.top().second) {
stack.pop();
}
}
Let Rep(Tree) be the representation of the tree Tree. Then, Rep(Tree) looks like this:
Root
<Rep(Subtree rooted at node 1)>
<Rep(Subtree rooted at node 2)>
.
.
.
So, have your dfs function simply return the representation of the subtree rooted at that node and modify this value accordingly. Alternately, just tell every dfs call to print the representation of the tree rooted at that node but pass it the current depth. Here's an example implementation of the latter approach.
void PrintRep(const Graph& g, Vertex current, int depth)
{
cout << std::string(' ', 2*depth) << current.name << endl;
current.visited = true;
for(int i = 0; i < current.adjacencyList.size(); i++)
if(current.adjacencyList[i].visited == false)
PrintRep(g, current.adjacencyList[i], depth+1);
}
You would call this function with with your origin and depth 0 like this:
PrintRep(g, origin, 0);
I am making a pathfinding AI based off a waypoint graph using DirectX. I have a car with a forwards vector to tell me which direction it's facing. The car will always move in the XZ plane, and never change it's Y coordinate. I want to tell the car to turn right if the next waypoint is on it's right, and the opposite for left. This is the current code that I have for the pathfinding function, and what I'm currently doing to check right (which is incorrect)
void Car::Pathfinding(XMVECTOR forwards)
{
int nextNode;
if(currentNode + 1 > waypoints->size())
nextNode = 0;
else
nextNode = currentNode + 1;
Accelerate();
if(onRightOf(forwards, waypoints->at(nextNode)))
TurnLeft();
else if(onLeftOf(forwards, waypoints->at(nextNode)))
TurnRight();
//SquaredDisplacement is just Pythagoras's theorem without sqrt
if(squaredDisplacement(waypoints->at(nextNode)) < 100)
currentNode = nextNode;
}
bool Car::onRightOf(XMVECTOR forwards, XMFLOAT3 b)
{
forwards = XMVector3AngleBetweenVectors(forwards, XMLoadFloat3(&b));
XMStoreFloat3(&b, forwards);
if(b.x <= XM_PI / 4) //So it doesn't have to be exact
//The onLeftOf has a >= instead of <=
return false;
return true;
}
I'm aware that XMVector3AngleBetweenVectors will always return positive, although it would be stellar if it didn't.
Let's vector
CB = b - CurrentPosition
(I don't see a variable for curr. pos. in your code)
then find sign of expression (it is Y-component of vector product, like cross-product)
forwards.X * CB.z -forwards.X * CB.X
If it is positive, B is left to current path, if negative - to right
I'm developing a structure that is like a binary tree but generalized across dimensions so you can set whether it is a binary tree, quadtree, octree, etc by setting the dimension parameter during initialization.
Here is the definition of it:
template <uint Dimension, typename StateType>
class NDTree {
public:
std::array<NDTree*, cexp::pow(2, Dimension)> * nodes;
NDTree * parent;
StateType state;
char position; //position in parents node list
bool leaf;
NDTree const &operator[](const int i) const
{
return (*(*nodes)[i]);
}
NDTree &operator[](const int i)
{
return (*(*nodes)[i]);
}
}
So, to initialize it- I set a dimension and then subdivide. I am going for a quadtree of depth 2 for illustration here:
const uint Dimension = 2;
NDTree<Dimension, char> tree;
tree.subdivide();
for(int i=0; i<tree.size(); i++)
tree[i].subdivide();
for(int y=0; y<cexp::pow(2, Dimension); y++) {
for(int x=0; x<cexp::pow(2, Dimension); x++) {
tree[y][x].state = ((y)*10)+(x);
}
}
std::cout << tree << std::endl;
This will result in a quadtree, the state of each of the values are initialized to [0-4][0-4].
([{0}{1}{2}{3}][{10}{11}{12}{13}][{20}{21}{22}{23}][{30}{31}{32}{33}])
I am having trouble finding adjacent nodes from any piece. What it needs to do is take a direction and then (if necessary) traverse up the tree if the direction goes off of the edge of the nodes parent (e.g. if we were on the bottom right of the quadtree square and we needed to get the piece to the right of it). My algorithm returns bogus values.
Here is how the arrays are laid out:
And here are the structures necessary to know for it:
This just holds the direction for items.
enum orientation : signed int {LEFT = -1, CENTER = 0, RIGHT = 1};
This holds a direction and whether or not to go deeper.
template <uint Dimension>
struct TraversalHelper {
std::array<orientation, Dimension> way;
bool deeper;
};
node_orientation_table holds the orientations in the structure. So in 2d, 0 0 refers to the top left square (or left left square).
[[LEFT, LEFT], [RIGHT, LEFT], [LEFT, RIGHT], [RIGHT, RIGHT]]
And the function getPositionFromOrientation would take LEFT, LEFT and return 0. It is just basically the opposite of the node_orientation_table above.
TraversalHelper<Dimension> traverse(const std::array<orientation, Dimension> dir, const std::array<orientation, Dimension> cmp) const
{
TraversalHelper<Dimension> depth;
for(uint d=0; d < Dimension; ++d) {
switch(dir[d]) {
case CENTER:
depth.way[d] = CENTER;
goto cont;
case LEFT:
if(cmp[d] == RIGHT) {
depth.way[d] = LEFT;
} else {
depth.way[d] = RIGHT;
depth.deeper = true;
}
break;
case RIGHT:
if(cmp[d] == LEFT) {
depth.way[d] = RIGHT;
} else {
depth.way[d] = LEFT;
depth.deeper = true;
}
break;
}
cont:
continue;
}
return depth;
}
std::array<orientation, Dimension> uncenter(const std::array<orientation, Dimension> dir, const std::array<orientation, Dimension> cmp) const
{
std::array<orientation, Dimension> way;
for(uint d=0; d < Dimension; ++d)
way[d] = (dir[d] == CENTER) ? cmp[d] : dir[d];
return way;
}
NDTree * getAdjacentNode(const std::array<orientation, Dimension> direction) const
{
//our first traversal pass
TraversalHelper<Dimension> pass = traverse(direction, node_orientation_table[position]);
//if we are lucky the direction results in one of our siblings
if(!pass.deeper)
return (*(*parent).nodes)[getPositionFromOrientation<Dimension>(pass.way)];
std::vector<std::array<orientation, Dimension>> up; //holds our directions for going up the tree
std::vector<std::array<orientation, Dimension>> down; //holds our directions for going down
NDTree<Dimension, StateType> * tp = parent; //tp is our tree pointer
up.push_back(pass.way); //initialize with our first pass we did above
while(true) {
//continue going up as long as it takes, baby
pass = traverse(up.back(), node_orientation_table[tp->position]);
std::cout << pass.way << " :: " << uncenter(pass.way, node_orientation_table[tp->position]) << std::endl;
if(!pass.deeper) //we've reached necessary top
break;
up.push_back(pass.way);
//if we don't have any parent we must explode upwards
if(tp->parent == nullptr)
tp->reverseBirth(tp->position);
tp = tp->parent;
}
//line break ups and downs
std::cout << std::endl;
//traverse upwards combining the matrices to get our actual position in cube
tp = const_cast<NDTree *>(this);
for(int i=1; i<up.size(); i++) {
std::cout << up[i] << " :: " << uncenter(up[i], node_orientation_table[tp->position]) << std::endl;
down.push_back(uncenter(up[i], node_orientation_table[tp->parent->position]));
tp = tp->parent;
}
//make our way back down (tp is still set to upmost parent from above)
for(const auto & i : down) {
int pos = 0; //we need to get the position from an orientation list
for(int d=0; d<i.size(); d++)
if(i[d] == RIGHT)
pos += cexp::pow(2, d); //consider left as 0 and right as 1 << dimension
//grab the child of treepointer via position we just calculated
tp = (*(*tp).nodes)[pos];
}
return tp;
}
For an example of this:
std::array<orientation, Dimension> direction;
direction[0] = LEFT; //x
direction[1] = CENTER; //y
NDTree<Dimension> * result = tree[3][0]->getAdjacentNode(direction);
This should grab the top right square within bottom left square, e.g. tree[2][1] which would have a value of 21 if we read its state. Which works since my last edit (algorithm is modified). Still, however, many queries do not return correct results.
//Should return tree[3][1], instead it gives back tree[2][3]
NDTree<Dimension, char> * result = tree[1][2].getAdjacentNode({ RIGHT, RIGHT });
//Should return tree[1][3], instead it gives back tree[0][3]
NDTree<Dimension, char> * result = tree[3][0].getAdjacentNode({ RIGHT, LEFT });
There are more examples of incorrect behavior such as tree[0][0](LEFT, LEFT), but many others work correctly.
Here is the folder of the git repo I am working from with this. Just run g++ -std=c++11 main.cpp from that directory if it is necessary.
here is one property you can try to exploit:
consider just the 4 nodes:
00 01
10 11
Any node can have up to 4 neighbor nodes; two will exist in the same structure (larger square) and you have to look for the other two in neighboring structures.
Let's focus on identifying the neighbors which are in the same structure: the neighbors for 00 are 01 and 10; the neighbors for 11 are 01 and 10. Notice that only one bit differs between neighbor nodes and that neighbors can be classified in horizontal and vertical. SO
00 - 01 00 - 01 //horizontal neighbors
| |
10 11 //vertical neighbors
Notice how flipping the MSB gets the vertical neighbor and flipping the LSB gets the horizontal node? Let's have a close look:
MSB: 0 -> 1 gets the node directly below
1 -> 0 sets the node directly above
LSB: 0 -> 1 gets the node to the right
1 -> 0 gets the node to the left
So now we can determine the node's in each direction assuming they exist in the same substructure. What about the node to the left of 00 or above 10?? According to the logic so far if you want a horizontal neighbor you should flip the LSB; but flipping it would retrieve 10 ( the node to the right). So let's add a new rule, for impossible operations:
you can't go left for x0 ,
you can't go right for x1,
you can't go up for 0x,
you can't go down for 1x
*impossible operations refers to operations within the same structure.
Let's look at the bigger picture which are the up and left neighbors for 00? if we go left for 00 of strucutre 0 (S0), we should end up with 01 of(S1), and if we go up we end up with node 10 of S(2). Notice that they are basically the same horizontal/ veritical neighbor values form S(0) only they are in different structures. So basically if we figure out how to jump from one structure to another we have an algorithm.
Let's go back to our example: going up from node 00 (S0). We should end up in S2; so again 00->10 flipping the MSB. So if we apply the same algorithm we use within the structure we should be fine.
Bottom line:
valid transition within a strucutres
MSB 0, go down
1, go up
LSB 0, go right
1, go left
for invalid transitions (like MSB 0, go up)
determine the neighbor structure by flipping the MSB for vertical and LSB for vertical
and get the neighbor you are looking for by transforming a illegal move in structure A
into a legal one in strucutre B-> S0: MSB 0 up, becomes S2:MSB 0 down.
I hope this idea is explicit enough
Check out this answer for neighbor search in octrees: https://stackoverflow.com/a/21513573/3146587. Basically, you need to record in the nodes the traversal from the root to the node and manipulate this information to generate the required traversal to reach the adjacent nodes.
The simplest answer I can think of is to get back your node from the root of your tree.
Each cell can be assigned a coordinate mapping to the deepest nodes of your tree. In your example, the (x,y) coordinates would range from 0 to 2dimension-1 i.e. 0 to 3.
First, compute the coordinate of the neighbour with whatever algorithm you like (for instance, decide if a right move off the edge should wrap to the 1st cell of the same row, go down to the next row or stay in place).
Then, feed the new coordinates to your regular search function. It will return the neighbour cell in dimension steps.
You can optimize that by looking at the binary value of the coordinates. Basically, the rank of the most significant bit of difference tells you how many levels up you should go.
For instance, let's take a quadtree of depth 4. Coordinates range from 0 to 15.
Assume we go left from cell number 5 (0101b). The new coordinate is 4 (0100b). The most significant bit changed is bit 0, which means you can find the neighbour in the current block.
Now if you go right, the new coordinate is 6 (0110b), so the change is affecting bit 1, which means you have to go up one level to access your cell.
All this being said, the computation time and volume of code needed to use such tricks seems hardly worth the effort to me.