Trying to implement A* pathfinding for a small game.
Running into some strange bugs.
Code:
class PathNode
{
private:
const PathNode* parent;
GameObject position;
int g;
int h;
int f;
public:
PathNode(GameObject _position, int _g, int _h, const PathNode* _parent = nullptr) {
parent = _parent;
position = _position;
g = _g; // distance between the start node and the current node
h = _h; // distance between the current node and the end node
f = g + h; // Total cost of the node (g + h)
}
PathNode(const PathNode& other) {
position = other.position;
g = other.g; // distance between the start node and the current node
h = other.h; // distance between the current node and the end node
f = other.f; // Total cost of the node (g + h)
parent = other.parent;
}
bool operator==(const PathNode& other) const {
return (this->position == other.position);
}
bool operator!=(const PathNode& other) const {
return !operator==(other);
}
// GETTERS:
int getg() const;
int geth() const;
int getf() const;
const PathNode* getParent() const;
const GameObject getPosition() const;
// SETTERS:
void setg(int newG);
void seth(int newH);
void setf(int newF);
void setParent(const PathNode* newParent);
void setPosition(GameObject newPos);
};
And The PathFinding code:
char Ghost::aStar(char board[BoardYSize][BoardXSize], GameObject end) {
int startEndDistance = pow((getX() - end.getX()), 2) + pow((getY() - end.getY()), 2);
PathNode startNode(getPosition(), 0, startEndDistance); // in the ctor of PathNode, the default values of Parent == nullptr.
PathNode endNode(end, startEndDistance, 0); // in the ctor of PathNode, the default values of Parent == nullptr.
// Initialize both Open and Closed Lists:
vector<PathNode> openList;
vector<PathNode> closedList;
openList.reserve(500);
closedList.reserve(500);
// Add the StartNode:
openList.push_back(startNode);
// Loop until you find the end:
while (openList.size() > 0) {
// Get the current Node.
PathNode currentNode(openList[0]); // Default copy ctor.
int index = 0;
for (int i = 0; i < openList.size(); i++) {
if (openList[i].getf() < currentNode.getf()) {
index = i;
currentNode = openList[i];
}
}
// Pop Current off openList, and add to closedList:
openList.erase(openList.begin() + index);
closedList.push_back(currentNode);
// If found the end, return:
if (currentNode == endNode) {
// return direction of the first move of the path:
const PathNode* nextNode = ¤tNode;
const PathNode* firstMoveNode = nullptr;
while (*nextNode != startNode) {
firstMoveNode = nextNode;
nextNode = nextNode->getParent();
}
GameObject newPos = firstMoveNode->getPosition();
GameObject currentPos = startNode.getPosition();
if (newPos.getX() - currentPos.getX() == 0 && newPos.getY() - currentPos.getY() == 1)
return 'w';
else if (newPos.getX() - currentPos.getX() == 0 && newPos.getY() - currentPos.getY() == -1)
return 'x';
else if (newPos.getX() - currentPos.getX() == 1 && newPos.getY() - currentPos.getY() == 0)
return 'd';
else if (newPos.getX() - currentPos.getX() == -1 && newPos.getY() - currentPos.getY() == 0)
return 'a';
else
return 's';
}
// Generate Children:
vector <PathNode> children;
// temp is used to generate the children of the currentNode.
GameObject temp[4];
temp[0] = GameObject(0, 1); // Position above currentNode
temp[1] = GameObject(1, 0); // Position right of currentNode
temp[2] = GameObject(0, -1); // Position left of currentNode
temp[3] = GameObject(-1, 0); // Position below currentNode
for (int i = 0 ; i < 4; i++) {
// Get Node Position:
GameObject nodePosition(currentNode.getPosition() + temp[i]);
// Make Sure within Range of board & Not a Wall (walkable terrain):
if (!checkLegalMove(nodePosition, board))
continue;
PathNode newNode(nodePosition, 0, 0, ¤tNode);
children.push_back(newNode);
}
// Loop through Children:
for (auto child : children) {
bool addChild = true;
// Child is on the closedList:
for (auto closedChild : closedList) {
if (child == closedChild) {
addChild = false;
continue;
}
}
if (!addChild) continue;
// Create the G H and F values:
child.setg(currentNode.getg() + 1);
child.seth(pow((child.getPosition().getX() - endNode.getPosition().getX()), 2) + pow((child.getPosition().getY() - endNode.getPosition().getY()), 2));
child.setf(child.getg() + child.geth());
// Child is already in the openList:
for (auto openNode : openList) {
if (child == openNode && child.getg() > openNode.getg()) {
addChild = false;
continue;
}
}
if (!addChild) continue;
openList.push_back(child);
}
}
return 's';
}
My goal is to use A* to find a route and simply return the direction of the first position to travel to.
The Problem I run into:
The problem is that when the condition of currentNode == endNode is met (reached the goal).
Every PathNode's parent is simply a pointing to itself (according to the debugger).
I don't understand why it's happening, I assume The current pointer gets destroyed at some point in time but i can't figure out why it's happening.
You're storing a pointer to a local variable, so every node will have that same local variable as the parent (and any access to it would be Undefined Behavior once it has been destroyed). This happens in the PathNode newNode(nodePosition, 0, 0, ¤tNode); declaration.
You might want to change currentNode to be a pointer, rather than an object, and have it point to the parent object in the closedList vector. This would allow you to update what node it points at without having to make copies. You've reserved enough space for 500 entries so you won't have a problem with dangling pointers until you add the 501st element to the vector.
Related
Here is my code i've been working on. I've scraped off all the errors from the code. But i cant get the code to run correctly and the application either explodes or doesn't atleast visualise the astar.
#include <SFML/Graphics.hpp>
#include <queue>
#include <unordered_map>
#include <cmath>
#include <vector>
const int WIDTH = 10;
const int HEIGHT = 10;
struct Node
{
int x;
int y;
mutable float gScore;
mutable float fScore;
mutable const Node* parent;
Node(int _x, int _y) : x(_x), y(_y), gScore(0), fScore(0), parent(nullptr) {}
bool operator==(const Node& other) const
{
return x == other.x && y == other.y;
}
};
// Hash function for nodes, needed for storing them in an unordered_map
struct NodeHash
{
std::size_t operator()(const Node& node) const
{
return std::hash<int>()(node.x) ^ std::hash<int>()(node.y);
}
};
// Overload the ">" operator for nodes, needed for using them in a priority queue
struct NodeGreater
{
bool operator()(const Node& left, const Node& right) const
{
return left.fScore > right.fScore;
}
};
// Represents a 2D grid
class Grid
{
public:
Grid()
{
// Initialize the grid with all nodes as walkable
for (int x = 0; x < WIDTH; x++)
{
for (int y = 0; y < HEIGHT; y++)
{
nodes[x][y] = true;
}
}
}
// Set the walkability of a node
void setNode(int x, int y, bool walkable)
{
nodes[x][y] = walkable;
}
// Check if a node is walkable
bool isNodeWalkable(int x, int y)
{
return nodes[x][y];
}
private:
bool nodes[WIDTH][HEIGHT];
};
// Calculate the Euclidean distance between two nodes
float euclideanDistance(Node a, Node b)
{
int xDist = b.x - a.x;
int yDist = b.y - a.y;
return std::sqrt(xDist * xDist + yDist * yDist);
}
// Estimate the cost of reaching the end node from a given node
float heuristicCostEstimate(Node start, Node end)
{
return euclideanDistance(start, end);
}
// Get the neighbors of a given node
std::vector<Node> getNeighbors(Grid grid, Node node)
{
std::vector<Node> neighbors;
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
// Skip the current node
if (x == 0 && y == 0)
{
continue;
}
int neighborX = node.x + x;
int neighborY = node.y + y;
// Make sure the neighbor is within the bounds of the grid
if (neighborX >= 0 && neighborX < WIDTH && neighborY >= 0 && neighborY < HEIGHT)
{
// Make sure the neighbor is walkable
if (grid.isNodeWalkable(neighborX, neighborY))
{
Node neighbor = { neighborX, neighborY };
neighbors.push_back(neighbor);
}
}
}
}
return neighbors;
}
// Reconstruct the path from the end node to the start node
std::vector<Node> reconstructPath(Node end)
{
std::vector<Node> path;
Node current = end;
while (current.parent != nullptr)
{
path.push_back(current);
current = *current.parent;
}
path.push_back(current);
std::reverse(path.begin(), path.end());
return path;
}
// Run the A* algorithm to find the shortest path from the start to the end node
std::vector<Node> aStar(Grid grid, Node start, Node end)
{
// Initialize the open and closed sets
std::priority_queue<Node, std::vector<Node>, NodeGreater> openSet;
std::unordered_map<Node, bool, NodeHash> closedSet;
// Add the start node to the open set
openSet.push(start);
// Set the starting node's gScore to 0 and fScore to the estimated cost to the end node
start.gScore = 0;
start.fScore = heuristicCostEstimate(start, end);
// Keep searching until the open set is empty
while (!openSet.empty())
{
// Get the node in the open set with the lowest fScore
Node current = openSet.top();
openSet.pop();
// If the current node is the end node, we have found the shortest path
if (current == end)
{
return reconstructPath(current);
}
// Add the current node to the closed set
closedSet[current] = true;
// Get the neighbors of the current node
std::vector<Node> neighbors = getNeighbors(grid, current);
// Iterate through the neighbors
for (const Node& neighbor : neighbors)
{
// Skip the neighbor if it is already in the closed set
if (closedSet.count(neighbor) > 0)
{
continue;
}
// Calculate the tentative gScore for the neighbor
float tentativeGScore = current.gScore + euclideanDistance(current, neighbor);
bool neighborIsBetter = false;
bool neighborInOpenSet = false;
// Check if the neighbor is already in the open set
std::priority_queue<Node, std::vector<Node>, NodeGreater> tempOpenSet = openSet;
while (!tempOpenSet.empty())
{
Node tempNeighbor = tempOpenSet.top();
tempOpenSet.pop();
if (tempNeighbor == neighbor)
{
neighborInOpenSet = true;
break;
}
}
// Update the neighbor's scores and parent if the tentative gScore is lower than
if (tentativeGScore < neighbor.gScore)
{
neighborIsBetter = true;
neighbor.gScore = tentativeGScore;
neighbor.fScore = tentativeGScore + heuristicCostEstimate(neighbor, end);
}
if (!neighborInOpenSet || neighborIsBetter)
{
// Update the neighbor's parent to the current node
neighbor.parent = ¤t;
// Add the neighbor to the open set if it is not already there
if (!neighborInOpenSet)
{
openSet.push(neighbor);
}
}
}
}
// If we reach here, it means that we have not found a path to the end node
return std::vector<Node>();
}
int main()
{
// Create the window and grid
sf::RenderWindow window(sf::VideoMode(500, 500), "A*");
Grid grid;
// Set some nodes as unwalkable
grid.setNode(3, 3, false);
grid.setNode(3, 4, false);
grid.setNode(3, 5, false);
grid.setNode(4, 3, false);
// Set the start and end nodes
Node start = { 1, 1 };
Node end = { 8, 8 };
// Run the A* algorithm and get the path
std::vector<Node> path = aStar(grid, start, end);
// Main loop
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
// Draw the grid
for (int x = 0; x < WIDTH; x++)
{
for (int y = 0; y < HEIGHT; y++)
{
sf::RectangleShape rect(sf::Vector2f(50, 50));
rect.setPosition(x * 50, y * 50);
if (grid.isNodeWalkable(x, y))
{
rect.setFillColor(sf::Color::White);
}
else
{
rect.setFillColor(sf::Color::Black);
}
window.draw(rect);
}
}
// Draw the path
for (Node node : path)
{
sf::RectangleShape rect(sf::Vector2f(50, 50));
rect.setPosition(node.x * 50, node.y * 50);
rect.setFillColor(sf::Color::Green);
window.draw(rect);
}
// Draw the start and end nodes
sf::RectangleShape startRect(sf::Vector2f(50, 50));
startRect.setPosition(start.x * 50, start.y * 50);
startRect.setFillColor(sf::Color::Red);
window.draw(startRect);
sf::RectangleShape endRect(sf::Vector2f(50, 50));
endRect.setPosition(end.x * 50, end.y * 50);
endRect.setFillColor(sf::Color::Blue);
window.draw(endRect);
window.display();
}
return 0;
}
any idea why its doing this?
I've tried to debug the code and i cant seem to find the problem. Either the code doesn't run or it just shows the same thing
Feel free to point out my mistakes, I do not claim to be a good C++ programmer or even good w/ Data structures. I'm just looking to improve and learn upon my example.
This approach is not my own but the code is. I learned a concept and attempted to code it in C++.
The point here is to learn to build this without using libraries such as std::priority_queue. Thanks again!
#include <iostream>
#include <memory>
#include <vector>
#include <unordered_map>
#include <algorithm>
// directions of each node
enum
{
IS_LEFT = 1,
IS_RIGHT = 2
};
// (ADT) Priority Queue which uses a (DS) heap
using std::vector;
// Makes removal of any node anywhere within the heap O(log(n))
using std::unordered_map;
class BinaryStack
{
private:
// tracks the last node in heap
size_t last_index { 0 };
// the heap
vector<int> heap{ };
// the hashmap to track where any given node is at (required for node removal within a heap)
unordered_map<int, vector<size_t>> hashmap{ };
// swap any two values from two given vectors
void swapVectorValue(vector<size_t> &vec1, size_t val1, vector<size_t> &vec2, size_t val2)
{
vec1.erase(std::remove(vec1.begin(), vec1.end(), val1), vec1.end());
vec2.erase(std::remove(vec2.begin(), vec2.end(), val2), vec2.end());
vec1.push_back(val2);
vec2.push_back(val1);
}
// adds a value to hashmap | the keys are the node values & the values are their respective indexes
void addToHashMap(int val, size_t last_index)
{
auto search = hashmap.find(val);
if (search != hashmap.end())
hashmap[val].push_back(last_index);
else
hashmap[val] = vector<size_t> { last_index };
}
public:
// insert a node into the heap
void insert(int val)
{
heap.push_back(val);
// point last node to end of array
size_t last_index = heap.size()-1;
addToHashMap(val, last_index);
// only one node so lets just stop here
if (heap.size() == 1) return;
// determines if node is on left or right
short node_direction = (heap.size() % 2 == 0) ? IS_LEFT : IS_RIGHT;
size_t parent_index = (last_index - node_direction) / 2;
// keep going until bubbles to top
while (heap[parent_index] > heap[last_index])
{
//swaps indexes of swapped nodes
swapVectorValue(hashmap[heap[parent_index]], parent_index, hashmap[heap[last_index]], last_index);
// update value of parent and new node
int temp { heap[parent_index] };
heap[parent_index] = heap[last_index];
heap[last_index] = temp;
// leave if at top
if (parent_index == 0) break;
// find new parent
last_index = parent_index;
node_direction = (last_index % 2 == 0) ? IS_RIGHT : IS_LEFT;
parent_index = (last_index - node_direction) / 2;
}
}
// remove any node
void remove(int val)
{
size_t parent_index{ };
auto search = hashmap.find(val);
if (search != hashmap.end())
{
parent_index = hashmap[val].back();
hashmap[val].pop_back();
if (hashmap[val].size() == 0) hashmap.erase(val);
} else return;
last_index = heap.size()-1;
heap[parent_index] = heap[last_index];
heap.pop_back();
size_t left_child = (parent_index * 2) + 1;
size_t right_child = (parent_index * 2) + 2;
size_t smallest_child{ };
if (left_child > last_index) return;
if (left_child == last_index && heap[left_child] > heap[parent_index]) return;
if (right_child > last_index)
smallest_child = left_child;
else
smallest_child = (heap[left_child] < heap[right_child]) ? left_child : right_child;
while (heap[parent_index] > heap[smallest_child])
{
swapVectorValue(hashmap[heap[parent_index]], parent_index, hashmap[heap[smallest_child]], smallest_child);
// update value of parent and new node
int temp { heap[parent_index] };
heap[parent_index] = heap[smallest_child];
heap[smallest_child] = temp;
parent_index = smallest_child;
left_child = (parent_index * 2) + 1;
right_child = (parent_index * 2) + 2;
if (left_child > last_index) return;
if (left_child == last_index && heap[left_child] > heap[parent_index]) return;
if (right_child > last_index)
smallest_child = left_child;
else
smallest_child = (heap[left_child] < heap[right_child]) ? left_child : right_child;
}
}
// removes only the top node
int poll(int val)
{
// NOT PROGRAMMED YET
return 1;
}
void display()
{
// get parent node
for (const auto &n: heap)
{
std::cout << n << ' ';
}
std::cout << std::endl;
}
void hashmapDisplay()
{
for (const auto& r: hashmap)
{
std::cout << "value: " << r.first << " at indexes: ";
for (const auto& s: r.second)
{
std::cout << s << ' ';
}
std::cout << std::endl;
}
}
};
int main()
{
std::unique_ptr<BinaryStack> bstack(new BinaryStack());
bstack->insert(95);
bstack->insert(9);
bstack->insert(9);
bstack->insert(12);
bstack->insert(14);
bstack->insert(9);
bstack->insert(93);
bstack->remove(93);
bstack->remove(9);
// proves order of heap
bstack->display();
// just to prove the indexes are being tracked correctly
bstack->hashmapDisplay();
return 0;
}
I'm creating a function that creates a map from an image with a maze in it.
It takes the image, goes to every pixel and decides if it has to create a node there. One rule is, that when it finds a white pixel at one of the borders it will either be the start or the end node. To tell my pathfinding function where to start, I push the node into a vector(like all other nodes, too) and save the address of that node in a (double)pointer. My problem is, at the end of the loop, the variables in this node change their values(a bool variable might get the value 133) I actually never modify most of the members in this loop. I really have no idea why this is the case...
For examle:
After assigning the node to the pointer:
Completed: false
DistanceTo: 4294967295
PreviousNode: 0x0
XPos: 3
YPos: 0
m_vConnections: <0 Elements>
After function finished:
Completed: 8
DistanceTo: 32674
PreviousNode: SomeAddress
XPos: 3
YPos: 0
m_vConnections: <0 Elements>
The node at SomeAddress actually has completely screwed values, too, but I suspect that the address just changed and it now interprets the data found there as a node.
Sometimes m_vConnections becomes "inaccesseble" which results in sigsegvs when I try to connect something to it.
My function:
bool CreateGraph(const sf::Image &mMaze, std::vector<dijkstra::CNode> *vGraph, dijkstra::CNode **mStart, dijkstra::CNode **mEnd, SMazeCol mColors)
{
*mStart = 0;
unsigned short nExits = 0;
//Get Maze Colours
sf::Color mWallColor = mColors.Wall;
sf::Color mPathColor = mColors.Path;
//Create nodes
for(unsigned int y = 0; y < mMaze.getSize().y; ++y)
{
for(unsigned int x = 0; x < mMaze.getSize().x; ++x)
{
if(mMaze.getPixel(x, y) == mPathColor) //Current pixel is a path
{
bool bTop = false;
bool bBottom = false;
unsigned short nNeighbours = 0;
//Check surroundings of pixel
if(y != 0 && mMaze.getPixel(x, y - 1) == mPathColor)
{
bTop = true;
++nNeighbours;
}
if(y != mMaze.getSize().y - 1 && mMaze.getPixel(x, y + 1) == mPathColor)
{
bBottom = true;
++nNeighbours;
}
if(x != 0 && mMaze.getPixel(x - 1, y) == mPathColor)
{
++nNeighbours;
}
if(x != mMaze.getSize().x - 1 && mMaze.getPixel(x + 1, y) == mPathColor)
{
++nNeighbours;
}
//Decide if a node has to be created at that pixel
if(x == 0 || y == 0 || x == mMaze.getSize().x - 1 || y == mMaze.getSize().y - 1)
{
dijkstra::CNode mNode;
mNode.XPos = x;
mNode.YPos = y;
vGraph->push_back(mNode);
if(*mStart == 0)
{
*mStart = &vGraph->back();
++nExits;
}
else
{
*mEnd = &vGraph->back();
++nExits;
}
}
else if(nNeighbours == 2 && bTop != bBottom)
{
dijkstra::CNode mNode;
mNode.XPos = x;
mNode.YPos = y;
vGraph->push_back(mNode);
}
else if(nNeighbours > 2)
{
dijkstra::CNode mNode;
mNode.XPos = x;
mNode.YPos = y;
vGraph->push_back(mNode);
}
}
}
}
if(nExits != 2)
return false;
The CNode class:
struct SConnection
{
SConnection(CNode *To, unsigned int Distance);
CNode *To;
unsigned int Distance;
};
class CNode
{
public:
CNode();
std::vector<SConnection> Connections()const;
bool AddConnection(CNode *mTo, unsigned int nDistance);
bool AddConnection(const SConnection &mConnection);
bool RemoveConnection(CNode *mTo);
bool operator > (const CNode &rhs)const;
bool operator < (const CNode &rhs)const;
bool operator == (const CNode &rhs)const;
CNode* Addr();
bool Completed;
unsigned int DistanceTo;
CNode *PreviousNode;
unsigned int XPos, YPos;
private:
std::vector<SConnection> m_vConnections;
};
Implementation:
SConnection::SConnection(CNode *To, unsigned int Distance)
{
this->Distance = Distance;
this->To = To;
}
CNode::CNode()
:Completed(false), DistanceTo(std::numeric_limits<unsigned int>::max()), PreviousNode(0)
{
}
std::vector<SConnection> CNode::Connections() const
{
return m_vConnections;
}
bool CNode::AddConnection(CNode *mTo, unsigned int nDistance)
{
if(mTo == 0)
return false;
if(mTo == this)
return false;
for(auto &it : m_vConnections)
{
if(it.To == mTo)
return false;
}
m_vConnections.push_back({mTo, nDistance});
mTo->m_vConnections.push_back({this, nDistance});
return true;
}
bool CNode::AddConnection(const SConnection &mConnection)
{
return (AddConnection(mConnection.To, mConnection.Distance));
}
bool CNode::RemoveConnection(CNode *mTo)
{
for(auto it = m_vConnections.begin(); it != m_vConnections.end(); ++it)
{
if(it->To == mTo)
{
m_vConnections.erase(it);
for(auto it2 = mTo->m_vConnections.begin(); it2 != m_vConnections.end(); ++it2)
{
if(it2->To == this)
mTo->m_vConnections.erase(it2);
}
}
}
return false;
}
bool CNode::operator >(const CNode &rhs)const
{
return DistanceTo > rhs.DistanceTo;
}
bool CNode::operator <(const CNode &rhs) const
{
return DistanceTo < rhs.DistanceTo;
}
bool CNode::operator ==(const CNode &rhs)const
{
return DistanceTo == rhs.DistanceTo;
}
CNode *CNode::Addr()
{
return this;
}
Your main problem is here:
dijkstra::CNode mNode;
//...
vGraph->push_back(mNode);
if(*mStart == 0)
{
*mStart = &vGraph->back();
++nExits;
}
else
{
*mEnd = &vGraph->back();
++nExits;
}
}
else if(nNeighbours == 2 && bTop != bBottom)
{
dijkstra::CNode mNode;
//...
vGraph->push_back(mNode);
}
else if(nNeighbours > 2)
{
dijkstra::CNode mNode;
//...
vGraph->push_back(mNode);
}
Local variables are not placed in global heap, and can be forgotten by programm soon after the code block where they were created is finished.
The better way to improve your code is to rewrite function declaration as
bool CreateGraph(const sf::Image &mMaze, std::vector<dijkstra::CNode*> *vGraph, dijkstra::CNode **mStart, dijkstra::CNode **mEnd, SMazeCol mColors)
And inside function change all:
dijkstra::CNode mNode
vGraph->push_back(mNode);
to
std::shared_ptr<dijkstra::CNode> mNode =
std::shared_ptr<dijkstra::CNode>(new dijkstra::CNode(X, Y));
vGraph->push_back(mNode.get());
I work in cocos2d-x help the problem. error in this line
string route = pathFind(xA, yA, xB, yB);
code HelloWorldScene.cpp
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
#include <iostream>
#include <iomanip>
#include <math.h>
#include <queue>
#include <string>
using namespace std;
using namespace cocos2d;
using namespace CocosDenshion;
const int n = 20; // horizontal size of the map
const int m = 20; // vertical size size of the map
static int MAPP[n][m];
static int putX[100];
static int putY[100];
static int closed_nodes_map[n][m]; // map of closed (tried-out) nodes
static int open_nodes_map[n][m]; // map of open (not-yet-tried) nodes
static int dir_map[n][m]; // map of directions
const int dir = 8; // number of possible directions to go at any position
static int dx[dir] = { 1, 1, 0, -1, -1, -1, 0, 1 };
static int dy[dir] = { 0, 1, 1, 1, 0, -1, -1, -1 };
class node
{
// current position
int xPos;
int yPos;
// total distance already travelled to reach the node
int level;
// priority=level+remaining distance estimate
int priority; // smaller: higher priority
public:
node(int xp, int yp, int d, int p)
{
xPos = xp; yPos = yp; level = d; priority = p;
}
int getxPos() const { return xPos; }
int getyPos() const { return yPos; }
int getLevel() const { return level; }
int getPriority() const { return priority; }
void updatePriority(const int & xDest, const int & yDest)
{
priority = level + estimate(xDest, yDest) * 10; //A*
}
// give better priority to going strait instead of diagonally
void nextLevel(const int & i) // i: direction
{
level += (dir == 8 ? (i % 2 == 0 ? 10 : 14) : 10);
}
// Estimation function for the remaining distance to the goal.
const int & estimate(const int & xDest, const int & yDest) const
{
static int xd, yd, d;
xd = xDest - xPos;
yd = yDest - yPos;
// Euclidian Distance
d = static_cast<int>(sqrt(xd*xd + yd*yd));
// Manhattan distance
//d=abs(xd)+abs(yd);
// Chebyshev distance
//d=max(abs(xd), abs(yd));
return(d);
}
};
CCScene* HelloWorld::scene()
{
// 'scene' is an autorelease object
CCScene *scene = CCScene::create();
// 'layer' is an autorelease object
HelloWorld *layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
for (int y = 0; y < m; y++)
{
for (int x = 0; x < n; x++) MAPP[x][y] = 0;
}
int xA=0, yA=0, xB=7, yB=5;
// get the route
string route = pathFind(xA, yA, xB, yB);
// follow the route on the map and display it
if (route.length() > 0)
{
int j; char c;
int x = xA;
int y = yA;
MAPP[x][y] = 2;
for (int i = 0; i < route.length(); i++)
{
c = route.at(i);
j = atoi(&c);
x = x + dx[j];
y = y + dy[j];
MAPP[x][y] = 3;
}
MAPP[x][y] = 4;
}
int putS=0;
for (int y = 0; y < m; y++)
{
for (int x = 0; x < n; x++)
{
if (MAPP[x][y] == 3)
{
putY[putS] = y;
putX[putS] = x;
putS++;
}
}
}
return true;
}
bool operator < (const node & a, const node & b)
{
return a.getPriority() > b.getPriority();
}
// A-star algorithm.
// The route returned is a string of direction digits.
string pathFind(const int & xStart, const int & yStart,
const int & xFinish, const int & yFinish)
{
static priority_queue<node> pq[2]; // list of open (not-yet-tried) nodes
static int pqi; // pq index
static node* n0;
static node* m0;
static int i, j, x, y, xdx, ydy;
static char c;
pqi = 0;
// reset the node maps
for (y = 0; y < m; y++)
{
for (x = 0; x < n; x++)
{
closed_nodes_map[x][y] = 0;
open_nodes_map[x][y] = 0;
}
}
// create the start node and push into list of open nodes
n0 = new node(xStart, yStart, 0, 0);
n0->updatePriority(xFinish, yFinish);
pq[pqi].push(*n0);
open_nodes_map[x][y] = n0->getPriority(); // mark it on the open nodes map
// A* search
while (!pq[pqi].empty())
{
// get the current node w/ the highest priority
// from the list of open nodes
n0 = new node(pq[pqi].top().getxPos(), pq[pqi].top().getyPos(),
pq[pqi].top().getLevel(), pq[pqi].top().getPriority());
x = n0->getxPos(); y = n0->getyPos();
pq[pqi].pop(); // remove the node from the open list
open_nodes_map[x][y] = 0;
// mark it on the closed nodes map
closed_nodes_map[x][y] = 1;
// quit searching when the goal state is reached
//if((*n0).estimate(xFinish, yFinish) == 0)
if (x == xFinish && y == yFinish)
{
// generate the path from finish to start
// by following the directions
string path = "";
while (!(x == xStart && y == yStart))
{
j = dir_map[x][y];
c = '0' + (j + dir / 2)%dir;
path = c + path;
x += dx[j];
y += dy[j];
}
// garbage collection
delete n0;
// empty the leftover nodes
while (!pq[pqi].empty()) pq[pqi].pop();
return path;
}
// generate moves (child nodes) in all possible directions
for (i = 0; i < dir; i++)
{
xdx = x + dx[i]; ydy = y + dy[i];
if (!(xdx<0 || xdx>n - 1 || ydy<0 || ydy>m - 1 || MAPP[xdx][ydy] == 1
|| closed_nodes_map[xdx][ydy] == 1))
{
// generate a child node
m0 = new node(xdx, ydy, n0->getLevel(),
n0->getPriority());
m0->nextLevel(i);
m0->updatePriority(xFinish, yFinish);
// if it is not in the open list then add into that
if (open_nodes_map[xdx][ydy] == 0)
{
open_nodes_map[xdx][ydy] = m0->getPriority();
pq[pqi].push(*m0);
// mark its parent node direction
dir_map[xdx][ydy] = (i + dir / 2)%dir;
}
else if (open_nodes_map[xdx][ydy] > m0->getPriority())
{
// update the priority info
open_nodes_map[xdx][ydy] = m0->getPriority();
// update the parent direction info
dir_map[xdx][ydy] = (i + dir / 2)%dir;
// replace the node
// by emptying one pq to the other one
// except the node to be replaced will be ignored
// and the new node will be pushed in instead
while (!(pq[pqi].top().getxPos() == xdx &&
pq[pqi].top().getyPos() == ydy))
{
pq[1 - pqi].push(pq[pqi].top());
pq[pqi].pop();
}
pq[pqi].pop(); // remove the wanted node
// empty the larger size pq to the smaller one
if (pq[pqi].size() > pq[1 - pqi].size()) pqi = 1 - pqi;
while (!pq[pqi].empty())
{
pq[1 - pqi].push(pq[pqi].top());
pq[pqi].pop();
}
pqi = 1 - pqi;
pq[pqi].push(*m0); // add the better node instead
}
else delete m0; // garbage collection
}
}
delete n0; // garbage collection
}
return ""; // no route found
}
void HelloWorld::menuCloseCallback(CCObject* pSender)
{
CCDirector::sharedDirector()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}
code HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
class HelloWorld : public cocos2d::CCLayer
{
public:
// Method 'init' in cocos2d-x returns bool, instead of 'id' in cocos2d-iphone (an object pointer)
virtual bool init();
// there's no 'id' in cpp, so we recommend to return the class instance pointer
static cocos2d::CCScene* scene();
// a selector callback
void menuCloseCallback(CCObject* pSender);
// preprocessor macro for "static create()" constructor ( node() deprecated )
CREATE_FUNC(HelloWorld);
};
#endif // __HELLOWORLD_SCENE_H__
pathFind is a function, you should prototype it. Add this somewhere the top of HelloWorldScene.cpp
string pathFind(const int & xStart, const int & yStart,
const int & xFinish, const int & yFinish);
Or if you are intending to call this function from another module then put the prototype in an appropriate header file.
I realized I can't post answers to my own questions because of my low rep or whatever so i deleted my old question and am reasking it. i changed some things and still can't get what i'm looking for.
Here is most of the code
I left out some of the simpler implementations such as parts of the pathFinder class because I know for sure they work, which is why you'll see playerVertex and time just randomly there.
In the example they used a decreaseKey function, I'm not sure if THAT'S what I'm missing? I'm a beginner here, so constructive criticism is welcome. (hopefully as polite as possible) lol. My problem is printing the path, I get a looop of the same two values over and over again.
class Heap
{
public: Heap();
~Heap();
void insert(double element);
double deletemin();
void print();
int size(){return heap.size();}
private:
int currentIndex;
int left(int parent);
int right(int parent);
int parent(int child);
void heapifyup(int index);
void heapifydown(int index);
private:
vector<double> heap;
};
Heap::Heap()
{
currentIndex = 0;
}
Heap::~Heap()
{}
void Heap::insert(double element)
{
heap.push_back(element);
currentIndex++;
heapifyup(heap.size() - 1);
}
double Heap::deletemin()
{
double min = heap.front();
heap[0] = heap.at(heap.size()-1);
heap.pop_back();
heapifydown(0);
currentIndex--;
return min;
}
void Heap::print()
{
vector<double>::iterator pos = heap.begin();
cout << "Heap = ";
while ( pos != heap.end() )
{
cout << *pos;
++pos;
cout << endl;
}
}
void Heap::heapifyup(int index)
{
while((index>0) && (parent(index) >=0) && (heap[parent(index)] > heap[index]))
{
double tmp = heap[parent(index)];
heap[parent(index)] = heap[index];
heap[index] = tmp;
index = parent(index);
}
}
void Heap::heapifydown(int index)
{
int child = left(index);
if((child > 0) && (right(index) > 0) && (heap[child]>heap[right(index)]))
{
child = right(index);
}
if(child > 0)
{
double tmp = heap[index];
heap[index] = heap[child];
heap[child] = tmp;
heapifydown(child);
}
}
int Heap::left(int parent)
{
int i = ( parent <<1) + 1;
return(i<heap.size()) ? i : - 1;
}
int Heap::right(int parent)
{
int i = ( parent <<1) + 2;
return(i<heap.size()) ? i : - 1;
}
int Heap::parent(int child)
{
if(child != 0)
{
int i = (child - 1) >>1;
return i;
}
return -1;
}
class pathFinder : public weightedGraph
{
private:
vertex* playerVertex;
double time;
public:
string source;
pathFinder()
{
playerVertex = NULL;
time = 0;
}
void Dijkstra(int s,int t)
{
vertex *verts = findVertex(grid[s][t]);
Heap H;
for each(vertex *v in vertexList)
{
if(v->data == verts->data)
{
verts->distance = 0;
verts->pred = NULL;
}
v->distance = INFINITY;
v->pred = NULL;
H.insert(v->data);
}
while(H.size() != 0)
{
vertex *x = findVertex(H.deletemin());
for each(edge *v in x->adjacencyList)
{
if(v->end->visited != true)
{
relax(x,v->end);
v->end->visited = true;
}
else
break;
}
}
}
void relax(vertex *a, vertex *b)
{
if(a->distance + weightFrom(a,b) > b->distance)
{
b->distance = a->distance + weightFrom(a,b);
b->pred = a;
}
}
void printPath(double dest,double dest1)
{
vertex *verta = findVertex(dest);
while(verta->pred->data != dest1)
{
cout<<verta->data<<endl;
verta = verta->pred;
}
}
and i'm not sure about the print path being that. i just used the print path from the BFS algorithm i've implemented before.
Where in your printPath function are you looking for the end of the list?
You keep going verta = verta->pred until the data is not equal to some value.
By the way, don't compare doubles for equality, as it ain't going to happen. See What Every Computer Scientist Should Know About Floating Point.
What happens when you single step with your debugger?
(Try drawing the links and how you traverse them.)