c++ - sorting std::vector in pathfinding - c++

I'm trying to implement a rough pathfinding example into a game i'm working on. I'm at a point where I need to sort a std::vector<Tile*> and i've tried to do so with the following but I get a bunch of errors I can't figure out. I've also tried to change the references in the sortF struct to pointers, but I get another error - Comparison between pointer and integer ('Tile *' and 'int').
The error in question is: No matching function for call to object of type 'Stage::sortF'
Wondering what exactly i'm doing wrong here.
(and if anyone had any comments on the pathfinding that would be good too)
in Stage.h public
struct sortF
{
bool operator()(const Tile& a, const Tile& b) const
{
return a.f > b.f;
}
};
in Stage.cpp
bool Stage::tilePath(Tile* start, Tile* end)
{
std::vector<Tile*> path;
std::vector<Tile*> open;
std::vector<Tile*> closed;
start->previousTile = start;
start->g = 0;
start->h = 0;
start->f = 0;
int i, j;
float g, h, f;
int sx, sy, ex, ey;
int cost;
Tile* current = start;
Tile* neighbor = NULL;
Tile* previous = NULL;
std::cout << neighbor << std::endl;
while(current != end) {
sx = fmaxf(0, current->x - 1);
sy = fmaxf(0, current->y - 1);
ex = fminf(17 - 1, current->x + 1);
ey = fminf(6 - 1, current->y + 1);
for(i = sx; i <= ex; i++) {
for(j = sy; j <= ey; j++) {
neighbor = tiles[((j - 1) * 17) + i - 1];
if(neighbor == current || !neighbor->walkable) continue;
previous = current;
if(false /* raytrace */) {
} else {
cost = (current->x != neighbor->x || current->y != neighbor->y) ? 1.4 : 1;
g = current->g + cost;
h = euclidian(neighbor, end);
f = g + h;
}
if(std::find(open.begin(), open.end(), neighbor) != open.end() ||
std::find(closed.begin(), closed.end(), neighbor) != closed.end()) {
if(neighbor->f > f) {
neighbor->f = f;
neighbor->g = g;
neighbor->h = h;
neighbor->previousTile = current;
}
} else {
neighbor->f = f;
neighbor->g = g;
neighbor->h = h;
neighbor->previousTile = current;
open.push_back(current);
}
}
}
closed.push_back(current);
if(open.size() == 0) {
return false;
}
std::sort(open.begin(), open.end(), sortF());
current = open[0];
std::remove(open.begin(), open.end(), 0);
}
return true;
}

Note: You didn't include your error messages, so the following answer is more or less based on view compiling:
sortF(), not sortF
error: expected primary-expression before ‘)’ token
std::sort(open.begin(), open.end(), sortF);
^
You need an instance of sortF, not the type struct sortF. Either use sortF() to create a temporary object, or use a function instead of a functor:
bool sortF(const Tile& a, const Tile& b)
{
return a.f > b.f;
}
Tile* vs const Tile&
You use std::sort on a std::vector<Tile*>, but your comparing function uses const Tile& as parameter. Either use std::vector<Tile> or correct the type in your function:
bool sortF(const Tile* a, const Tile* b)
{
return a->f > b->f;
}

Since the elements of the std::vector of type Tile*, the function that compares two items of the std::vector must take two Tile*s.
struct sortF
{
bool operator()(Tile* ap, Tile* bp) const
{
return a->f > b->f;
}
};

You definitely must change references to pointers in the comparer function. You didn't tell where exactly you got the error about comparison between pointer and integer, but I believe it happened on this line:
std::remove(open.begin(), open.end(), 0);
std::remove() takes a value type of the container, not an index. What you wanted to do is probably
open.pop_front()
That said, this operation as well as sorting take quite a bit of time, and as Javi V commented, using heap structure is preferable here.

Related

Improving usage of C++ vector access

I am writing currently an importer for Labplot to support BLF files. The importer works fine, but when looking at the performance it is visible that there is a lot of room to improve. It is visible that adding the data to the datacontainer consumes the most of the computational power. I tried already for testing using std::vector, but it has not that big impact.
I am not able to use a static array, because the actual number of messages is unknown (only upper limit is known), because if the dbcParser is not able to parse the message it will be skipped. So at the end of the import I have to resize the array.
Are there any recomandations how to improve the performance of the code?
Definition of v: QVector<const Vector::BLF::ObjectHeaderBase*> v;
bool firstMessageValid = false;
for (const auto ohb : v) {
int id;
std::vector<double> values;
if (ohb->objectType == Vector::BLF::ObjectType::CAN_MESSAGE) {
const auto message = reinterpret_cast<const Vector::BLF::CanMessage*>(ohb);
id = message->id;
m_dbcParser.parseMessage(message->id, message->data, values);
} else if (ohb->objectType == Vector::BLF::ObjectType::CAN_MESSAGE2) {
const auto message = reinterpret_cast<const Vector::BLF::CanMessage2*>(ohb);
id = message->id;
m_dbcParser.parseMessage(message->id, message->data, values);
} else
return 0;
if (values.size() == 0) {
// id is not available in the dbc file, so it is not possible to decode
DEBUG("Unable to decode message: " << id);
continue;
}
uint64_t timestamp;
timeInNS = getTime(ohb, timestamp);
if (convertTimeToSeconds) {
double timestamp_seconds;
if (timeInNS)
timestamp_seconds = (double)timestamp / pow(10, 9); // TimeOneNans
else
timestamp_seconds = (double)timestamp / pow(10, 5); // TimeTenMics
m_DataContainer.setData<double>(0, message_index, timestamp_seconds);
} else
m_DataContainer.setData<qint64>(0, message_index, timestamp);
if (firstMessageValid) {
const auto startIndex = idIndexTable.value(id) + 1; // +1 because of time
for (std::vector<double>::size_type i = 1; i < startIndex; i++) {
const auto prevValue = m_DataContainer.data<double>(i, message_index - 1);
m_DataContainer.setData<double>(i, message_index, prevValue);
}
for (std::vector<double>::size_type i = startIndex; i < startIndex + values.size(); i++) {
m_DataContainer.setData<double>(i, message_index, values.at(i - startIndex));
}
for (std::vector<double>::size_type i = startIndex + values.size(); i < m_DataContainer.size(); i++) {
const auto prevValue = m_DataContainer.data<double>(i, message_index - 1);
m_DataContainer.setData<double>(i, message_index, prevValue);
}
} else {
const auto startIndex = idIndexTable.value(id) + 1; // +1 because of time
for (std::vector<double>::size_type i = 1; i < startIndex; i++) {
m_DataContainer.setData<double>(i, message_index, 0);
}
for (std::vector<double>::size_type i = startIndex; i < startIndex + values.size(); i++) {
m_DataContainer.setData<double>(i, message_index, values.at(i - startIndex));
}
for (std::vector<double>::size_type i = startIndex + values.size(); i < m_DataContainer.size(); i++) {
m_DataContainer.setData<double>(i, message_index, 0);
}
firstMessageValid = true;
}
message_index++;
}
struct DataContainer {
void clear();
template<class T>
void appendVector(QVector<T>* data, AbstractColumn::ColumnMode cm) {
m_dataContainer.push_back(data);
m_columnModes.append(cm);
};
template<class T>
void setData(int indexDataContainer, int indexData, T value) {
auto* v = static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer));
v->operator[](indexData) = value;
}
template<class T>
T data(int indexDataContainer, int indexData) {
auto* v = static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer));
return v->at(indexData);
}
int size() const;
const QVector<AbstractColumn::ColumnMode> columnModes() const;
/*!
* \brief dataContainer
* Do not modify outside as long as DataContainer exists!
* \return
*/
std::vector<void*> dataContainer() const;
AbstractColumn::ColumnMode columnMode(int index) const;
const void* datas(int index) const;
bool resize(uint32_t) const;
private:
QVector<AbstractColumn::ColumnMode> m_columnModes;
std::vector<void*> m_dataContainer; // pointers to the actual data containers
};
Edit
Before the loop I resize every vector to the absolute maximum number of messages.
if (convertTimeToSeconds) {
auto* vector = new QVector<double>();
vector->resize(message_counter);
m_DataContainer.appendVector<double>(vector, AbstractColumn::ColumnMode::Double);
} else {
auto* vector = new QVector<qint64>();
vector->resize(message_counter);
m_DataContainer.appendVector<qint64>(vector, AbstractColumn::ColumnMode::BigInt); // BigInt is qint64 and not quint64!
}
for (int i = 0; i < vectorNames.length(); i++) {
auto* vector = new QVector<double>();
vector->resize(message_counter);
m_DataContainer.appendVector(vector, AbstractColumn::ColumnMode::Double);
}
During parsing I discard messages if I am not able to parse them, therefore message_index <= message_counter. So when having 100k messages, but I parse only 50k of them, I have to resize the array at the end to not waste memory.
m_DataContainer.resize(message_index);
Edit2
Replacing
auto* v = static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer));
v->operator[](indexData) = value;
by
static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer))->operator[](indexData) = value;
and replacing
auto* v = static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer));
return v->at(indexData);
by
return static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer))->at(indexData);
brought about 20%. I thought it will be optimized out at -O2 but was not.
With -O2 moving from QVector to std::vector was again an improvement of around 25%

Implementing a* pathfinding in c++

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 = &currentNode;
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, &currentNode);
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, &currentNode); 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.

C++ Bubble sort dynamically allocated array

I wrote a bubble sorting algorithm which sorts a dynamically allocated array using string comparison.
Here is my code:
void AddressBook::bubble_sort_address_book(){
bool swapped = true;
while(swapped){
swapped = false;
for(int i = 0; i < noOfEmployees; i++){
if(employees[i].combined_name() > employees[i+1].combined_name()){
Employee temp_employee = employees[i+1];
employees[i+1] = employees[i];
employees[i] = temp_employee;
}
}
}
}
My problem is pretty obvious, yet I can not seem to figure out how to solve it: The code sometimes fails on the line (in an undefined manner) :
Employee temp_employee = employees[i+1]
Its pretty obvious because if i is equal to the end of the array, accessing memory with i+1 results in undefined behaviour. However, if I stop the for loop with noOfEmployees-1, this does not happen but the first element is never sorted (obviously).
How can I implement bubble sort properly? It seems as such a trivial task. Am I missing something?
The following simplified version in pure C works fine:
int employees[10]= {3,1,7,6,9,7,1,0,2,6};
int noOfEmployees= 10;
void bubble_sort_address_book(void){
bool swapped = true;
int i;
while(swapped){
swapped = false;
for(i = 0; i < noOfEmployees-1; i++){
if(employees[i] > employees[i+1]){
int temp_employee = employees[i+1];
employees[i+1] = employees[i];
employees[i] = temp_employee;
swapped= true;
}
}
}
}
int main()
{
int i;
bubble_sort_address_book();
for (i=0; i<noOfEmployees; i++) {
printf("emp %d= %d\n", i, employees[i]);
}
return 0;
}
As you request, the function of variable swapped is to indicate that following a complete pass through the array no swap occurred and so it indicates the array is now sorted.
You can use an explicit bound on the outer loop.
You should also split things out into smaller functions.
bool operator <(Employee const & lhs, Employee const & rhs) {
return lhs.combined_name() < rhs.combined_name();
}
// a.k.a. std::swap
void swap(Employee & lhs, Employee & rhs) {
Employee temp(static_cast<Employee&&>(lhs)); // a.k.a. std::move
lhs = static_cast<Employee&&>(rhs);
rhs = static_cast<Employee&&>(temp);
}
void bubble_sort_impl(Employee * begin, Employee * end) {
for (; end != begin; --end) {
for (Employee * it = begin; it+1 != end; ++it) {
if (*(it+1) < *it) {
swap(*it, *(it+1));
}
}
}
}
// do we really need "bubble_" or "_address_book" in this name?
void AddressBook::bubble_sort_address_book() {
bubble_sort_impl(employees, employees + noOfEmployees);
}
another solution:
#include <iostream>
#include <vector>
using namespace std;
int employees[10] = { 3,1,7,6,9,7,1,0,2,6 };
void bubble_sort_address_book(void) {
bool swapped = true;
int i;
int noOfEmployees = 10;
while (swapped) {
swapped = false;
for (i = 1; i <= noOfEmployees ; i++) {
if (employees[i] > employees[i - 1]) {
int temp_employee = employees[i - 1];
employees[i - 1] = employees[i];
employees[i] = temp_employee;
swapped = true;
}
}
}
}
int main()
{
int i;
int noOfEmployees = 10;
bubble_sort_address_book();
for (i = 0; i<noOfEmployees; i++) {
printf("emp %d= %d\n", i, employees[i]);
}
return 0;
}

Prim's Algorithm with std::vector and std::queue , What is wrong with my code?

I am trying sort edges in Prim's Algorithm using STL and overloading operator () , But I am getting runtime error Invalid operator and Invalid Heap.
When I compile my code in CodeBlocks everything's working but Visual Studio 2015 displayed runtime errors. What should I do?
struct edge {
int cost;
int start;
int end;
};
struct sorting {
bool operator() (const edge &a, const edge &b)
{
if (a.cost<b.cost) return false;
else return true;
}
};
priority_queue < edge , vector <edge> , sorting> queue;
edge tree[1005];
int T[1000][1000];
int G[1005][1005];
bool ISIT[1005];
string STRINGS[1005];
int ID[40005];
int howmany = 0;
int howmanyneigh[1005];
void PRIM() {
int w = 1;
ISIT[w] = 1;
edge K;
howmany++;
for (int i = 0; i<howmanyneigh[w]; i++) {
K.start = w;
K.end = G[w][i];
K.cost = T[w][G[w][i]];
queue.push(K);
}
while (howmany<N)
edge b;
b = queue.top();
queue.pop();
while (ISIT[b.end]) {
b = queue.top();
queue.pop();
}
ISIT[b.end] = 1;
tree[howmany - 1] = b;
for (int i = 0; i<howmanyneigh[b.end]; i++) {
K.start = b.end;
K.end = G[b.end][i];
K.cost = T[b.end][G[b.end][i]];
queue.push(K);
}
howmany++;
}
}
It seems that you have a problem in your sorting comparator. This comparator should provide strict weak ordering. One of the requirements for strict weak ordering is that comp(a, a) == false. Change your sorting::operator() from
if (a.cost<b.cost) return false;
else return true;
to:
if (a.cost>b.cost) return true;
else return false;
or simply:
return a.cost > b.cost;

Why is this merge sort function returning linked list with zeroes (c++)?

I've got this merge sort function
namespace sorted{
template<typename T>
class list {
/* other stuff */
list<T>* slice(int from, int to){
from = (from < 0) ? 0 : from;
to = (to > this->len) ? this->len : to;
list<T>* result = new list<T>();
node<T> *n = this->head;
int idx = 0;
while (n && (idx < this->len)){
if ((from <= idx) && (idx <= to)) result->append(n->value);
if (idx > to) break;
n = n->next;
idx++;
}
return result;
}
}
template<typename T>
list<T>* merge(list<T>* left, list<T>* right){
list<T>* result = new list<T>();
while ((left->length() > 0) || (right->length() > 0)){
if ((left->length() > 0) && (right->length() > 0)){
T l = left->get(0);
T r = right->get(0);
if (l <= r){
result->append(l);
left->remove(0);
} else{
result->append(r);
right->remove(0);
}
continue;
}
if (left->length() > 0) {
result->append(left->get(0));
left->remove(0);
}
if (right->length() > 0) {
result->append(right->get(0));
right->remove(0);
}
}
return result;
}
template<typename T>
list<T>* merge_sort(list<T>* original){
if (original->length() <= 1) {
return original;
}
int len = original->length();
list<T>* left = NULL;
list<T>* right = NULL;
if (len > 2){
left = original->slice(0,(len/2));
right = original->slice((len/2)+1,len-1);
}else if (len == 2){
left = original->slice(0,0);
right = original->slice(1,1);
}
left = merge_sort(left);
right = merge_sort(right);
delete original;
list<T>* result = merge(left, right);
delete left;
delete right;
return result;
}
/* other stuff */
}
And here's my main method
int main(int argc, char** argv){
sorted::list<int>* l = get_random_list();
l = merge_sort(l);
for (int i = 0; i < (l->length() - 1); i++){
int t = l->get(i);
int u = l->get(i+1);
if (t > u){
sorted::list<int>* m = l->slice(i - 5, i + 5);
cout << m << endl;
delete m;
break;
}
}
delete l;
return 0;
}
Link to bitbucket.org project
My question was this.
If the list is returned properly from the slicing function, why would it not be returned to the main function properly, if its being done the same way?
[Update] Added functions as they're currently functioning the way they should be. A full version is up on bitbucket.
After checking your full code in the link you provided, I can definitely say the problem is because you don't have an assignment operator.
What happens now is that the assignments of the lists will use the default assignment operator that is automatically generated by the compiler. This does a shallow copy, so the list on the left hand side of the assignment will have its pointers be the same as for the list on the right hand side. This means that when the local variable you return goes out of scope, it will of course invoke the destructor which deletes the lists. Now the copy have pointers which points to deleted memory, and accessing thos pointers is undefined behavior. This is why it seems to work in one place and not the other.