I implemented an A* algorithm in C++ but the vector doesn't always have the target node in it resulting in an endless loop. The library I'm using is: https://nullptr.club/libkmint/index.html. The function shortestPathToDist() should do the following: find the shortest path from the source node to the target node using A* ad return that path. Is this a correct implementation of A* or did I do something wrong and what?
Helper.hpp:
#ifndef UFO_HELPER_HPP
#define UFO_HELPER_HPP
#include <map>
#include <iostream>
#include "kmint/map/map.hpp"
namespace kmint {
namespace ufo {
class helper {
public:
helper();
std::vector<const map::map_node*> shortestPathToDist(const kmint::map::map_node& source_node, const kmint::map::map_node& target_node);
private:
const map::map_node* helper::smallestDistance(std::map<const map::map_node*, float>& actualCost, std::map<const map::map_node*, float>& heuristicCost, std::vector<const kmint::map::map_node*>& queue);
float heuristic(const map::map_node& source_node, const map::map_node& target_node);
};
}
}
#endif
Helper.cpp:
#include "kmint/ufo/helper.hpp"
namespace kmint {
namespace ufo {
helper::helper() {
}
std::vector<const map::map_node*> helper::shortestPathToDist(const map::map_node& source_node, const map::map_node& target_node)
{
std::vector<const map::map_node*> path;
std::vector<const map::map_node*> visited;
std::vector<const map::map_node*> queue;
std::map<const map::map_node*, const map::map_node*> previous;
std::map<const map::map_node*, float> cost;
std::map<const map::map_node*, float> heuristic_cost;
queue.push_back(&source_node);
cost[&source_node] = 0;
heuristic_cost[&source_node] = heuristic(source_node, target_node);
while (queue.size() > 0) {
const map::map_node* shortest_path_node = smallestDistance(cost, heuristic_cost, queue);
for (int i = 0; i < shortest_path_node->num_edges(); i++) {
map::map_edge edge = (*shortest_path_node)[i];
const map::map_node *node_to = &edge.to();
if (std::find(visited.begin(), visited.end(), node_to) == visited.end()
&& std::find(queue.begin(), queue.end(), node_to) == queue.end()) {
queue.push_back(node_to);
}
if (cost.find(node_to) == cost.end() || cost[node_to] > cost[shortest_path_node] + edge.weight())
{
cost[node_to] = cost[shortest_path_node] + edge.weight();
heuristic_cost[node_to] = heuristic(*shortest_path_node, target_node);
previous[node_to] = shortest_path_node;
}
if (node_to->node_id() == target_node.node_id())
{
cost[node_to] = cost[shortest_path_node] + edge.weight();
heuristic_cost[node_to] = heuristic(*shortest_path_node, target_node);
previous[node_to] = shortest_path_node;
break;
}
}
queue.erase(queue.begin());
visited.push_back(shortest_path_node);
}
// shortest path to target_node
const map::map_node* current_node = nullptr;
for (auto const&[key, val] : previous) {
if (key != nullptr && key != NULL) {
if (key->node_id() == target_node.node_id()) {
current_node = val;
break;
}
}
}
path.clear();
if (current_node == nullptr || current_node == NULL) {
std::cout << "could not find target node\n";
//this->shortest_path_to_target(source_node, target_node);
return path;
}
while (current_node != &source_node) {
if (current_node != nullptr && current_node != NULL) {
if (path.size() > 0) {
bool found = false;
for (auto &p : path) {
if (p != NULL && p != nullptr && p->node_id() == current_node->node_id()) {
found = true;
break;
}
}
if (!found) {
path.insert(path.begin(), current_node);
}
}
else {
path.insert(path.begin(), current_node);
}
}
for (auto const&[key, val] : previous) {
if (key != nullptr && key != NULL && current_node != nullptr && current_node != NULL) {
if (key->node_id() == current_node->node_id()) {
current_node = val;
break;
}
}
}
}
return path;
}
// manhatan heuristic
float helper::heuristic(const map::map_node& fNode, const map::map_node& sNode)
{
return std::abs(fNode.location().x() - sNode.location().x()) + std::abs(fNode.location().y() - sNode.location().y());
}
const map::map_node* helper::smallestDistance(std::map<const map::map_node*, float>& actualCost, std::map<const map::map_node*, float>& heuristicCost, std::vector<const map::map_node*>& queue)
{
const map::map_node* sDN = nullptr;
for (int i = 0; i < queue.size(); i++)
{
if (sDN == nullptr || actualCost[queue[i]] + heuristicCost[queue[i]] < actualCost[sDN] + heuristicCost[sDN]) {
sDN = queue[i];
}
}
return sDN;
}
}
}
queue.erase(queue.begin());. This is a bug. You are erasing the oldest added object, while you should pop the current shortest path node.
You should also remove the shortest path node from the visited set!
The real issue is that you are not using the correct data structures.
std::vector<const map::map_node*> queue;
should become
using scored_node = std::pair<double, const map::map_node*>
std::priority_queue<scored_node, std::vector<scored_node>, std::greater<scored_node>> queue;
With this change, you don't need smallestDistance anymore, and you should use
const auto shortest_path_scored_node = queue.top();
const auto shortest_path_node = shortest_path_scored_node.second;
queue.pop();
visited.erase(shortest_path_node);
Rather than use a vector
std::vector<const map::map_node*> visited;
you should use an unordered set, given you just care whether the element was visited. You are guaranteed uniqueness and fast retrieval without effort.
std::unodered_set<const map::map_node*> visited;
Related
This is my code:
void list_all_files_subfolder(string& foldername, vector<string>& output) {
DIR* dir;
struct dirent* DirEntry;
if ((dir = opendir(foldername.c_str())) != NULL)
{
while ((DirEntry = readdir(dir)) != NULL)
{
if (strcmp(DirEntry->d_name, ".") && strcmp(DirEntry->d_name, ".."))
{
string subfolder(foldername);
subfolder += "\\";
subfolder += DirEntry->d_name;
DIR* subdir;
if ((subdir = opendir(subfolder.c_str())) != NULL)
{
list_all_files_subfolder(subfolder, output);
closedir(subdir);
}
else
{
string fullname(foldername);
fullname = fullname + '\\' + DirEntry->d_name;
output.push_back(fullname);
}
}
}
closedir(dir);
}
else
{
}
}
It works. But I want to add code that does not search for files with a .dll extension. Please tell me, how can I add that?
You already know how to filter entries on DirEntry->d_name. Simply add an addition check to see if that string ends with ".dll" or not. See How to compare ends of strings in C?. For example:
void list_all_files_subfolder(string& foldername, vector<string>& output) {
DIR* dir;
struct dirent* DirEntry;
if ((dir = opendir(foldername.c_str())) != NULL)
{
while ((DirEntry = readdir(dir)) != NULL)
{
if (strcmp(DirEntry->d_name, ".") && strcmp(DirEntry->d_name, ".."))
{
// you can do the filter here...
/*
char *dot = strrchr(DirEntry->d_name, '.');
if (dot && strcmpi(dot, ".dll") == 0)
continue;
*/
string subfolder(foldername);
subfolder += "\\";
subfolder += DirEntry->d_name;
DIR* subdir;
if ((subdir = opendir(subfolder.c_str())) != NULL)
{
list_all_files_subfolder(subfolder, output);
closedir(subdir);
}
else
{
// or, you can do the filter here...
/*
char *dot = strrchr(DirEntry->d_name, '.');
if (dot && strcmpi(dot, ".dll") == 0)
continue;
*/
string fullname(foldername);
fullname = fullname + '\\' + DirEntry->d_name;
output.push_back(fullname);
}
}
}
closedir(dir);
}
else
{
}
}
On a side note: I would not suggest using opendir() to determine whether the dirent represents a folder or a file. dirent has a d_type member for that purpose, eg:
while ((DirEntry = readdir(dir)) != NULL)
{
if (DirEntry->d_type == DT_DIR)
{
if (strcmp(DirEntry->d_name, ".") && strcmp(DirEntry->d_name, ".."))
{
...
}
}
else if (DirEntry->d_type == DT_REG)
{
...
}
}
The solution could be rewritten slightly to use a dir_iterator. Supposedly modern C++ should use <filesystem> but let's see how one could do it "from scratch".
Let's work backwards, starting with an example of how we would use an acceptor function, and work our way toward beginning of the example:
template <auto N>
bool ends_with(const std::string& str, const char (&suffix)[N])
{
constexpr auto suffixLen = N-1;
return str.size() >= suffixLen && 0 == str.compare(str.size()-suffixLen, suffixLen, suffix, suffixLen);
}
int main()
{
auto const files = list_files(".", [](auto &path){ return !ends_with(path, ".dll"); });
for (auto &f : files)
std::cout << f << "\n";
}
// complete example ends
Then the file list generator would be:
using acceptor_fn = std::function<bool(const std::string &)>;
std::vector<std::string> list_files(dir_contents dir, acceptor_fn accept = {})
{
std::vector<std::string> result;
for (const auto &entry : dir) {
if (entry.getName() == ".") continue;
if (entry.getName() == "..") continue;
auto subPath = entry.getFilePath();
auto subDir = dir_contents(subPath);
bool const isDir = subDir.is_open();
if (isDir)
subPath.push_back(PATH_SEP); // indicate that this is a directory
if (accept && !accept(subPath)) continue;
result.emplace_back(subPath);
if (isDir)
for (auto &path : list_files(std::move(subDir)))
result.emplace_back(std::move(path));
}
return result;
}
std::vector<std::string> list_files(const std::string &path, acceptor_fn accept = {})
{ return list_files(dir_contents(path), accept); }
We have dir_contents as a range adaptor: an iterable range for a given path.
class dir_contents {
dir_iterator m_iter;
public:
explicit dir_contents(const std::string &path) : m_iter(path) {}
bool is_open() const { return m_iter.is_open(); }
dir_iterator begin() { return std::move(m_iter); }
dir_iterator end() const { return {}; }
};
The "magic" happens in the directory iterator:
template <auto fn>
using deleter_from_fn = std::integral_constant<decltype(fn), fn>;
class dir_iterator {
dir_entry m_data;
std::unique_ptr<DIR, deleter_from_fn<&closedir>> m_dir{ open() };
DIR *open() const {
return m_data.hasPath() ? opendir(m_data.getPath().c_str()) : nullptr;
}
dir_iterator(std::shared_ptr<const std::string> &&path) : m_data(std::move(path)) {
if (m_dir) m_data.update(readdir(m_dir.get()));
}
public:
dir_iterator() = default;
dir_iterator(const std::string &path) :
dir_iterator(std::make_shared<const std::string>(path)) {}
const dir_entry &operator*() const { return m_data; }
const dir_entry* operator->() const { return &m_data; }
bool is_open() const { return bool(m_dir); }
bool operator==(const dir_iterator &o) const { return m_data == o.m_data; }
bool operator!=(const dir_iterator &o) const { return !(m_data == o.m_data); }
dir_iterator &operator++() {
m_data.update(readdir(m_dir.get()));
return *this;
}
};
Finally, the dir_entry wrapper:
// complete example begins
#include <cassert>
#include <cstring>
#include <functional>
#include <iostream>
#include <memory>
#include <stack>
#include <vector>
#include <sys/types.h>
#include <dirent.h>
static constexpr char PATH_SEP = '/';
class dir_entry {
const std::shared_ptr<const std::string> m_path;
std::string m_name;
public:
dir_entry() = default;
dir_entry(std::shared_ptr<const std::string> &&path) :
m_path(std::move(path)) { assert(m_path); }
std::string getFilePath() const {
if (!*this) return {};
std::string result;
result.reserve(m_path->size() + 1 + m_name.size());
result.append(*m_path);
result.push_back(PATH_SEP);
result.append(m_name);
return result;
}
bool hasPath() const { return bool(m_path); }
const std::string &getPath() const { return *m_path; }
const std::string &getName() const { return m_name; }
bool operator==(const dir_entry &o) const {
return (!bool(*this) && !bool(o))
|| (m_path && o.m_path
&& (m_path == o.m_path || *m_path == *o.m_path) && m_name == o.m_name);
}
explicit operator bool() const { return m_path && !m_name.empty(); }
void update(dirent* entry) { if (entry) m_name = entry->d_name; else m_name.clear(); }
};
I have the following Edgeclass:
class Edge {
public:
int src, dest;
bool operator== (const Edge &edge) const {
return ((src == edge.src) && (dest == edge.dest)) || ((src == edge.dest) && (dest == edge.src));
}
bool operator<(const Edge& edge) const {
return !(((src == edge.src) && (dest == edge.dest)) || ((src == edge.dest) && (dest == edge.src)));
}
Edge(int src, int dest) {
this->src = src;
this->dest = dest;
}
};
The point of overriding the < operator is when I try to find an edge in a set Edge(0, 1) should be equal to Edge(1, 0). However, the following test code fails to do so and std::find returns an edge that doesn't even exist:
Edge edge(0, 3);
set<Edge> test;
test.insert(Edge(3, 1));
test.insert(Edge(3, 0));
auto a = test.find(edge);
cout << a->src << " " << a->dest << endl;
This will strangely print out 2 0. I can't figure out why and I'm new to C++ any help is appreciated.
You currently don't have a valid Compare for std::set, so your program has undefined behaviour.
Here is one that is compatible with your ==
bool operator<(const Edge& edge) const {
return std::minmax(src, dest) < std::minmax(edge.src, edge.dest);
}
This can also be used to simplify your ==
bool operator==(const Edge& edge) const {
return std::minmax(src, dest) == std::minmax(edge.src, edge.dest);
}
There are two issues in your code.
First, you do not check whether test.find() returns a valid edge; note that find returns end() if no element was found.
Second, your <-operator does not implement a strict ordering, it actually just defines a !=. To overcome this, I'd normalize each edge such that the lower node is always treated as the start; then decide based on the starting nodes, and only if they are equal, consider the destination nodes:
class Edge {
public:
int src, dest;
bool operator== (const Edge &edge) const {
return ((src == edge.src) && (dest == edge.dest)) || ((src == edge.dest) && (dest == edge.src));
}
bool operator<(const Edge& edge) const {
// return !(((src == edge.src) && (dest == edge.dest)) || ((src == edge.dest) && (dest == edge.src)));
int thisSrc = std::min(src,dest);
int thisDest = std::max(src,dest);
int eSrc = std::min(edge.src,edge.dest);
int eDest = std::max(edge.src,edge.dest);
if (thisSrc < eSrc) {
return true;
} else if (thisSrc > eSrc) {
return false;
} else {
return thisDest < eDest;
}
}
Edge(int src, int dest) {
this->src = src;
this->dest = dest;
}
};
#include <set>
int main() {
Edge edge(0, 3);
std::set<Edge> test;
test.insert(Edge(3, 1));
test.insert(Edge(3, 0));
auto a = test.find(edge);
if (a == test.end()) {
std::cout << "edge not found." << std::endl;
} else {
std::cout << a->src << " " << a->dest << std::endl;
}
}
Output:
3 0
Essentially, I am a brick wall as to how I should compare strings with my insert function, not taking case into consideration while simultaneously inserting those same strings with their original case.
Here is my insert function.
TreeNode* Tree::insert(TreeNode* node, string value) {
transform(value.begin(), value.end(), value.begin(), ::tolower);
if (node == nullptr) {
return newTreeNode(value);
}
if (node->data < value) {
node->left = insert(node->left, value);
}
else if(node-> data > value) {
node->right = insert(node->right, value);
}
else {
return node;
}
node->height = 1 + max(height(node->left), height(node->right));
return node;
}
Here is my tree header file:
struct TreeNode {
public:
TreeNode* left;
TreeNode* right;
string data;
};
class Tree {
public:
TreeNode * newTreeNode(string data);
TreeNode * insert(TreeNode* node, string value);
void lexographicPrint(TreeNode* root);
};
newTreeNode Funciton:
TreeNode* AvlTree::newTreeNode(string value) {
TreeNode* treeNode = new TreeNode();
treeNode->data = value;
treeNode->left = nullptr;
treeNode->right= nullptr;
treeNode->height = 1;
return treeNode;
}
Print Function:
void AvlTree::lexographicPrint(TreeNode* root) {
if (root != nullptr) {
lexographicPrint(root->right);
cout << root->data << " ";
lexographicPrint(root->left);
}
}
This currently works as I want it to except for the fact that the Tree contains all values as lower case, obviously due to the transform function. I have tried using a holdValue, like so:
string holdValue;
if (isupper(value[0]) {
holdValue = value;
}
at the top of my function, replacing all insert calls with holdValue. I am confused as to why that changes the order of my tree when comparisons are still made with value. I expected that to work, but it does not. I have yet to find a solution through Google searches.
Rather than use std::string's <, you can use a case insensitive comparison.
bool ci_less(const std::string & lhs, const std::string & rhs) {
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](char l, char r){ return std::to_lower(l) < std::tolower(r); });
}
TreeNode* Tree::insert(TreeNode* node, std::string value) {
if (node == nullptr) {
return newTreeNode(std::move(value));
}
if (ci_less(node->data, value)) {
node->left = insert(node->left, std::move(value));
}
else if(ci_less(value, node->data)) {
node->right = insert(node->right, std::move(value));
}
else {
return node;
}
node->height = 1 + max(height(node->left), height(node->right));
return node;
}
You will need to #include <algorithm> for std::lexicographical_compare.
In a similar vein, you could instead define a case insensitive string type
struct ci_char_traits : public std::char_traits<char> {
static char to_upper(char ch) {
return std::toupper((unsigned char) ch);
}
static bool eq(char c1, char c2) {
return to_upper(c1) == to_upper(c2);
}
static bool lt(char c1, char c2) {
return to_upper(c1) < to_upper(c2);
}
static int compare(const char* s1, const char* s2, size_t n) {
while ( n-- != 0 ) {
if ( to_upper(*s1) < to_upper(*s2) ) return -1;
if ( to_upper(*s1) > to_upper(*s2) ) return 1;
++s1; ++s2;
}
return 0;
}
static const char* find(const char* s, int n, char a) {
auto const ua (to_upper(a));
while ( n-- != 0 )
{
if (to_upper(*s) == ua)
return s;
s++;
}
return nullptr;
}
};
using ci_string = std::basic_string<char, ci_char_traits>;
Essentially, you want to store mixed-case values, but ordered as if they were lowercase.
There are two things you can do.
Replace all of your a < b and a > b checks with case_insensitive_compare(a, b) < 0 and case_insensitive_compare(a, b) > 0, where case_insensitive_compare looks something like:
// +ve => l > r
// 0 => l equivalent to r (possibly different case)
// -ve => l < r
int case_insensitive_compare(const std::string& l, const std::string& r) noexcept {
std::size_t max_size = std::max(l.size(), r.size());
for (std::size_t i = 0; i < max_size; ++i) {
int cmp = std::tolower(l[i]) - std::tolower(r[i]);
if (cmp != 0) return cmp;
}
return l.size() - r.size();
}
// Or in c++20
// std::weak_ordering::greater => l > r
// std::weak_ordering::equivalent => l equivalent to r
// std::weak_ordering::less => l < r
std::weak_ordering case_insensitive_compare(const std::string& l, const std::string& r) noexcept {
std::size_t max_size = std::max(l.size(), r.size());
for (std::size_t i = 0; i < max_size; ++i) {
auto cmp = std::tolower(l[i]) <=> std::tolower(r[i]);
if (cmp != 0) return cmp;
}
return l.size() <=> r.size();
}
You should be able to generalise this to any comparator function (For a Tree<T>, a comparator cmp(const T&, const T&) -> int)
Make your TreeNode store a key/value pair, where the key is the lowercase string, and the value is the mixed-case string. If you need to make the tree store another value, make the value a std::tuple<std::string, ValueType>.
error picture
The compiler says there is something wrong with the conversion of iterators in my for loops. Can someone tell me what's wrong exactly?
For this project, I have supposed to store values into keys. I am storing the keys and values in each node and each time theres a new key, there will be a new node with its vectors of values.
struct node{
int key;
vector<int> values;
};
class key_value_sequences {
public:
key_value_sequences() { }
~key_value_sequences() { }
key_value_sequences(const key_value_sequences& A) {
myList = A.myList;
v = A.v;
}
key_value_sequences& operator=(const key_value_sequences& A) {
if (this == &A) return *this;
myList = A.myList;
v = A.v;
return *this;
}
// YOU SHOULD USE C++ CONTAINERS TO AVOID RAW POINTERS
// IF YOU DECIDE TO USE POINTERS, MAKE SURE THAT YOU MANAGE MEMORY PROPERLY
// IMPLEMENT ME: SHOULD RETURN SIZE OF A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN -1
int size(int key) const {
if (find(v.begin(), v.end(), key)!=v.end()) {
for(list<node>::iterator it = myList.begin(); it != myList.end(); it++) {
if (it->key == key) {
return it->values.size();
}
}
}
else return -1;
}
// IMPLEMENT ME: SHOULD RETURN POINTER TO A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN nullptr
const int* data(int key) const {
if (find(v.begin(), v.end(), key)!=v.end()) {
for(list<node>::iterator it = myList.begin(); it != myList.end(); it++) {
if (it->key == key) {
return it->values.data();
break;
}
}
}
else return nullptr;
}
// IMPLEMENT ME: INSERT VALUE INTO A SEQUENCE IDENTIFIED BY GIVEN KEY
void insert(int key, int value) {
if(v.size() == 0) { //empty list
v.push_back(key);
node n;
n.key = key;
n.values.push_back(value);
myList.push_back(n);
}
else if((find(v.begin(), v.end(), key)!=v.end())) { //if key exists already
for(list<node>::iterator it = myList.begin(); it != myList.end(); it++) {
if (it->key == key) {
it->values.push_back(value);
break;
}
}
}
else { //if theres no existing key
v.push_back(key);
node n;
n.key = key;
n.values.push_back(value);
myList.push_back(n);
}
}
private:
vector<int> v;
list<node> myList;
}; // class key_value_sequences
Your size and data methods are declared const. Therefore myList is const and its begin method returns const_iterators. Change list::iterator into list::const_iterator and it should work fine.
I'm trying to refactor part of a pathfinding algorithm I had that used pointers to not use pointers. Unfortunately I'm not that knowledgable about references. I get the error: Invalid operands to binary expression ('std::__1::reference_wrapper<Tile>' and 'const std::__1::reference_wrapper<Tile>')
I also have no idea what that means. My code is below, and I can gather that it comes from the line: openList.erase(std::find(openList.begin(), openList.end(), current)); but I'm not sure how to fix this.
bool TileMap::tilesBetween(Tile& p_start, Tile& p_end)
{
std::vector<std::reference_wrapper<Tile>> openList;
std::vector<std::reference_wrapper<Tile>> closedList;
openList.push_back(p_start);
do
{
std::sort(openList.begin(), openList.end(), sortF());
Tile& current = openList[0];
closedList.push_back(current);
openList.erase(std::find(openList.begin(), openList.end(), current));
if(std::find(closedList.begin(), closedList.end(), p_end) != closedList.end())
{
return true;
}
std::vector<std::reference_wrapper<Tile>> adjacentTiles;
if (current.m_coordinates.x > 0)
{
adjacentTiles.push_back(m_tiles[current.m_coordinates.y * m_width + (current.m_coordinates.x - 1)]);
}
if (current.m_coordinates.x < m_width)
{
adjacentTiles.push_back(m_tiles[current.m_coordinates.y * m_width + (current.m_coordinates.x + 1)]);
}
if (current.m_coordinates.y > 0)
{
adjacentTiles.push_back(m_tiles[(current.m_coordinates.y - 1) * m_width + current.m_coordinates.x]);
}
if (current.m_coordinates.y < m_height)
{
adjacentTiles.push_back(m_tiles[(current.m_coordinates.y + 1) * m_width + current.m_coordinates.x]);
}
for(auto t : adjacentTiles)
{
if(std::find(closedList.begin(), closedList.end(), t) != closedList.end())
{
continue;
}
if(std::find(openList.begin(), openList.end(), t) == closedList.end())
{
openList.push_back(t);
}
}
}
while(!openList.empty());
return false;
}
EDIT: posted sortF
struct sortF
{
bool operator()(const Tile* p_a, const Tile* p_b) const
{
return p_a->f < p_b->f;
}
};
UPDATE: As per the suggestion, I've changed the function to use pointers instead of references. It SEEMS to be working, but I have more to implement before it's finished.
bool TileMap::tilesBetween(Tile* p_start, Tile* p_end)
{
std::vector<Tile*> openList;
std::vector<Tile*> closedList;
std::cout << p_start << ", ";
openList.push_back(p_start);
do
{
std::sort(openList.begin(), openList.end(), sortF());
Tile* current = openList[0];
closedList.push_back(current);
openList.erase(std::find(openList.begin(), openList.end(), current));
if(std::find(closedList.begin(), closedList.end(), p_end) != closedList.end())
{
return true;
}
std::vector<Tile*> adjacentTiles;
if (current->m_coordinates.x > 0)
{
adjacentTiles.push_back(&m_tiles[current->m_coordinates.y * m_width + (current->m_coordinates.x - 1)]);
}
if (current->m_coordinates.x < m_width)
{
std::cout << &m_tiles[current->m_coordinates.y * m_width + (current->m_coordinates.x + 1)] << std::endl;
adjacentTiles.push_back(&m_tiles[current->m_coordinates.y * m_width + (current->m_coordinates.x + 1)]);
}
if (current->m_coordinates.y > 0)
{
adjacentTiles.push_back(&m_tiles[(current->m_coordinates.y - 1) * m_width + current->m_coordinates.x]);
}
if (current->m_coordinates.y < m_height)
{
adjacentTiles.push_back(&m_tiles[(current->m_coordinates.y + 1) * m_width + current->m_coordinates.x]);
}
for(auto t : adjacentTiles)
{
if(std::find(closedList.begin(), closedList.end(), t) != closedList.end())
{
continue;
}
if(std::find(openList.begin(), openList.end(), t) == openList.end())
{
openList.push_back(t);
}
}
}
while(!openList.empty());
return false;
}
I can gather that it comes from the line: openList.erase(std::find(openList.begin(), openList.end(), current)); but I'm not sure how to fix this.
std::find iterates over std::reference_wrapper<Tile> and not Tile& itself. Hence
Tile& current = openList[0];
openList.erase(std::find(openList.begin(), openList.end(), current));
is incorrect. Change this to
openList.erase(std::find_if(openList.begin(), openList.end(), [&](const std::reference_wrapper<Tile> &i)
{
return i.get() == current;
}));
std::reference_wrapper::get returns the underlying reference.
A simple, working example to demonstrate this
#include <algorithm>
#include <list>
#include <vector>
#include <iostream>
#include <functional>
struct S
{
int val;
S(int i) : val(i) {}
};
int main()
{
std::list<S> l = {-4, -3, -2, -1, 0, 1, 2, 3, 4};
std::vector<std::reference_wrapper<S>> v(l.begin(), l.end());
auto& x = l.front();
v.erase(std::find_if(v.cbegin(), v.cend(), [&](const std::reference_wrapper<S> &i)
{
return i.get().val == x.val;
}));
std::cout << v[0].get().val;
}
Your problem is here:
std::sort(openList.begin(), openList.end(), sortF());
Your sortF is an invalid comparison object. The operator() must look like this:
bool operator()(const Tile& lhs, const Tile& rhs) const
// ^^^ ref ^^^ ^^^ ref ^^^
{
return lhs.f < rhs.f;
}
Instead of:
bool operator()(const Tile* p_a, const Tile* p_b) const
// ^^^ ptr ^^^ ^^^ ptr ^^^
You have a vector of reference_wrapper<Tile>, not a vector of Tile*.