Skip to content

Instantly share code, notes, and snippets.

@MORTAL2000
Last active July 9, 2016 21:26
Show Gist options
  • Save MORTAL2000/e26c072bf1e730bde93235da95308d77 to your computer and use it in GitHub Desktop.
Save MORTAL2000/e26c072bf1e730bde93235da95308d77 to your computer and use it in GitHub Desktop.
#include <SFML/Graphics.hpp>
#include <iostream>
#include <iomanip>
#include <unordered_map>
#include <unordered_set>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <functional>
#include <algorithm>
namespace std
{
template <>
struct hash<sf::Vector2<int> >
{
inline size_t operator()(const sf::Vector2<int>& location) const
{
return location.x * 1812433253 + location.y;
}
};
}
template<typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> loc)
{
out << '(' << loc.x << ',' << loc.y << ')';
return out;
}
namespace sf
{
template<typename T >
bool operator> (const Vector2<T> &left, const Vector2<T> &right)
{
return std::tie(left.x, left.y) > std::tie(right.x, right.y);
}
template<typename T >
bool operator< (const Vector2<T> &left, const Vector2<T> &right)
{
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
}
class Grid : public sf::Drawable
{
using PQElement = std::pair<sf::Vector2i, double>;
public:
Grid(int width_, int height_)
: width(width_)
, height(height_)
, grid(width_ * height_)
{
const auto tile_size = sf::Vector2f(20, 20);
int i=0;
for(auto& tile : grid)
{
sf::Vector2f position(i % width, i / width); i++;
tile.setSize(tile_size);
tile.setFillColor(sf::Color::White);
tile.setOutlineColor(sf::Color(205,201,201));
tile.setOutlineThickness(-1);
tile.setPosition(position.x * tile_size.x, position.y * tile_size.y );
}
}
void add_wall(int x1, int y1, int x2, int y2)
{
// if(!in_bounds(sf::Vector2i(x1+1, y1+1)) || !in_bounds(sf::Vector2i(x2+1, y2+1)))
// {
// std::cout << "Square::add_wall: invalid parameters: "
// << sf::Vector2i(x1, y1) << ' '
// << sf::Vector2i(x2, y2) <<"\n";
// return;
// }
for (int x = x1; x < x2; ++x)
{
for (int y = y1; y < y2; ++y)
{
walls.insert(sf::Vector2i { x, y });
grid[x + width * y].setFillColor(sf::Color(139,137,137));
}
}
}
void breadth_first_search(sf::Vector2i start)
{
came_from.clear();
std::queue<sf::Vector2i> frontier;
frontier.push(start);
came_from[start] = start;
while (!frontier.empty())
{
auto current = frontier.front();
frontier.pop();
for (auto next : neighbors(current))
{
if (!came_from.count(next))
{
frontier.push(next);
came_from[next] = current;
}
}
}
begin = came_from.begin();
}
void breadth_first_search(sf::Vector2i start, sf::Vector2i goal)
{
std::queue<sf::Vector2i> frontier;
frontier.push(start);
came_from[start] = start;
while (!frontier.empty())
{
auto current = frontier.front();
frontier.pop();
if (current == goal) {
begin = came_from.begin();
reconstruct_path(start, goal);
return;
}
for (auto next : neighbors(current))
{
if (!came_from.count(next))
{
frontier.push(next);
came_from[next] = current;
}
}
}
}
void dijkstra_search(sf::Vector2i start, sf::Vector2i goal)
{
std::priority_queue<PQElement, std::vector<PQElement>, std::greater<PQElement>> frontier;
frontier.emplace(start, 0);
came_from[start] = start;
cost_so_far[start] = 0;
while (!frontier.empty())
{
auto current = frontier.top().first;
frontier.pop();
if (current == goal) {
begin = came_from.begin();
reconstruct_path(start, goal);
return;
}
for (auto next : neighbors(current))
{
double new_cost = cost_so_far[current] + cost(current, next);
if (!cost_so_far.count(next) || new_cost < cost_so_far[next])
{
cost_so_far[next] = new_cost;
came_from[next] = current;
frontier.emplace(next, new_cost);
}
}
}
}
void a_star_search(sf::Vector2i start, sf::Vector2i goal)
{
std::priority_queue<PQElement, std::vector<PQElement>, std::greater<PQElement>> frontier;
frontier.emplace(start, 0);
came_from[start] = start;
cost_so_far[start] = 0;
while (!frontier.empty())
{
auto current = frontier.top().first;
frontier.pop();
if (current == goal) {
begin = came_from.begin();
reconstruct_path(start, goal);
return;
}
for (auto next : neighbors(current))
{
double new_cost = cost_so_far[current] + cost(current, next);
if (!cost_so_far.count(next) || new_cost < cost_so_far[next])
{
cost_so_far[next] = new_cost;
double priority = new_cost + heuristic(next, goal);
frontier.emplace(next, priority);
came_from[next] = current;
}
}
}
}
bool updateSearch(sf::Time dt)
{
if (timer <= sf::Time::Zero)
{
timer += sf::seconds(0.0125f);//sf::seconds(0.25f);
if(begin == came_from.end()){timer = sf::Time::Zero; return false;}
if ( begin->second != start && begin->second != goal ){
//grid[begin->second.x + width * begin->second.y].setFillColor(sf::Color(127,255,212));
grid[begin->second.x + width * begin->second.y].setFillColor(sf::Color(102,205,170));
}
begin++;
}else timer -= dt;
return true;
}
bool updatePath(sf::Time dt)
{
if (timer <= sf::Time::Zero)
{
timer += sf::seconds(0.0125f);//sf::seconds(0.25f);
if(vbegin == path.end()) {timer = sf::Time::Zero; return false;}
if ( *vbegin != start && *vbegin != goal )
grid[vbegin->x + width * vbegin->y].setFillColor(sf::Color::Yellow);
vbegin++;
}else timer -= dt;
return true;
}
private:
inline bool in_bounds(sf::Vector2i id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
inline bool passable(sf::Vector2i id) const
{
return !walls.count(id);
}
std::vector<sf::Vector2i> neighbors(sf::Vector2i id) const
{
static const std::array<sf::Vector2i, 4> DIRS{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1}
}};
std::vector<sf::Vector2i> results;
for (auto dir : DIRS)
{
sf::Vector2i next(id + dir);
if (in_bounds(next) && passable(next))
{
results.emplace_back(next);
}
}
if ((id.x + id.y) % 2 == 0)
{
// aesthetic improvement on square grids
std::reverse(results.begin(), results.end());
}
return results;
}
double cost(sf::Vector2i from_node, sf::Vector2i to_node) const
{
return forests.count(to_node) ? 5 : 1;
}
inline double heuristic(sf::Vector2i a, sf::Vector2i b)
{
return abs(a.x - b.x) + abs(a.y - b.y);
}
void reconstruct_path(sf::Vector2i start_, sf::Vector2i goal_)
{
start = start_;
goal = goal_;
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
auto current = goal;
path.push_back(current);
while (current != start) {
current = came_from[current];
path.push_back(current);
}
std::reverse(path.begin(), path.end());
vbegin = path.begin();
}
void draw(sf::RenderTarget& target, sf::RenderStates) const
{
for(const auto& tile : grid)
{
target.draw(tile);
}
}
private:
int width, height;
std::vector<sf::RectangleShape> grid;
std::unordered_set<sf::Vector2i> walls;
std::unordered_map<sf::Vector2i, double> cost_so_far;
std::unordered_map<sf::Vector2i, sf::Vector2i> came_from;
std::unordered_map<sf::Vector2i, sf::Vector2i>::iterator begin;
std::unordered_set<sf::Vector2i> forests;
std::vector<sf::Vector2i> path;
std::vector<sf::Vector2i>::iterator vbegin;
sf::Time timer = sf::Time::Zero;
sf::Vector2i start{};
sf::Vector2i goal{};
};
Grid make_diagram1()
{
Grid grid(30, 15);//(45, 30);
grid.add_wall(3, 3, 5, 12);
grid.add_wall(13, 4, 15, 15);
grid.add_wall(21, 0, 23, 7);
grid.add_wall(23, 5, 26, 7);
return grid;
}
Grid make_diagram4()
{
Grid grid(10, 10);
grid.add_wall(1, 7, 4, 9);
// grid.forests = std::unordered_set<sf::Vector2i> {
// {3, 4}, {3, 5}, {4, 1}, {4, 2},
// {4, 3}, {4, 4}, {4, 5}, {4, 6},
// {4, 7}, {4, 8}, {5, 1}, {5, 2},
// {5, 3}, {5, 4}, {5, 5}, {5, 6},
// {5, 7}, {5, 8}, {6, 2}, {6, 3},
// {6, 4}, {6, 5}, {6, 6}, {6, 7},
// {7, 3}, {7, 4}, {7, 5}
// };
return grid;
}
int main()
{
sf::RenderWindow window({30*20, 15*20}/*{45*20, 30*20}*/, "algorithm");
Grid grid = make_diagram1();
//grid.breadth_first_search(sf::Vector2i(7, 8), sf::Vector2i{17, 13});
//grid.dijkstra_search(sf::Vector2i(7, 8), sf::Vector2i{17, 13});
grid.a_star_search(sf::Vector2i(7, 8), sf::Vector2i{17, 13});
sf::Clock clock;
const sf::Time TimePerFrame = sf::seconds(1.f / 60.f);
sf::Time timeSinceLastUpdate = sf::Time::Zero;
while(window.isOpen())
{
sf::Time dt = clock.restart();
timeSinceLastUpdate += dt;
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
while (timeSinceLastUpdate > TimePerFrame)
{
timeSinceLastUpdate -= TimePerFrame;
if(!grid.updateSearch(TimePerFrame))
grid.updatePath(TimePerFrame);
}
window.clear();
window.draw(grid);
window.display();
}
}
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <iostream>
#include <iomanip>
#include <unordered_map>
#include <unordered_set>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <functional>
#include <algorithm>
namespace std
{
template <>
struct hash<sf::Vector2<int> >
{
inline size_t operator()(const sf::Vector2<int>& v) const
{
return v.x + 1812433253 * v.y;
}
};
}
template<typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> v)
{
out << '(' << v.x << ',' << v.y << ')';
return out;
}
namespace sf
{
template<typename T >
bool operator> (const Vector2<T> &left, const Vector2<T> &right)
{
return std::tie(left.x, left.y) > std::tie(right.x, right.y);
}
template<typename T >
bool operator< (const Vector2<T> &left, const Vector2<T> &right)
{
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
}
class Grid
{
using pairs = std::pair<sf::Vector2i, double>;
public:
Grid(int width_, int height_, int block_size, sf::RenderWindow& window)
: width(width_)
, height(height_)
, grid(width_ * height_)
, window(window)
{
const auto tile_size = sf::Vector2f(block_size, block_size);
int i=0;
for(auto& tile : grid)
{
sf::Vector2f position(i % width, i / width); i++;
tile.setSize(tile_size);
tile.setFillColor(sf::Color::White);
tile.setOutlineColor(sf::Color(205,201,201));
tile.setOutlineThickness(-1);
tile.setPosition(position.x * tile_size.x, position.y * tile_size.y );
}
}
void add_wall(int x1, int y1, int x2, int y2)
{
// if(!in_bounds(sf::Vector2i(x1+1, y1+1)) || !in_bounds(sf::Vector2i(x2+1, y2+1)))
// {
// std::cout << "Square::add_wall: invalid parameters: "
// << sf::Vector2i(x1, y1) << ' '
// << sf::Vector2i(x2, y2) <<"\n";
// return;
// }
for (int x = x1; x < x2; ++x)
{
for (int y = y1; y < y2; ++y)
{
walls.insert({ x, y });
grid[x + width * y].setFillColor({139,137,137});
}
}
}
void breadth_first_search(sf::Vector2i start)
{
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
std::queue<sf::Vector2i> frontier;
frontier.push(start);
came_from[start] = start;
while (!frontier.empty())
{
sf::sleep(sf::seconds(0.5f));
auto current = frontier.front();
frontier.pop();
if(current != start && current != goal)
grid[current.x + width * current.y].setFillColor({102,205,170});
for (auto next : neighbors(current))
{
if (!came_from.count(next))
{
frontier.push(next);
came_from[next] = current;
if(next != start && next != goal)
grid[next.x + width * next.y].setFillColor({127,255,212});
}
}
}
}
void breadth_first_search(sf::Vector2i start, sf::Vector2i goal)
{
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
std::queue<sf::Vector2i> frontier;
frontier.push(start);
came_from[start] = start;
while (!frontier.empty())
{
sf::sleep(sf::seconds(0.5f));
auto current = frontier.front();
frontier.pop();
if(current != start && current != goal)
grid[current.x + width * current.y].setFillColor({102,205,170});
if (current == goal) {
reconstruct_path(start, goal);
return;
}
for (auto next : neighbors(current))
{
if (!came_from.count(next))
{
frontier.push(next);
came_from[next] = current;
if(next != start && next != goal)
grid[next.x + width * next.y].setFillColor({127,255,212});
}
}
draw();
}
}
void dijkstra_search(sf::Vector2i start, sf::Vector2i goal)
{
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
std::priority_queue<pairs, std::vector<pairs>, std::greater<pairs>> frontier;
frontier.emplace(start, 0);
came_from[start] = start;
cost_so_far[start] = 0;
while (!frontier.empty())
{
sf::sleep(sf::seconds(0.5f));
auto current = frontier.top().first;
frontier.pop();
if(current != start && current != goal)
grid[current.x + width * current.y].setFillColor({102,205,170});
if (current == goal) {
reconstruct_path(start, goal);
return;
}
for (auto next : neighbors(current))
{
double new_cost = cost_so_far[current] + cost(current, next);
if (!cost_so_far.count(next) || new_cost < cost_so_far[next])
{
cost_so_far[next] = new_cost;
came_from[next] = current;
frontier.emplace(next, new_cost);
if(next != start && next != goal)
grid[next.x + width * next.y].setFillColor({127,255,212});
}
}
draw();
}
}
void a_star_search(sf::Vector2i start, sf::Vector2i goal)
{
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
std::priority_queue<pairs, std::vector<pairs>, std::greater<pairs>> frontier;
frontier.emplace(start, 0);
came_from[start] = start;
cost_so_far[start] = 0;
while (!frontier.empty())
{
sf::sleep(sf::seconds(0.5f));
auto current = frontier.top().first;
frontier.pop();
if(current != start && current != goal)
grid[current.x + width * current.y].setFillColor({102,205,170});
if (current == goal) {
reconstruct_path(start, goal);
return;
}
for (auto next : neighbors(current))
{
std::cout << next << ' ';
double new_cost = cost_so_far[current] + cost(current, next);
if (!cost_so_far.count(next) || new_cost < cost_so_far[next])
{
cost_so_far[next] = new_cost;
double priority = new_cost + heuristic(next, goal);
frontier.emplace(next, priority);
came_from[next] = current;
if(next != start && next != goal)
grid[next.x + width * next.y].setFillColor({127,255,212});
}
}
std::cout << '\n';
draw();
}
}
bool updatePath(sf::Time dt)
{
if (timer <= sf::Time::Zero)
{
timer += sf::seconds(0.0125f);//sf::seconds(0.25f);
if(vbegin == path.end()) {timer = sf::Time::Zero; return false;}
if ( *vbegin != start && *vbegin != goal )
grid[vbegin->x + width * vbegin->y].setFillColor(sf::Color::Yellow);
vbegin++;
}else timer -= dt;
return true;
}
void draw() const
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
for(const auto& tile : grid)
{
window.draw(tile);
}
window.display();
}
std::unordered_set<sf::Vector2i> forests;
private:
inline bool in_bounds(sf::Vector2i id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
inline bool passable(sf::Vector2i id) const
{
return !walls.count(id);
}
std::vector<sf::Vector2i> neighbors(sf::Vector2i id) const
{
static const std::array<sf::Vector2i, 4> DIRS{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1},
// { 1, 1},
// { 1, -1},
// {-1, 1},
// { 1, 1},
}};
std::vector<sf::Vector2i> results;
for (auto dir : DIRS)
{
sf::Vector2i next(id + dir);
if (in_bounds(next) && passable(next))
{
results.emplace_back(next);
}
}
if ((id.x + id.y) % 2 == 0)
{
// aesthetic improvement on square grids
//std::reverse(results.begin(), results.end());
}
return results;
}
double cost(sf::Vector2i from_node, sf::Vector2i to_node) const
{
return forests.count(to_node) ? 5 : 1;
}
inline double heuristic(sf::Vector2i a, sf::Vector2i b)
{
return abs(a.x - b.x) + abs(a.y - b.y);
}
void reconstruct_path(sf::Vector2i start_, sf::Vector2i goal_)
{
start = start_;
goal = goal_;
auto current = goal;
path.push_back(current);
while (current != start) {
current = came_from[current];
path.push_back(current);
}
std::reverse(path.begin(), path.end());
vbegin = path.begin();
}
private:
int width, height;
std::vector<sf::RectangleShape> grid;
std::unordered_set<sf::Vector2i> walls;
std::unordered_map<sf::Vector2i, double> cost_so_far;
std::unordered_map<sf::Vector2i, sf::Vector2i> came_from;
//std::unordered_set<sf::Vector2i> forests;
std::vector<sf::Vector2i> path;
std::vector<sf::Vector2i>::iterator vbegin;
sf::Time timer = sf::Time::Zero;
sf::Vector2i start{};
sf::Vector2i goal{};
sf::RenderWindow& window;
};
Grid make_diagram1(
sf::RenderWindow &window,
int width = 30,
int height = 15,
int block_size = 20)
{
Grid grid(width, height, block_size, window);//(45, 30);
grid.add_wall(3, 3, 5, 12);
grid.add_wall(13, 4, 15, 15);
grid.add_wall(21, 0, 23, 7);
grid.add_wall(23, 5, 26, 7);
return grid;
}
Grid make_diagram4(
sf::RenderWindow &window,
int width = 10,
int height = 10,
int block_size = 20)
{
Grid grid(width, height, block_size, window);
grid.add_wall(1, 7, 4, 9);
// grid.forests = std::unordered_set<sf::Vector2i> {
// {3, 4}, {3, 5}, {4, 1}, {4, 2},
// {4, 3}, {4, 4}, {4, 5}, {4, 6},
// {4, 7}, {4, 8}, {5, 1}, {5, 2},
// {5, 3}, {5, 4}, {5, 5}, {5, 6},
// {5, 7}, {5, 8}, {6, 2}, {6, 3},
// {6, 4}, {6, 5}, {6, 6}, {6, 7},
// {7, 3}, {7, 4}, {7, 5}
// };
return grid;
}
int main()
{
unsigned width = 10;
unsigned height = 10;
unsigned block_size = 20;
sf::RenderWindow window({width*block_size, height*block_size}, "algorithm");
Grid grid = make_diagram4(window);
//grid.breadth_first_search({1, 4}, {8, 5});
grid.dijkstra_search({1, 4}, {8, 5});
//grid.a_star_search({1, 4}, {8, 5});
sf::Clock clock;
const sf::Time TimePerFrame = sf::seconds(1.f / 60.f);
sf::Time timeSinceLastUpdate = sf::Time::Zero;
while(window.isOpen())
{
sf::Time dt = clock.restart();
timeSinceLastUpdate += dt;
while (timeSinceLastUpdate > TimePerFrame)
{
timeSinceLastUpdate -= TimePerFrame;
grid.updatePath(TimePerFrame);
}
grid.draw();
}
}
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <iostream>
#include <iomanip>
#include <unordered_map>
#include <unordered_set>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <set>
#include <functional>
#include <algorithm>
namespace sf
{
template <typename T>
bool operator> (Vector2<T> const& left, Vector2<T> const& right)
{
return std::tie(left.x, left.y) > std::tie(right.x, right.y);
}
template <typename T>
bool operator< (Vector2<T> const& left, Vector2<T> const& right)
{
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
template <typename T>
Vector2<T> operator /(Vector2<T> const& left, Vector2<T> const& right)
{
T x = left.x / right.x;
T y = left.y / right.y;
return {x, y};
}
}
template<typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> v)
{
out << '(' << v.x << ',' << v.y << ')';
return out;
}
template<typename N>
class Graph
{
class Node;
using NodeHolder = std::set<Node>;
public:
using NodeVal = N;
using NodeRef = typename NodeHolder::iterator;
using Edges = std::vector<std::pair<NodeRef, int>>;
private:
class Node
{
N data;
mutable Edges outedge;
public:
Node(N const& data)
: data(data)
{}
void addEdge(NodeRef e, int cost) const
{
outedge.emplace_back(e, cost);
}
NodeVal const& value() const
{
return data;
}
Edges const& getEdges() const
{
return outedge;
}
friend bool operator<(Node const& lhs, Node const& rhs)
{
return lhs.data < rhs.data;
}
};
NodeHolder nodes;
public:
NodeRef addNode(N const& data)
{
auto const& result = nodes.emplace(data);
return result.first;
}
NodeRef getRef(N const& data)
{
auto const& found = nodes.find(data);
if(found == nodes.end())
throw std::runtime_error("Graph::getRef: Key not found!\n");
return found;
}
void addEdge(NodeRef src, NodeRef dst, int cost)
{
if (src != nodes.end() && dst != nodes.end()) {
src->addEdge(dst, cost);
}
}
Edges const& getEdges(N const& node) const
{
auto const& found = nodes.find(node);
if(found == nodes.end())
throw std::runtime_error("Graph::getEdges: node not found!\n");
return found;
}
};
template<typename Graph>
class Dijkstra
{
using NodeRef = typename Graph::NodeRef;
using NodeVal = typename Graph::NodeVal;
struct QueInfo: public std::tuple<NodeRef, int, std::vector<NodeRef>>
{
QueInfo(QueInfo const&) = default;
QueInfo(NodeRef const& data, int cost, std::vector<NodeRef> const& route)
: std::tuple<NodeRef, int, std::vector<NodeRef>>(data, cost, route)
{
std::get<2>(*this).emplace_back(data);
}
friend bool operator<(QueInfo const& lhs, QueInfo const& rhs)
{
return std::get<1>(lhs) > std::get<1>(rhs);
}
};
Graph const& graph;
sf::RenderWindow& window;
std::vector<sf::RectangleShape>& grid;
sf::Vector2i start, goal;
public:
Dijkstra(Graph const& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
sf::Vector2i start,
sf::Vector2i goal)
: graph(graph), window(window), grid(grid), start(start), goal(goal)
{}
std::vector<NodeRef> route(NodeRef const& src, NodeRef const& dst)
{
std::set<NodeVal> found;
std::priority_queue<QueInfo> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while(!frontier.empty())
{
sf::sleep(sf::seconds(0.0125f));
auto next = frontier.top();
frontier.pop();
auto const& current = std::get<0>(next);
if (found.find(current->value()) != found.end()) continue;
if(current->value() != start && current->value() != goal)
paint(current->value(),{102,205,170});
found.emplace(current->value());
auto const& result = std::get<2>(next);
if (current == dst) return result;
for(auto const& loop: current->getEdges())
{
if(loop.first->value() != start && loop.first->value() != goal)
paint(loop.first->value(),{127,255,212});
frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
}
draw();
}
return {};
}
// debug drawing
void paint(sf::Vector2i coord, sf::Color color)
{
grid[coord.x + 10 * coord.y].setFillColor(color);
}
void draw()
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
for(const auto& tile : grid)
{
window.draw(tile);
}
window.display();
}
};
class Grid : public sf::Drawable
{
public:
Grid(int width_,
int height_,
int block_size,
sf::Vector2i start,
sf::Vector2i goal,
sf::RenderWindow& window)
: width(width_), height(height_), grid(width_ * height_)
, start(start), goal(goal), graph()
, dijkstra(graph, window, grid, start, goal)
{
auto tile_size = sf::Vector2f(block_size, block_size);
int i=0;
for(auto& tile : grid)
{
sf::Vector2f position(i % width, i / width); i++;
tile.setSize(tile_size);
tile.setFillColor(sf::Color::White);
tile.setOutlineColor(sf::Color(205,201,201));
tile.setOutlineThickness(-1);
tile.setPosition(position.x * tile_size.x, position.y * tile_size.y );
graph.addNode(sf::Vector2i{position});
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
for(auto& tile : grid)
{
auto p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size) ;
for (auto next : neighbors(p))
graph.addEdge(graph.getRef(p), graph.getRef(next), 1);
}
auto const& result = dijkstra.route(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](auto d){return d->value();});
vbegin = path.begin();
}
bool updatePath(sf::Time dt)
{
if (timer <= sf::Time::Zero)
{
timer += sf::seconds(0.125f);//sf::seconds(0.25f);
if(vbegin == path.end()) {timer = sf::Time::Zero; return false;}
if ( *vbegin != start && *vbegin != goal )
grid[vbegin->x + width * vbegin->y].setFillColor(sf::Color::Yellow);
vbegin++;
}else timer -= dt;
return true;
}
void draw(sf::RenderTarget& target, sf::RenderStates) const
{
for(const auto& tile : grid)
{
target.draw(tile);
}
}
private:
inline bool in_bounds(sf::Vector2i id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
std::vector<sf::Vector2i> neighbors(sf::Vector2i id) const
{
static const std::array<sf::Vector2i, 4> Directions{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1},
// {-1, 1},
// { 1, -1},
// {-1, -1},
// { 1, 1},
}};
std::vector<sf::Vector2i> results;
for (auto direction : Directions)
{
const auto& next(id + direction);
if (in_bounds(next))
{
results.emplace_back(next);
}
}
return results;
}
private:
int width, height;
std::vector<sf::RectangleShape> grid;
std::vector<sf::Vector2i> path;
// for animation
std::vector<sf::Vector2i>::iterator vbegin;
sf::Time timer = sf::Time::Zero;
sf::Vector2i start;
sf::Vector2i goal;
// Algorithm's engine
Graph<sf::Vector2i> graph;
Dijkstra<Graph<sf::Vector2i>> dijkstra;
};
int main()
{
try{
unsigned width = 10;
unsigned height = 10;
unsigned block_size = 20;
sf::RenderWindow window({width*block_size, height*block_size}, "algorithm");
Grid grid(width, height, block_size, {1, 4}, {8, 5}, window);
sf::Clock clock;
const sf::Time TimePerFrame = sf::seconds(1.f / 60.f);
sf::Time timeSinceLastUpdate = sf::Time::Zero;
while(window.isOpen())
{
sf::Time dt = clock.restart();
timeSinceLastUpdate += dt;
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
while (timeSinceLastUpdate > TimePerFrame)
{
timeSinceLastUpdate -= TimePerFrame;
grid.updatePath(TimePerFrame);
}
window.clear();
window.draw(grid);
window.display();
}
} catch(std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << '\n';
return 1;
} catch(...)
{
std::cout << "Unknown Exception!\n";
return 1;
}
}
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <iostream>
#include <iomanip>
#include <unordered_map>
#include <unordered_set>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <set>
#include <functional>
#include <algorithm>
namespace sf
{
template <typename T>
bool operator > (Vector2<T> const& left, Vector2<T> const& right)
{
return std::tie(left.x, left.y) > std::tie(right.x, right.y);
}
template <typename T>
bool operator < (Vector2<T> const& left, Vector2<T> const& right)
{
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
template <typename T>
Vector2<T> operator /(Vector2<T> const& left, Vector2<T> const& right)
{
T x = left.x / right.x;
T y = left.y / right.y;
return {x, y};
}
}
template <typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> v)
{
out << '(' << v.x << ',' << v.y << ')';
return out;
}
template <typename N>
class Graph
{
class Node;
using NodeHolder = std::set<Node>;
public:
using NodeVal = N;
using NodeRef = typename NodeHolder::iterator;
using Edges = std::vector<std::pair<NodeRef, int>>;
private:
class Node
{
N data;
mutable Edges outedge;
public:
Node(N const& data)
: data(data)
{}
void addEdge(NodeRef e, int cost) const
{
outedge.emplace_back(e, cost);
}
NodeVal const& value() const
{
return data;
}
Edges const& getEdges() const
{
return outedge;
}
friend bool operator<(Node const& lhs, Node const& rhs)
{
return lhs.data < rhs.data;
}
};
NodeHolder nodes;
public:
NodeRef addNode(N const& data)
{
auto const& result = nodes.emplace(data);
return result.first;
}
NodeRef getRef(N const& data)
{
auto const& found = nodes.find(data);
if(found == nodes.end())
throw std::runtime_error("Graph::getRef: Key not found!\n");
return found;
}
void addEdge(NodeRef src, NodeRef dst, int cost)
{
if (src != nodes.end() && dst != nodes.end()) {
src->addEdge(dst, cost);
}
}
Edges const& getEdges(N const& node) const
{
auto const& found = nodes.find(node);
if(found == nodes.end())
throw std::runtime_error("Graph::getEdges: node not found!\n");
return found;
}
};
template <typename Graph>
class Dijkstra
{
using NodeRef = typename Graph::NodeRef;
using NodeVal = typename Graph::NodeVal;
struct QueInfo: public std::tuple<NodeRef, int, std::vector<NodeRef>>
{
QueInfo(QueInfo const&) = default;
QueInfo(NodeRef const& data, int cost, std::vector<NodeRef> const& route)
: std::tuple<NodeRef, int, std::vector<NodeRef>>(data, cost, route)
{
std::get<2>(*this).emplace_back(data);
}
friend bool operator<(QueInfo const& lhs, QueInfo const& rhs)
{
return std::get<1>(lhs) > std::get<1>(rhs);
}
};
Graph const& graph;
sf::RenderWindow& window;
std::vector<sf::RectangleShape>& grid;
sf::Vector2i start, goal;
int width;
public:
Dijkstra(Graph const& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
sf::Vector2i start,
sf::Vector2i goal,
int width)
: graph(graph), window(window), grid(grid), start(start), goal(goal), width(width)
{}
std::vector<NodeRef> route(NodeRef const& src, NodeRef const& dst)
{
std::set<NodeVal> found;
std::priority_queue<QueInfo> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while(!frontier.empty())
{
sf::sleep(sf::seconds(0.0125f));
auto next = frontier.top();
frontier.pop();
auto const& current = std::get<0>(next);
if (found.find(current->value()) != found.end()) continue;
if(current->value() != start && current->value() != goal)
paint(current->value(),{102,205,170});
found.emplace(current->value());
auto const& result = std::get<2>(next);
if (current == dst) return result;
for(auto const& loop: current->getEdges())
{
if(loop.first->value() != start && loop.first->value() != goal)
paint(loop.first->value(),{127,255,212});
frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
}
draw();
}
return {};
}
// debug drawing
void paint(sf::Vector2i coord, sf::Color color)
{
grid[coord.x + width * coord.y].setFillColor(color);
}
void draw()
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
for(const auto& tile : grid)
{
window.draw(tile);
}
window.display();
}
};
template <typename Graph>
class Astar
{
using NodeRef = typename Graph::NodeRef;
using NodeVal = typename Graph::NodeVal;
struct QueInfo: public std::tuple<NodeRef, int, std::vector<NodeRef>>
{
QueInfo(QueInfo const&) = default;
QueInfo(NodeRef const& data, int cost, std::vector<NodeRef> const& route)
: std::tuple<NodeRef, int, std::vector<NodeRef>>(data, cost, route)
{
std::get<2>(*this).emplace_back(data);
}
friend bool operator<(QueInfo const& lhs, QueInfo const& rhs)
{
return std::get<1>(lhs) > std::get<1>(rhs);
}
};
Graph const& graph;
sf::RenderWindow& window;
std::vector<sf::RectangleShape>& grid;
sf::Vector2i start, goal;
int width;
public:
Astar(Graph const& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
sf::Vector2i start,
sf::Vector2i goal,
int width)
: graph(graph), window(window), grid(grid), start(start), goal(goal), width(width)
{}
std::vector<NodeRef> route(NodeRef const& src, NodeRef const& dst)
{
std::set<NodeVal> found;
std::priority_queue<QueInfo> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while(!frontier.empty())
{
//sf::sleep(sf::seconds(0.0125f));
auto next = frontier.top();
frontier.pop();
auto const& current = std::get<0>(next);
if (found.find(current->value()) != found.end()) continue;
if(current->value() != start && current->value() != goal)
paint(current->value(),{102,205,170});
found.emplace(current->value());
auto const& result = std::get<2>(next);
if (current == dst) return result;
for(auto const& loop: current->getEdges())
{
if(loop.first->value() != start && loop.first->value() != goal)
paint(loop.first->value(),{127,255,212});
frontier.emplace(loop.first, std::get<1>(next) + loop.second + heuristic(loop.first->value(), goal), result);
}
draw();
}
return {};
}
inline int heuristic(sf::Vector2i a, sf::Vector2i b)
{
return std::abs(a.x - b.x) + std::abs(a.y - b.y);
}
// debug drawing
void paint(sf::Vector2i coord, sf::Color color)
{
grid[coord.x + width * coord.y].setFillColor(color);
}
void draw()
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
for(const auto& tile : grid)
{
window.draw(tile);
}
window.display();
}
};
class Grid : public sf::Drawable
{
public:
Grid(int width_,
int height_,
int block_size,
sf::Vector2i start,
sf::Vector2i goal,
sf::RenderWindow& window)
: width(width_), height(height_), grid(width_ * height_)
, start(start), goal(goal), graph()
, dijkstra(graph, window, grid, start, goal, width)
, astar(graph, window, grid, start, goal, width)
{
auto tile_size = sf::Vector2f(block_size, block_size);
int i=0;
for(auto& tile : grid)
{
sf::Vector2f position(i % width, i / width); i++;
tile.setSize(tile_size);
tile.setFillColor(sf::Color::White);
tile.setOutlineColor(sf::Color(205,201,201));
tile.setOutlineThickness(-1);
tile.setPosition(position.x * tile_size.x, position.y * tile_size.y );
graph.addNode(sf::Vector2i(position));
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
for(auto& tile : grid)
{
auto p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size) ;
for (auto next : neighbors(p))
graph.addEdge(graph.getRef(p), graph.getRef(next), 1);
}
// auto const& result = dijkstra.route(graph.getRef(start), graph.getRef(goal));
//
// std::transform(std::begin(result), std::end(result),
// std::back_inserter(path), [](auto d){return d->value();});
auto const& result = astar.route(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](auto d){return d->value();});
vbegin = path.begin();
}
bool updatePath(sf::Time dt)
{
if (timer <= sf::Time::Zero)
{
timer += sf::seconds(0.125f);//sf::seconds(0.25f);
if(vbegin == path.end()) {timer = sf::Time::Zero; return false;}
if ( *vbegin != start && *vbegin != goal )
grid[vbegin->x + width * vbegin->y].setFillColor(sf::Color::Yellow);
vbegin++;
}else timer -= dt;
return true;
}
void draw(sf::RenderTarget& target, sf::RenderStates) const
{
for(const auto& tile : grid)
{
target.draw(tile);
}
}
private:
inline bool in_bounds(sf::Vector2i id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
std::vector<sf::Vector2i> neighbors(sf::Vector2i id) const
{
static const std::array<sf::Vector2i, 4> Directions{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1},
// {-1, 1},
// { 1, -1},
// {-1, -1},
// { 1, 1},
}};
std::vector<sf::Vector2i> results;
for (auto direction : Directions)
{
const auto& next(id + direction);
if (in_bounds(next))
{
results.emplace_back(next);
}
}
return results;
}
private:
int width, height;
std::vector<sf::RectangleShape> grid;
std::vector<sf::Vector2i> path;
// for animation
std::vector<sf::Vector2i>::iterator vbegin;
sf::Time timer = sf::Time::Zero;
sf::Vector2i start;
sf::Vector2i goal;
// Algorithm's engine
Graph<sf::Vector2i> graph;
Dijkstra<Graph<sf::Vector2i>> dijkstra;
Astar<Graph<sf::Vector2i>> astar;
};
int main()
{
try{
unsigned width = 45;//10;
unsigned height = 30;//10;
unsigned block_size = 20;
sf::RenderWindow window({width*block_size, height*block_size}, "algorithm");
//Grid grid(width, height, block_size, {1, 4}, {8, 5}, window);
Grid grid(width, height, block_size, {5, 20}, {40, 5}, window);
sf::Clock clock;
const sf::Time TimePerFrame = sf::seconds(1.f / 60.f);
sf::Time timeSinceLastUpdate = sf::Time::Zero;
while(window.isOpen())
{
sf::Time dt = clock.restart();
timeSinceLastUpdate += dt;
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
while (timeSinceLastUpdate > TimePerFrame)
{
timeSinceLastUpdate -= TimePerFrame;
grid.updatePath(TimePerFrame);
}
window.clear();
window.draw(grid);
window.display();
}
} catch(std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << '\n';
return 1;
} catch(...)
{
std::cout << "Unknown Exception!\n";
return 1;
}
}
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <iostream>
#include <iomanip>
#include <unordered_map>
#include <unordered_set>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <set>
#include <functional>
#include <algorithm>
namespace std
{
template <>
struct hash<sf::Vector2<int> >
{
inline size_t operator()(const sf::Vector2<int>& location) const
{
return location.x * 1812433253 + location.y;
}
};
}
namespace sf
{
template <typename T>
bool operator > (Vector2<T> const& left, Vector2<T> const& right)
{
return std::tie(left.x, left.y) > std::tie(right.x, right.y);
}
template <typename T>
bool operator < (Vector2<T> const& left, Vector2<T> const& right)
{
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
template <typename T>
Vector2<T> operator /(Vector2<T> const& left, Vector2<T> const& right)
{
T x = left.x / right.x;
T y = left.y / right.y;
return {x, y};
}
}
template <typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> v)
{
out << '(' << v.x << ',' << v.y << ')';
return out;
}
inline double heuristic(sf::Vector2i a, sf::Vector2i b)
{
// auto const& dx = b.x - a.x;//m_location[m_goal].x - m_location[u].x;
// auto const& dy = b.y - a.y;;
// return ::sqrt(dx * dx + dy * dy);
// return std::max(std::abs(a.x - b.x), std::abs(a.y - b.y));
static auto const D = 1;
auto const& dx = abs(a.x - b.x);
auto const& dy = abs(a.y - b.y);
return D * (dx + dy);
//return std::abs(a.x - b.x) + std::abs(a.y - b.y);
}
//{
// assert(std::isless(0, epsilon)); // epsilon is a part of the whole quantity
// assert(std::isless(epsilon, 1));
//
// const auto delta = std::abs(a - b);
// const auto x = std::abs(a);
// const auto y = std::abs(b);
//
// // comparable generally and |a - b| < eps * (|a| + |b|) / 2
// return std::isless(epsilon * y, x)
// && std::isless(epsilon * x, y)
// && std::isless((delta + delta) / (x + y), epsilon);
//};
template <typename N>
class Graph
{
class Node;
using NodeHolder = std::set<Node>;
public:
using NodeVal = N;
using NodeRef = typename NodeHolder::iterator;
using Edges = std::vector<std::pair<NodeRef, double>>;
private:
class Node
{
N data;
mutable Edges outedge;
public:
Node(N const& data)
: data(data)
{}
void addEdge(NodeRef e, double cost) const
{
outedge.emplace_back(e, cost);
}
NodeVal const& value() const
{
return data;
}
Edges const& getEdges() const
{
return outedge;
}
friend bool operator<(Node const& lhs, Node const& rhs)
{
return lhs.data < rhs.data;
}
};
NodeHolder nodes;
public:
NodeRef addNode(N const& data)
{
auto const& result = nodes.emplace(data);
return result.first;
}
NodeRef getRef(N const& data)
{
auto const& found = nodes.find(data);
if(found == nodes.end())
throw std::runtime_error("Graph::getRef: Key not found!\n");
return found;
}
void addEdge(NodeRef src, NodeRef dst, double cost)
{
if (src != nodes.end() && dst != nodes.end()) {
src->addEdge(dst, cost);
}
}
Edges const& getEdges(N const& node) const
{
auto const& found = nodes.find(node);
if(found == nodes.end())
throw std::runtime_error("Graph::getEdges: node not found!\n");
return found;
}
};
template <typename Graph>
struct Base
{
using NodeRef = typename Graph::NodeRef;
using NodeVal = typename Graph::NodeVal;
struct QueInfo: public std::tuple<NodeRef, double, std::vector<NodeRef>>
{
QueInfo(QueInfo const&) = default;
QueInfo(NodeRef const& data, double cost, std::vector<NodeRef> const& route)
: std::tuple<NodeRef, double, std::vector<NodeRef>>(data, cost, route)
{
std::get<2>(*this).emplace_back(data);
}
friend bool operator<(QueInfo const& lhs, QueInfo const& rhs)
{
return std::get<1>(lhs) > std::get<1>(rhs);
}
};
Graph const& graph;
sf::RenderWindow& window;
std::vector<sf::RectangleShape>& grid;
int width;
Base(Graph const& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
int width)
: graph(graph), window(window), grid(grid), width(width)
{}
// debug drawing
void paint(sf::Vector2i coord, sf::Color color)
{
grid[coord.x + width * coord.y].setFillColor(color);
}
void draw()
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
for(const auto& tile : grid)
{
window.draw(tile);
}
window.display();
}
};
template <typename Graph>
class Dijkstra : Base<Graph>
{
using NodeRef = typename Base<Graph>::NodeRef;
using NodeVal = typename Base<Graph>::NodeVal;
public:
Dijkstra(Graph const& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
int width)
: Base<Graph>(graph, window,grid, width)
{}
std::vector<NodeRef> route(NodeRef const& src, NodeRef const& dst)
{
std::set<NodeVal> found;
std::priority_queue<typename Base<Graph>::QueInfo> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while(!frontier.empty())
{
sf::sleep(sf::seconds(0.0125f));
auto next = frontier.top();
frontier.pop();
auto const& current = std::get<0>(next);
if (found.find(current->value()) != found.end()) continue;
if(current->value() != src->value() && current->value() != dst->value())
Base<Graph>::paint(current->value(),{102,205,170});
found.emplace(current->value());
auto const& result = std::get<2>(next);
if (current == dst) return result;
for(auto const& loop: current->getEdges())
{
if(loop.first->value() != src->value() && loop.first->value() != dst->value())
Base<Graph>::paint(loop.first->value(),{127,255,212});
frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
}
Base<Graph>::draw();
}
return {};
}
};
template <typename Graph>
class Astar : Base<Graph>
{
using NodeRef = typename Base<Graph>::NodeRef;
using NodeVal = typename Base<Graph>::NodeVal;
public:
Astar(Graph const& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
int width)
: Base<Graph>(graph, window,grid, width)
{}
std::vector<NodeRef> route(NodeRef const& src, NodeRef const& dst)
{
std::set<NodeVal> found;
std::priority_queue<typename Base<Graph>::QueInfo> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while(!frontier.empty())
{
//sf::sleep(sf::seconds(0.0125f));
auto next = frontier.top();
frontier.pop();
auto const& current = std::get<0>(next);
auto const& value = current->value();
if (found.find(value) != found.end()) continue;
if(value != src->value() && value != dst->value()) Base<Graph>::paint(value,{102,205,170});
found.emplace(value);
auto const& result = std::get<2>(next);
if (current == dst) return result;
// for(auto const& loop: current->getEdges())
// {
// auto const& value = std::get<0>(loop)->value();
// //if(value != start && value != goal) paint(value,{127,255,212});
// auto const& oldCost = std::get<1>(next);
// auto const& newCost = std::get<1>(loop);
// //std::cout << oldCost << ' ' << newCost << ' ' << heuristic(value, goal) <<'\n'; getchar();
// if(heuristic(std::get<0>(next)->value(), goal) > heuristic(value, goal))
// {
// if(value != start && value != goal) paint(value,{127,255,212});
// auto const& totalCost = std::get<1>(next) + std::get<1>(loop);// + heuristic(value, goal);
// frontier.emplace(std::get<0>(loop), totalCost, result);
// }
// }
for(auto const& loop: current->getEdges())
{
// dx1 = current.x - goal.x
// dy1 = current.y - goal.y
// dx2 = start.x - goal.x
// dy2 = start.y - goal.y
// cross = abs(dx1*dy2 - dx2*dy1)
// heuristic += cross*0.001
auto const& value = loop.first->value();
auto const& dx1 = value.x - dst->value().x;
auto const& dy1 = value.y - dst->value().y;
auto const& dx2 = src->value().x - dst->value().x;
auto const& dy2 = src->value().y - dst->value().y;
auto const& cross = abs(dx1*dy2 - dx2*dy1);
auto heuristics = heuristic(value, dst->value());
heuristics += cross*0.001;
//std::cout << heuristics << '\n';
//if(value != start && value != goal) paint(value,{127,255,212});
//auto const& oldCost = std::get<1>(next);
//auto const& newCost = std::get<1>(loop);
//std::cout << oldCost << ' ' << newCost << ' ' << heuristic(value, goal) <<'\n'; getchar();
//if(heuristic(std::get<0>(next)->value(), goal) > heuristic(loop.first->value(), goal))
{
if(value != src->value() && value != dst->value()) Base<Graph>::paint(value,{127,255,212});
//auto const& totalCost = std::get<1>(next) + loop.second;// + heuristic(value, goal);
frontier.emplace(std::get<0>(loop), loop.second + heuristics, result);
}
}
Base<Graph>::draw();
}
return {};
}
};
class Grid : public sf::Drawable
{
public:
Grid(int width_,
int height_,
int block_size,
sf::Vector2i start,
sf::Vector2i goal,
sf::RenderWindow& window)
: width(width_), height(height_), grid(width_ * height_)
, start(start), goal(goal), graph()
, dijkstra(graph, window, grid, width)
, astar(graph, window, grid, width)
{
auto tile_size = sf::Vector2f(block_size, block_size);
int i=0;
for(auto& tile : grid)
{
sf::Vector2f position(i % width, i / width); i++;
tile.setSize(tile_size);
//tile.setFillColor(sf::Color::White);
tile.setOutlineColor(sf::Color(205,201,201));
tile.setOutlineThickness(-1);
tile.setPosition(position.x * tile_size.x, position.y * tile_size.y );
graph.addNode(sf::Vector2i(position));
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
for(auto& tile : grid)
{
auto p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size) ;
for (auto next : neighbors(p))
graph.addEdge(graph.getRef(p), graph.getRef(next), 1);
}
// auto const& result = dijkstra.route(graph.getRef(start), graph.getRef(goal));
//
// std::transform(std::begin(result), std::end(result),
// std::back_inserter(path), [](auto d){return d->value();});
auto const& result = astar.route(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](auto d){return d->value();});
vbegin = path.begin();
}
void setPoint(sf::Vector2i s, sf::Vector2i g)
{
path.clear();
start = s;
goal = g;
reset();
auto const& result = astar.route(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](auto d){return d->value();});
vbegin = path.begin();
}
void reset()
{
for (auto& tile : grid)
{
tile.setFillColor(sf::Color::White);
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
}
//void setPath()
bool updatePath(sf::Time dt)
{
//if (timer <= sf::Time::Zero) {
timer += sf::seconds(0.125f);//sf::seconds(0.25f);
if(vbegin == path.end()) {timer = sf::Time::Zero; return false;}
if ( *vbegin != start && *vbegin != goal )
grid[vbegin->x + width * vbegin->y].setFillColor(sf::Color::Yellow);
vbegin++;
//}else timer -= dt;
return true;
}
void draw(sf::RenderTarget& target, sf::RenderStates) const
{
for(const auto& tile : grid)
{
target.draw(tile);
}
}
private:
inline bool in_bounds(sf::Vector2i id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
std::vector<sf::Vector2i> neighbors(sf::Vector2i id) const
{
static const std::array<sf::Vector2i, 4> Directions{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1},
// {-1, 1},
// { 1, -1},
// {-1, -1},
// { 1, 1},
}};
std::vector<sf::Vector2i> results;
for (auto direction : Directions)
{
const auto& next(id + direction);
if (in_bounds(next))
{
results.emplace_back(next);
}
}
return results;
}
private:
int width, height;
std::vector<sf::RectangleShape> grid;
std::vector<sf::Vector2i> path;
// for animation
std::vector<sf::Vector2i>::iterator vbegin;
sf::Time timer = sf::Time::Zero;
sf::Vector2i start;
sf::Vector2i goal;
// Algorithm's engine
Graph<sf::Vector2i> graph;
Dijkstra<Graph<sf::Vector2i>> dijkstra;
Astar<Graph<sf::Vector2i>> astar;
};
int main()
{
try{
unsigned width = 45;//10;
unsigned height = 30;//10;
unsigned block_size = 20;
sf::RenderWindow window({width*block_size, height*block_size}, "algorithm");
//Grid grid(width, height, block_size, {1, 4}, {8, 5}, window);
Grid grid(width, height, block_size, {5, 20}, {40, 5}, window);
sf::Clock clock;
const sf::Time TimePerFrame = sf::seconds(1.f / 60.f);
sf::Time timeSinceLastUpdate = sf::Time::Zero;
while(window.isOpen())
{
sf::Time dt = clock.restart();
timeSinceLastUpdate += dt;
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
else if(event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::P)
grid.setPoint({10, 25},{35, 10});
}
while (timeSinceLastUpdate > TimePerFrame)
{
timeSinceLastUpdate -= TimePerFrame;
grid.updatePath(TimePerFrame);
}
window.clear();
window.draw(grid);
window.display();
}
} catch(std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << '\n';
return 1;
} catch(...)
{
std::cout << "Unknown Exception!\n";
return 1;
}
}
#include <SFML/Graphics.hpp>
#include <iostream>
#include <iomanip>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <set>
#include <functional>
#include <algorithm>
namespace sf
{
template <typename T>
bool operator > (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) > std::tie(right.x, right.y);
}
template <typename T>
bool operator < (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
template <typename T>
Vector2<T> operator / (const Vector2<T>& left, const Vector2<T>& right)
{
T x = left.x / right.x;
T y = left.y / right.y;
return {x, y};
}
}
template <typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> v)
{
out << '(' << v.x << ',' << v.y << ')';
return out;
}
inline double heuristic(const sf::Vector2i& a, const sf::Vector2i b)
{
//static const auto D = 1;
const auto& dx = std::abs(a.x - b.x);
const auto& dy = std::abs(a.y - b.y);
return /*D * */ (dx + dy);
}
template <typename N>
class Graph
{
class Node;
using NodeHolder = std::set<Node>;
public:
using NodeVal = N;
using NodeRef = typename NodeHolder::iterator;
using Edges = std::vector<std::pair<NodeRef, double>>;
public:
NodeRef addNode(const N& data)
{
auto const& result = nodes.emplace(data);
return result.first;
}
NodeRef getRef(const N& data)
{
const auto& found = nodes.find(data);
if(found == nodes.end())
throw std::runtime_error("Graph::getRef: Key not found!\n");
return found;
}
void addEdge(const NodeRef src, const NodeRef dst, double cost)
{
if (src != nodes.end() && dst != nodes.end()) {
src->addEdge(dst, cost);
}
}
const Edges& getEdges(const N& node) const
{
const auto& found = nodes.find(node);
if(found == nodes.end())
throw std::runtime_error("Graph::getEdges: node not found!\n");
return found;
}
private:
class Node
{
N data;
mutable Edges outedge;
public:
Node(const N& data) : data(data) {}
void addEdge(const NodeRef e, double cost) const
{
outedge.emplace_back(e, cost);
}
const NodeVal& value() const
{
return data;
}
const Edges& getEdges() const
{
return outedge;
}
friend bool operator < (Node const& lhs, Node const& rhs)
{
return lhs.data < rhs.data;
}
};
private:
NodeHolder nodes;
};
template <typename Graph>
class PathFinder
{
public:
PathFinder(const Graph& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
int width)
: graph(graph), window(window), grid(grid), width(width)
{}
protected:
using NodeRef = typename Graph::NodeRef;
using NodeVal = typename Graph::NodeVal;
struct QueInfo : public std::tuple<NodeRef, double, std::vector<NodeRef>>
{
QueInfo(const QueInfo&) = default;
QueInfo(const NodeRef& data, double cost, const std::vector<NodeRef>& route)
: std::tuple<NodeRef, double, std::vector<NodeRef>>(data, cost, route)
{
std::get<2>(*this).emplace_back(data);
}
friend bool operator<(const QueInfo& lhs, const QueInfo& rhs)
{
return std::get<1>(lhs) > std::get<1>(rhs);
}
};
// debug drawing
void paint(const sf::Vector2i& coord, const sf::Color& color)
{
grid[coord.x + width * coord.y].setFillColor(color);
}
void draw()
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
for(const auto& tile : grid)
{
window.draw(tile);
}
window.display();
}
private:
const Graph& graph;
sf::RenderWindow& window;
std::vector<sf::RectangleShape>& grid;
const int width;
};
template <typename Graph>
class Dijkstra : public PathFinder<Graph>
{
using NodeRef = typename PathFinder<Graph>::NodeRef;
using NodeVal = typename PathFinder<Graph>::NodeVal;
using QueInfo = typename PathFinder<Graph>::QueInfo;
public:
Dijkstra(const Graph& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
int width)
: PathFinder<Graph>(graph, window,grid, width)
{}
std::vector<NodeRef> route(const NodeRef& src, const NodeRef& dst) // const
{
std::set<NodeVal> found;
std::priority_queue<QueInfo> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst)
PathFinder<Graph>::paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
if( loop.first != src && loop.first != dst)
PathFinder<Graph>::paint(loop.first->value(),{127,255,212});
frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
}
PathFinder<Graph>::draw();
}
return {};
}
};
template <typename Graph>
class Astar : public PathFinder<Graph>
{
using NodeRef = typename PathFinder<Graph>::NodeRef;
using NodeVal = typename PathFinder<Graph>::NodeVal;
using QueInfo = typename PathFinder<Graph>::QueInfo;
public:
Astar(const Graph& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
int width)
: PathFinder<Graph>(graph, window,grid, width)
{}
std::vector<NodeRef> route(const NodeRef& src, const NodeRef& dst) //const
{
std::set<NodeVal> found;
std::priority_queue<QueInfo> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
const auto& start = src->value();
const auto& goal = dst->value();
while(!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if(current != src && current != dst)
PathFinder<Graph>::paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for(const auto& loop: current->getEdges())
{
const auto& value = loop.first->value();
const auto& d1 = value - goal;
const auto& d2 = start - goal;
const auto& cross = std::abs(d1.x*d2.y - d2.x*d1.y);
auto heuristics = heuristic(value, goal);
heuristics += cross*0.001;
if(loop.first != src && loop.first != dst)
PathFinder<Graph>::paint(value,{127,255,212});
frontier.emplace(loop.first, loop.second + heuristics, result);
}
PathFinder<Graph>::draw();
}
return {};
}
};
class Grid : public sf::Drawable
{
public:
Grid(int width,
int height,
int block_size,
sf::Vector2i start,
sf::Vector2i goal,
sf::RenderWindow& window)
: width(width), height(height), grid(width * height)
, start(start), goal(goal), graph()
, dijkstra(graph, window, grid, width)
, astar(graph, window, grid, width)
{
const auto& tile_size = sf::Vector2f(block_size, block_size);
int i=0;
for(auto& tile : grid)
{
sf::Vector2f position(i % width, i / width); i++;
tile.setSize(tile_size);
tile.setOutlineColor(sf::Color(205,201,201));
tile.setOutlineThickness(-1);
tile.setPosition(position.x * tile_size.x, position.y * tile_size.y );
graph.addNode(sf::Vector2i(position));
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
for(auto& tile : grid)
{
const auto& p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size) ;
for (const auto& next : neighbors(p))
graph.addEdge(graph.getRef(p), graph.getRef(next), 1);
}
// auto const& result = dijkstra.route(graph.getRef(start), graph.getRef(goal));
//
// std::transform(std::begin(result), std::end(result),
// std::back_inserter(path), [](auto d){return d->value();});
auto const& result = astar.route(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](auto d){return d->value();});
vbegin = path.begin();
}
void setPoints(const sf::Vector2i& s, const sf::Vector2i& g)
{
path.clear();
start = s;
goal = g;
reset();
const auto& result = astar.route(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](auto d){return d->value();});
vbegin = path.begin();
}
void reset()
{
for (auto& tile : grid)
{
tile.setFillColor(sf::Color::White);
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
}
void updatePath()
{
if(vbegin == path.end()) return;
if ( *vbegin != start && *vbegin != goal )
grid[vbegin->x + width * vbegin->y].setFillColor(sf::Color::Yellow);
vbegin++;
}
void draw(sf::RenderTarget& target, sf::RenderStates) const
{
for(const auto& tile : grid)
{
target.draw(tile);
}
}
private:
inline bool in_bounds(const sf::Vector2i& id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
std::vector<sf::Vector2i> neighbors(const sf::Vector2i& id) const
{
static const std::array<sf::Vector2i, 4> Directions{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1},
// {-1, 1},
// { 1, -1},
// {-1, -1},
// { 1, 1},
}};
std::vector<sf::Vector2i> results;
for (auto direction : Directions)
{
const auto& next(id + direction);
if (in_bounds(next))
{
results.emplace_back(next);
}
}
return results;
}
private:
int width, height;
std::vector<sf::RectangleShape> grid;
std::vector<sf::Vector2i> path;
// for animation
std::vector<sf::Vector2i>::iterator vbegin;
sf::Time timer = sf::Time::Zero;
sf::Vector2i start;
sf::Vector2i goal;
// Algorithm's engine
Graph<sf::Vector2i> graph;
Dijkstra<Graph<sf::Vector2i>> dijkstra;
Astar<Graph<sf::Vector2i>> astar;
};
int main()
{
try{
unsigned width = 45;//10;
unsigned height = 30;//10;
unsigned block_size = 20;
sf::RenderWindow window({width*block_size, height*block_size}, "algorithm");
//Grid grid(width, height, block_size, {1, 4}, {8, 5}, window);
Grid grid(width, height, block_size, {5, 20}, {40, 5}, window);
while(window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
else if(event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::P)
grid.setPoints({10, 25},{35, 10});
}
grid.updatePath();
window.clear();
window.draw(grid);
window.display();
}
} catch(std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << '\n';
return 1;
} catch(...)
{
std::cout << "Unknown Exception!\n";
return 1;
}
}
#include <SFML/Graphics.hpp>
#include <iostream>
#include <iomanip>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <set>
#include <algorithm>
namespace sf
{
template <typename T>
bool operator > (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) > std::tie(right.x, right.y);
}
template <typename T>
bool operator < (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
template <typename T>
Vector2<T> operator / (const Vector2<T>& left, const Vector2<T>& right)
{
T x = left.x / right.x;
T y = left.y / right.y;
return {x, y};
}
}
template <typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> v)
{
out << '(' << v.x << ',' << v.y << ')';
return out;
}
inline double heuristic(const sf::Vector2i& a, const sf::Vector2i b)
{
//static const auto D = 1;
const auto& dx = std::abs(a.x - b.x);
const auto& dy = std::abs(a.y - b.y);
return /*D * */ (dx + dy);
}
template <typename N>
class Graph
{
class Node;
using NodeHolder = std::set<Node>;
public:
using NodeVal = N;
using NodeRef = typename NodeHolder::iterator;
using Edges = std::vector<std::pair<NodeRef, double>>;
public:
NodeRef addNode(const N& data)
{
const auto& result = nodes.emplace(data);
return result.first;
}
NodeRef getRef(const N& data)
{
const auto& found = nodes.find(data);
if (found == nodes.end())
throw std::runtime_error("Graph::getRef: Key not found!\n");
return found;
}
void addEdge(const NodeRef src, const NodeRef dst, double cost)
{
if (src != nodes.end() && dst != nodes.end())
src->addEdge(dst, cost);
}
private:
class Node
{
N data;
mutable Edges outedge;
public:
Node(const N& data) : data(data) {}
void addEdge(const NodeRef e, double cost) const
{
outedge.emplace_back(e, cost);
}
const NodeVal& value() const
{
return data;
}
const Edges& getEdges() const
{
return outedge;
}
friend bool operator < (Node const& lhs, Node const& rhs)
{
return lhs.data < rhs.data;
}
};
private:
NodeHolder nodes;
};
template <typename Graph>
class PathFinder
{
using NodeRef = typename Graph::NodeRef;
using NodeVal = typename Graph::NodeVal;
struct QueueHelper : public std::tuple<NodeRef, double, std::vector<NodeRef>>
{
QueueHelper(const QueueHelper&) = default;
QueueHelper(const NodeRef& data, double cost, const std::vector<NodeRef>& route)
: std::tuple<NodeRef, double, std::vector<NodeRef>>(data, cost, route)
{
std::get<2>(*this).emplace_back(data);
}
friend bool operator < (const QueueHelper& lhs, const QueueHelper& rhs)
{
return std::get<1>(lhs) > std::get<1>(rhs);
}
};
public:
PathFinder(const Graph& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
int width)
: graph(graph), window(window), grid(grid), width(width)
{}
std::vector<NodeRef> routeAstar(const NodeRef& src, const NodeRef& dst) //const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
const auto& start = src->value();
const auto& goal = dst->value();
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
const auto& value = loop.first->value();
const auto& d1 = value - goal;
const auto& d2 = start - goal;
const auto& cross = std::abs(d1.x*d2.y - d2.x*d1.y);
auto heuristics = heuristic(value, goal);
heuristics += cross*0.001;
if(loop.first != src && loop.first != dst)
paint(value,{127,255,212});
frontier.emplace(loop.first, loop.second + heuristics, result);
}
draw();
}
return {};
}
std::vector<NodeRef> routeDijkstra(const NodeRef& src, const NodeRef& dst) // const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
if( loop.first != src && loop.first != dst)
paint(loop.first->value(),{127,255,212});
frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
}
draw();
}
return {};
}
private:
// debug drawing
void paint(const sf::Vector2i& coord, const sf::Color& color)
{
grid[coord.x + width * coord.y].setFillColor(color);
}
void draw()
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
for (const auto& tile : grid)
{
window.draw(tile);
}
window.display();
}
private:
const Graph& graph;
sf::RenderWindow& window;
std::vector<sf::RectangleShape>& grid;
const int width;
};
class Grid : public sf::Drawable
{
public:
Grid(int width,
int height,
int block_size,
sf::Vector2i start,
sf::Vector2i goal,
sf::RenderWindow& window)
: width(width), height(height), grid(width * height)
, start(start), goal(goal), graph()
, pathFinder(graph, window, grid, width)
{
const auto& tile_size = sf::Vector2f(block_size, block_size);
int i=0;
for (auto& tile : grid)
{
sf::Vector2f position(i % width, i / width); i++;
tile.setSize(tile_size);
tile.setOutlineColor(sf::Color(205,201,201));
tile.setOutlineThickness(-1);
tile.setPosition(position.x * tile_size.x, position.y * tile_size.y );
graph.addNode(sf::Vector2i(position));
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
for (auto& tile : grid)
{
const auto& p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size) ;
for (const auto& next : neighbors(p))
graph.addEdge(graph.getRef(p), graph.getRef(next), 1);
}
const auto& result = pathFinder.routeDijkstra(graph.getRef(start), graph.getRef(goal));
// const auto& result = pathFinder.routeAstar(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](const auto& d){return d->value();});
begin = path.begin();
}
void setPoints(const sf::Vector2i& s, const sf::Vector2i& g)
{
path.clear();
start = s;
goal = g;
reset();
const auto& result = pathFinder.routeAstar(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](const auto& d){return d->value();});
begin = path.begin();
}
void reset()
{
for (auto& tile : grid)
{
tile.setFillColor(sf::Color::White);
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
}
void updatePath()
{
if (begin == path.end()) return;
if ( *begin != start && *begin != goal )
grid[begin->x + width * begin->y].setFillColor(sf::Color::Yellow);
begin++;
}
void draw(sf::RenderTarget& target, sf::RenderStates) const
{
for (const auto& tile : grid)
{
target.draw(tile);
}
}
private:
inline bool in_bounds(const sf::Vector2i& id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
std::vector<sf::Vector2i> neighbors(const sf::Vector2i& id) const
{
static const std::array<sf::Vector2i, 4> Directions{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1},
// {-1, 1},
// { 1, -1},
// {-1, -1},
// { 1, 1},
}};
std::vector<sf::Vector2i> results;
for (auto direction : Directions)
{
const auto& next(id + direction);
if (in_bounds(next))
{
results.emplace_back(next);
}
}
return results;
}
private:
const int width, height;
std::vector<sf::RectangleShape> grid;
std::vector<sf::Vector2i> path;
// for animation
std::vector<sf::Vector2i>::iterator begin;
sf::Time timer = sf::Time::Zero;
sf::Vector2i start;
sf::Vector2i goal;
// Algorithm's engine
Graph<sf::Vector2i> graph;
PathFinder<Graph<sf::Vector2i>> pathFinder;
};
int main()
{
try{
unsigned width = 45;
unsigned height = 30;
unsigned block_size = 20;
sf::RenderWindow window({width*block_size, height*block_size}, "algorithm");
Grid grid(width, height, block_size, {5, 20}, {40, 5}, window);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
else if (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::P)
grid.setPoints({10, 25},{35, 10});
}
grid.updatePath();
window.clear();
window.draw(grid);
window.display();
}
} catch (std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << '\n';
return 1;
} catch (...)
{
std::cout << "Unknown Exception!\n";
return 1;
}
}
#include <SFML/Graphics.hpp>
#include <iostream>
#include <iomanip>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <set>
#include <algorithm>
#include <chrono>
#include <thread>
#include <future>
namespace sf
{
template <typename T>
bool operator > (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) > std::tie(right.x, right.y);
}
template <typename T>
bool operator < (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
template <typename T>
Vector2<T> operator / (const Vector2<T>& left, const Vector2<T>& right)
{
T x = left.x / right.x;
T y = left.y / right.y;
return {x, y};
}
//template <typename T>
bool operator > (const Color& left, const Color& right)
{
return std::tie(left.r, left.g, left.b) > std::tie(right.r, right.g, right.b);
}
//template <typename T>
bool operator < (const Color& left, const Color& right)
{
return std::tie(left.r, left.g, left.b) < std::tie(right.r, right.g, right.b);
}
}
template <typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> v)
{
out << '(' << v.x << ',' << v.y << ')';
return out;
}
inline double heuristic(const sf::Vector2i& a, const sf::Vector2i b)
{
//static const auto D = 1;
const auto& dx = std::abs(a.x - b.x);
const auto& dy = std::abs(a.y - b.y);
return /*D * */ (dx + dy);
}
template <typename N>
class Graph
{
class Node;
using NodeHolder = std::set<Node>;
public:
using NodeVal = N;
using NodeRef = typename NodeHolder::iterator;
using Edges = std::vector<std::pair<NodeRef, double>>;
public:
NodeRef addNode(const N& data)
{
const auto& result = nodes.emplace(data);
return result.first;
}
NodeRef getRef(const N& data)
{
const auto& found = nodes.find(data);
if (found == nodes.end())
throw std::runtime_error("Graph::getRef: Key not found!\n");
return found;
}
void addEdge(const NodeRef src, const NodeRef dst, double cost)
{
if (src != nodes.end() && dst != nodes.end())
src->addEdge(dst, cost);
}
void clear()
{
nodes.clear();
}
private:
class Node
{
N data;
mutable Edges outedge;
public:
Node(const N& data) : data(data) {}
void addEdge(const NodeRef e, double cost) const
{
outedge.emplace_back(e, cost);
}
const NodeVal& value() const
{
return data;
}
const Edges& getEdges() const
{
return outedge;
}
friend bool operator < (Node const& lhs, Node const& rhs)
{
return lhs.data < rhs.data;
}
};
private:
NodeHolder nodes;
};
template <typename Graph>
class PathFinder
{
using NodeRef = typename Graph::NodeRef;
using NodeVal = typename Graph::NodeVal;
struct QueueHelper : public std::tuple<NodeRef, double, std::vector<NodeRef>>
{
QueueHelper(const QueueHelper&) = default;
QueueHelper(const NodeRef& data, double cost, const std::vector<NodeRef>& route)
: std::tuple<NodeRef, double, std::vector<NodeRef>>(data, cost, route)
{
std::get<2>(*this).emplace_back(data);
}
friend bool operator < (const QueueHelper& lhs, const QueueHelper& rhs)
{
return std::get<1>(lhs) > std::get<1>(rhs);
}
};
public:
PathFinder(const Graph& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
int width)
: graph(graph), window(window), grid(grid), width(width)
{}
std::vector<NodeRef> routeAstar(const NodeRef& src, const NodeRef& dst) //const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
const auto& start = src->value();
const auto& goal = dst->value();
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
const auto& value = loop.first->value();
const auto& d1 = value - goal;
const auto& d2 = start - goal;
const auto& cross = std::abs(d1.x*d2.y - d2.x*d1.y);
auto heuristics = heuristic(value, goal);
heuristics += cross*0.001;
if(loop.first != src && loop.first != dst)
paint(value,{127,255,212});
frontier.emplace(loop.first, loop.second + heuristics, result);
}
draw();
}
return {};
}
std::vector<NodeRef> routeDijkstra(const NodeRef& src, const NodeRef& dst) // const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
if( loop.first != src && loop.first != dst)
paint(loop.first->value(),{127,255,212});
frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
}
draw();
}
return {};
}
private:
// debug drawing
void paint(const sf::Vector2i& coord, const sf::Color& color)
{
grid[coord.x + width * coord.y].setFillColor(color);
}
void draw()
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
for (const auto& tile : grid)
{
window.draw(tile);
}
window.display();
}
private:
const Graph& graph;
sf::RenderWindow& window;
std::vector<sf::RectangleShape>& grid;
const int width;
};
class Grid : public sf::Drawable
{
public:
enum Algorithm
{
None = 0,
Dijkstra = 1 << 0,
Astar = 1 << 1,
};
public:
Grid(int width,
int height,
int block_size,
sf::Vector2i start,
sf::Vector2i goal,
sf::RenderWindow& window)
: width(width), height(height), tile_size(block_size, block_size), grid(width * height)
, start(start), goal(goal), algorithm(Algorithm::Dijkstra), graph()
, pathFinder(graph, window, grid, width)
{
int i=0;
for (auto& tile : grid)
{
sf::Vector2f position(i % width, i / width); i++;
tile.setSize(tile_size);
tile.setOutlineColor(sf::Color(205,201,201));
tile.setOutlineThickness(-1);
tile.setPosition(position.x * tile_size.x, position.y * tile_size.y );
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
begin = path.end();
}
void setAlgorithmType(Algorithm a)
{
algorithm = a;
reset();
std::vector<Graph<sf::Vector2i>::NodeRef> result;
if (algorithm & Algorithm::Dijkstra)
result = pathFinder.routeDijkstra(graph.getRef(start), graph.getRef(goal));
else
result = pathFinder.routeAstar(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](const auto& d){return d->value();});
begin = path.begin();
}
void setPoints(const sf::Vector2i& s, const sf::Vector2i& g)
{
start = s;
goal = g;
reset();
}
void updatePath()
{
if (begin == path.end()) return;
if ( *begin != start && *begin != goal )
grid[begin->x + width * begin->y].setFillColor(sf::Color::Yellow);
begin++;
}
void addWall(const sf::Vector2i& position)
{
if(position == start || position == goal) return;
auto& tile = grid[position.x + width * position.y];
if (passable(position))tile.setFillColor(Gray);
else tile.setFillColor(sf::Color::White);
}
void movePoints(const sf::Vector2i& position)
{
//if(position != start && position != goal) return;
//if(position == start)
auto& tile = grid[position.x + width * position.y];
tile.setFillColor(sf::Color::Red);
}
private:
void draw(sf::RenderTarget& target, sf::RenderStates) const
{
for (const auto& tile : grid)
{
target.draw(tile);
}
}
void reset()
{
path.clear();
begin = path.end();
for (auto& tile : grid)
{
if (tile.getFillColor() == Gray) continue;
tile.setFillColor(sf::Color::White);
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
graph.clear();
for (const auto& tile : grid)
{
const auto& p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size);
if (!passable(p)) continue;
graph.addNode(p);
}
for (const auto& tile : grid)
{
const auto& p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size);
if (!passable(p)) continue;
for (const auto& next : neighbors(p))
graph.addEdge(graph.getRef(p), graph.getRef(next), 1);
}
}
inline bool in_bounds(const sf::Vector2i& id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
inline bool passable(const sf::Vector2i& position) const
{
auto& tile = grid[position.x + width * position.y];
return tile.getFillColor() != Gray;
}
std::vector<sf::Vector2i> neighbors(const sf::Vector2i& id) const
{
static const std::array<sf::Vector2i, 4> Directions{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1},
}};
std::vector<sf::Vector2i> results;
for (auto direction : Directions)
{
const auto& next(id + direction);
if (in_bounds(next) && passable(next))
{
results.emplace_back(next);
}
}
return results;
}
private:
static const sf::Color Gray;
const int width, height;
sf::Vector2f tile_size;
std::vector<sf::RectangleShape> grid;
std::vector<sf::Vector2i> path;
// for animation
std::vector<sf::Vector2i>::iterator begin;
sf::Vector2i start;
sf::Vector2i goal;
// Algorithm's engine
Algorithm algorithm;
Graph<sf::Vector2i> graph;
PathFinder<Graph<sf::Vector2i>> pathFinder;
};
const sf::Color Grid::Gray = sf::Color{139,137,137};
int main()
{
try{
unsigned width = 45;
unsigned height = 30;
int block_size = 20;
sf::RenderWindow window({width*block_size, height*block_size}, "Path Finders - Algorithms");
window.setFramerateLimit(60);
Grid grid(width, height, block_size, {5, 20}, {40, 5}, window);
unsigned algorithm = Grid::Dijkstra;
const unsigned toggle = (Grid::Dijkstra ^ Grid::Astar);
unsigned onlyOnce = 1;
enum Mode
{
None = 0, // alway get back to edit
Start = 1 << 0,
Restart = 1 << 1,
ClearPath = 1 << 2,
ClearWall = 1 << 3,
};
unsigned mode = None;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type){
case sf::Event::Closed:window.close(); break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Escape)
window.close();
else if (event.key.code == sf::Keyboard::P)
{
grid.setPoints({10, 25},{35, 10});
}
else if (event.key.code == sf::Keyboard::A)
{
onlyOnce ^= 1;
}
else if (event.key.code == sf::Keyboard::S)
{
mode = Start;
}
break;
case sf::Event::MouseButtonPressed:
{
const auto& position = sf::Mouse::getPosition(window);
auto coord = static_cast<sf::Vector2i>(window.mapPixelToCoords(position));
coord /= block_size;
grid.addWall(coord);
}
break;
case sf::Event::MouseButtonReleased:
break;
default:break;
};
}
// TODO: need algorithm https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
// if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
// {
// sf::Vector2i position = sf::Mouse::getPosition(window);
// sf::Vector2i coord = static_cast<sf::Vector2i>(window.mapPixelToCoords(position));
// coord /= block_size;
// grid.movePoints(coord);
// grid.addWall(coord);
// }
if(mode & Mode::Start){
if ((algorithm & Grid::Dijkstra) && onlyOnce)
{
algorithm ^= toggle;
grid.setAlgorithmType(Grid::Dijkstra);
}
else if((algorithm & Grid::Astar) && !onlyOnce)
{
algorithm ^= toggle;
grid.setAlgorithmType(Grid::Astar);
}
//mode = None;
}
grid.updatePath();
window.clear();
window.draw(grid);
window.display();
}
} catch (std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << '\n';
return 1;
} catch (...)
{
std::cout << "Unknown Exception!\n";
return 1;
}
}
#include <SFML/Graphics.hpp>
#include <iostream>
#include <iomanip>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <set>
#include <algorithm>
namespace sf
{
template <typename T>
bool operator > (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) > std::tie(right.x, right.y);
}
template <typename T>
bool operator < (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
template <typename T>
Vector2<T> operator / (const Vector2<T>& left, const Vector2<T>& right)
{
if (right.x == 0 || right.y == 0 ) return left;
T x = left.x / right.x;
T y = left.y / right.y;
return {x, y};
}
}
template <typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> v)
{
out << '(' << v.x << ',' << v.y << ')';
return out;
}
inline double heuristic(const sf::Vector2i& a, const sf::Vector2i b)
{
//static const auto D = 1;
const auto& dx = std::abs(a.x - b.x);
const auto& dy = std::abs(a.y - b.y);
return /*D * */ (dx + dy);
}
template <typename N>
class Graph
{
class Node;
using NodeHolder = std::set<Node>;
public:
using NodeVal = N;
using NodeRef = typename NodeHolder::iterator;
using Edges = std::vector<std::pair<NodeRef, double>>;
public:
NodeRef addNode(const N& data)
{
const auto& result = nodes.emplace(data);
return result.first;
}
NodeRef getRef(const N& data)
{
const auto& found = nodes.find(data);
if (found == nodes.end())
throw std::runtime_error("Graph::getRef: Key not found!\n");
return found;
}
void addEdge(const NodeRef src, const NodeRef dst, double cost)
{
if (src != nodes.end() && dst != nodes.end())
src->addEdge(dst, cost);
}
void clear()
{
nodes.clear();
}
private:
class Node
{
N data;
mutable Edges outedge;
public:
Node(const N& data) : data(data) {}
void addEdge(const NodeRef e, double cost) const
{
outedge.emplace_back(e, cost);
}
const NodeVal& value() const
{
return data;
}
const Edges& getEdges() const
{
return outedge;
}
friend bool operator < (Node const& lhs, Node const& rhs)
{
return lhs.data < rhs.data;
}
};
private:
NodeHolder nodes;
};
template <typename Graph>
class PathFinder
{
using NodeRef = typename Graph::NodeRef;
using NodeVal = typename Graph::NodeVal;
struct QueueHelper : public std::tuple<NodeRef, double, std::vector<NodeRef>>
{
QueueHelper(const QueueHelper&) = default;
QueueHelper(const NodeRef& data, double cost, const std::vector<NodeRef>& route)
: std::tuple<NodeRef, double, std::vector<NodeRef>>(data, cost, route)
{
std::get<2>(*this).emplace_back(data);
}
friend bool operator < (const QueueHelper& lhs, const QueueHelper& rhs)
{
return std::get<1>(lhs) > std::get<1>(rhs);
}
};
public:
PathFinder(const Graph& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
int width)
: graph(graph), window(window), grid(grid), width(width)
{}
std::vector<NodeRef> routeAstar(const NodeRef& src, const NodeRef& dst) //const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
const auto& start = src->value();
const auto& goal = dst->value();
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
const auto& value = loop.first->value();
const auto& d1 = value - goal;
const auto& d2 = start - goal;
const auto& cross = std::abs(d1.x*d2.y - d2.x*d1.y);
auto heuristics = heuristic(value, goal);
heuristics += cross*0.001;
if(loop.first != src && loop.first != dst)
paint(value,{127,255,212});
frontier.emplace(loop.first, loop.second + heuristics, result);
}
draw();
}
return {};
}
std::vector<NodeRef> routeDijkstra(const NodeRef& src, const NodeRef& dst) // const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
if( loop.first != src && loop.first != dst)
paint(loop.first->value(),{127,255,212});
frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
}
draw();
}
return {};
}
private:
// debug drawing
void paint(const sf::Vector2i& coord, const sf::Color& color)
{
grid[coord.x + width * coord.y].setFillColor(color);
}
void draw()
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
for (const auto& tile : grid)
{
window.draw(tile);
}
window.display();
}
private:
const Graph& graph;
sf::RenderWindow& window;
std::vector<sf::RectangleShape>& grid;
const int width;
};
class Grid : public sf::Drawable
{
public:
enum Algorithm
{
None = 0,
Dijkstra = 1 << 0,
Astar = 1 << 1,
};
public:
Grid(int width,
int height,
int block_size,
sf::Vector2i start,
sf::Vector2i goal,
sf::RenderWindow& window)
: width(width), height(height), tile_size(block_size, block_size), grid(width * height)
, start(start), goal(goal), algorithm(Algorithm::Dijkstra), graph()
, pathFinder(graph, window, grid, width)
{
int i=0;
for (auto& tile : grid)
{
sf::Vector2f position(i % width, i / width); i++;
tile.setSize(tile_size);
tile.setOutlineColor(sf::Color(205,201,201));
tile.setOutlineThickness(-1);
tile.setPosition(position.x * tile_size.x, position.y * tile_size.y );
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
begin = path.end();
}
void setAlgorithmType(Algorithm a)
{
algorithm = a;
reset();
std::vector<Graph<sf::Vector2i>::NodeRef> result;
if (algorithm & Algorithm::Dijkstra)
result = pathFinder.routeDijkstra(graph.getRef(start), graph.getRef(goal));
else
result = pathFinder.routeAstar(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](const auto& d){return d->value();});
begin = path.begin();
}
void setPoints(const sf::Vector2i& s, const sf::Vector2i& g)
{
start = s;
goal = g;
reset();
}
void updatePath()
{
if (begin == path.end()) return;
if ( *begin != start && *begin != goal )
grid[begin->x + width * begin->y].setFillColor(sf::Color::Yellow);
begin++;
}
void addWall(const sf::Vector2i& position)
{
if(position == start || position == goal || !in_bounds(position)) return;
auto& tile = grid[position.x + width * position.y];
if (passable(position))tile.setFillColor(Gray);
else tile.setFillColor(sf::Color::White);
}
void movePoints(const sf::Vector2i& position)
{
//if(position != start && position != goal) return;
//if(position == start)
auto& tile = grid[position.x + width * position.y];
tile.setFillColor(sf::Color::Red);
}
private:
void draw(sf::RenderTarget& target, sf::RenderStates) const
{
for (const auto& tile : grid)
{
target.draw(tile);
}
}
void reset()
{
path.clear();
begin = path.end();
for (auto& tile : grid)
{
if (tile.getFillColor() == Gray) continue;
tile.setFillColor(sf::Color::White);
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
graph.clear();
for (const auto& tile : grid)
{
const auto& p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size);
if (!passable(p)) continue;
graph.addNode(p);
}
for (const auto& tile : grid)
{
const auto& p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size);
if (!passable(p)) continue;
for (const auto& next : neighbors(p))
graph.addEdge(graph.getRef(p), graph.getRef(next), 1);
}
}
inline bool in_bounds(const sf::Vector2i& id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
inline bool passable(const sf::Vector2i& position) const
{
auto& tile = grid[position.x + width * position.y];
return tile.getFillColor() != Gray;
}
std::vector<sf::Vector2i> neighbors(const sf::Vector2i& id) const
{
static const std::array<sf::Vector2i, 4> Directions{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1},
}};
std::vector<sf::Vector2i> results;
for (auto direction : Directions)
{
const auto& next(id + direction);
if (in_bounds(next) && passable(next))
{
results.emplace_back(next);
}
}
return results;
}
private:
static const sf::Color Gray;
const int width, height;
const sf::Vector2f tile_size;
std::vector<sf::RectangleShape> grid;
std::vector<sf::Vector2i> path;
// for animation
std::vector<sf::Vector2i>::iterator begin;
sf::Vector2i start;
sf::Vector2i goal;
// Algorithm's engine
Algorithm algorithm;
Graph<sf::Vector2i> graph;
PathFinder<Graph<sf::Vector2i>> pathFinder;
};
const sf::Color Grid::Gray = sf::Color{139,137,137};
int main()
{
try{
unsigned width = 45;
unsigned height = 30;
int block_size = 20;
sf::RenderWindow window({width*block_size, height*block_size}, "Path Finders - Algorithms");
window.setFramerateLimit(60);
Grid grid(width, height, block_size, {5, 20}, {40, 5}, window);
unsigned algorithm = Grid::Dijkstra;
const unsigned toggle = (Grid::Dijkstra ^ Grid::Astar);
unsigned onlyOnce = 1;
enum State
{
None = 0, // always get back to edit state
Start = 1 << 0,
Restart = 1 << 1,
ClearPath = 1 << 2,
ClearWall = 1 << 3,
};
unsigned state = None;
bool mouse = false;
std::set<sf::Vector2i> positions;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type){
case sf::Event::Closed:window.close(); break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Escape)
window.close();
else if (event.key.code == sf::Keyboard::P)
{
grid.setPoints({10, 25},{35, 10});
}
else if (event.key.code == sf::Keyboard::A)
{
onlyOnce ^= 1;
}
else if (event.key.code == sf::Keyboard::S)
{
state = Start;
}
break;
case sf::Event::MouseButtonPressed:
{
//if(!mouse){
const auto& position = sf::Mouse::getPosition(window);
auto coord = static_cast<sf::Vector2i>(window.mapPixelToCoords(position));
coord /= block_size;
grid.addWall(coord);
mouse = !mouse;
//}
}
break;
case sf::Event::MouseButtonReleased:
if (event.mouseButton.button == sf::Mouse::Left)
{
positions.clear();
mouse = !mouse;
}
break;
case sf::Event::MouseMoved:
{
if(!mouse) break;
const auto& position = sf::Mouse::getPosition(window);
auto coord = static_cast<sf::Vector2i>(window.mapPixelToCoords(position));
coord /= block_size;
positions.emplace(coord);
if(positions.size() > 1)
{
grid.addWall(*std::next(positions.begin()));
positions.clear();
}
}
break;
default:break;
};
}
if(state & State::Start)
{
if ((algorithm & Grid::Dijkstra) && onlyOnce)
{
algorithm ^= toggle;
grid.setAlgorithmType(Grid::Dijkstra);
}
else if((algorithm & Grid::Astar) && !onlyOnce)
{
algorithm ^= toggle;
grid.setAlgorithmType(Grid::Astar);
}
//state = None;
}
grid.updatePath();
window.clear();
window.draw(grid);
window.display();
}
} catch (std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << '\n';
return 1;
} catch (...)
{
std::cout << "Unknown Exception!\n";
return 1;
}
}
#include <SFML/Graphics.hpp>
#include <iostream>
#include <iomanip>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <set>
#include <algorithm>
#include <random>
namespace sf
{
template <typename T>
bool operator > (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) > std::tie(right.x, right.y);
}
template <typename T>
bool operator < (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
template <typename T>
Vector2<T> operator / (const Vector2<T>& left, const Vector2<T>& right)
{
if (right.x == 0 || right.y == 0 ) return left;
T x = left.x / right.x;
T y = left.y / right.y;
return {x, y};
}
}
//std::mt19937 engine{ std::random_device()() };
template <typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> v)
{
out << '(' << v.x << ',' << v.y << ')';
return out;
}
inline double heuristic(const sf::Vector2i& a, const sf::Vector2i b)
{
//static const auto D = 1;
const auto& dx = std::abs(a.x - b.x);
const auto& dy = std::abs(a.y - b.y);
return /*D * */ (dx + dy);
}
template <typename N>
class Graph
{
class Node;
using NodeHolder = std::set<Node>;
public:
using NodeVal = N;
using NodeRef = typename NodeHolder::iterator;
using Edges = std::vector<std::pair<NodeRef, double>>;
public:
NodeRef addNode(const N& data)
{
const auto& result = nodes.emplace(data);
return result.first;
}
NodeRef getRef(const N& data)
{
const auto& found = nodes.find(data);
if (found == nodes.end())
throw std::runtime_error("Graph::getRef: Key not found!\n");
return found;
}
void addEdge(const NodeRef src, const NodeRef dst, double cost)
{
if (src != nodes.end() && dst != nodes.end())
src->addEdge(dst, cost);
}
void clear()
{
nodes.clear();
}
private:
class Node
{
N data;
mutable Edges outedge;
public:
Node(const N& data) : data(data) {}
void addEdge(const NodeRef e, double cost) const
{
outedge.emplace_back(e, cost);
}
const NodeVal& value() const
{
return data;
}
const Edges& getEdges() const
{
return outedge;
}
friend bool operator < (Node const& lhs, Node const& rhs)
{
return lhs.data < rhs.data;
}
};
private:
NodeHolder nodes;
};
template <typename Graph>
class PathFinder
{
using NodeRef = typename Graph::NodeRef;
using NodeVal = typename Graph::NodeVal;
struct QueueHelper : public std::tuple<NodeRef, double, std::vector<NodeRef>>
{
QueueHelper(const QueueHelper&) = default;
QueueHelper(const NodeRef& data, double cost, const std::vector<NodeRef>& route)
: std::tuple<NodeRef, double, std::vector<NodeRef>>(data, cost, route)
{
std::get<2>(*this).emplace_back(data);
}
friend bool operator < (const QueueHelper& lhs, const QueueHelper& rhs)
{
return std::get<1>(lhs) > std::get<1>(rhs);
}
};
public:
PathFinder(const Graph& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
int width)
: graph(graph), window(window), grid(grid), width(width)
{}
std::vector<NodeRef> routeAstar(const NodeRef& src, const NodeRef& dst) //const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
const auto& start = src->value();
const auto& goal = dst->value();
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
const auto& value = loop.first->value();
const auto& d1 = value - goal;
const auto& d2 = start - goal;
const auto& cross = std::abs(d1.x*d2.y - d2.x*d1.y);
auto heuristics = heuristic(value, goal);
heuristics += cross*0.001;
if(loop.first != src && loop.first != dst)
paint(value,{127,255,212});
frontier.emplace(loop.first, loop.second + heuristics, result);
}
draw();
}
return {};
}
std::vector<NodeRef> routeDijkstra(const NodeRef& src, const NodeRef& dst) // const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
if( loop.first != src && loop.first != dst)
paint(loop.first->value(),{127,255,212});
frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
}
draw();
}
return {};
}
private:
// debug drawing
void paint(const sf::Vector2i& coord, const sf::Color& color)
{
grid[coord.x + width * coord.y].setFillColor(color);
}
void draw()
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
for (const auto& tile : grid)
{
window.draw(tile);
}
window.display();
}
private:
const Graph& graph;
sf::RenderWindow& window;
std::vector<sf::RectangleShape>& grid;
const int width;
};
class Grid : public sf::Drawable
{
public:
enum Algorithm
{
None = 0,
Dijkstra = 1 << 0,
Astar = 1 << 1,
};
public:
Grid(int width,
int height,
int block_size,
sf::RenderWindow& window)
: width(width), height(height), tile_size(block_size, block_size), grid(width * height)
, start({}), goal({}), algorithm(Algorithm::Dijkstra), graph()
, pathFinder(graph, window, grid, width)
{
int i=0;
for (auto& tile : grid)
{
sf::Vector2f position(i % width, i / width); i++;
tile.setSize(tile_size);
tile.setOutlineColor(sf::Color(205,201,201));
tile.setOutlineThickness(-1);
tile.setPosition(position.x * tile_size.x, position.y * tile_size.y );
}
std::random_device rd;
std::mt19937 gen(rd());
auto distH = std::uniform_int_distribution<>(1,height/2);
auto distW = std::uniform_int_distribution<>(width/4,width*2);
start.x = distW(gen)%width; start.y = distH(gen)%height;
goal.x = distW(gen)%width; goal.y = distH(gen)%height;
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
begin = path.end();
}
void setAlgorithmType(Algorithm a)
{
algorithm = a;
reset();
std::vector<Graph<sf::Vector2i>::NodeRef> result;
if (algorithm & Algorithm::Dijkstra)
result = pathFinder.routeDijkstra(graph.getRef(start), graph.getRef(goal));
else
result = pathFinder.routeAstar(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](const auto& d){return d->value();});
begin = path.begin();
}
void setPoints(const sf::Vector2i& s, const sf::Vector2i& g)
{
start = s;
goal = g;
reset();
}
void updatePath()
{
if (begin == path.end()) return;
if ( *begin != start && *begin != goal )
grid[begin->x + width * begin->y].setFillColor(sf::Color::Yellow);
begin++;
}
void movePoints(const sf::Vector2i& position)
{
if(!in_bounds(position) || !passable(position)) return;
//if(position == start)
auto& tile = grid[position.x + width * position.y];
//tile.setFillColor(sf::Color::White);
if (passable(position)) tile.setFillColor(sf::Color::Blue);
//else tile.setFillColor(sf::Color::White);
}
// mouse input
void addWall(const sf::Vector2i& position)
{
if(position == start || position == goal || !in_bounds(position)) return;
grid[position.x + width * position.y].setFillColor(Gray);
}
void removeWall(const sf::Vector2i& position)
{
if(position == start || position == goal || !in_bounds(position)) return;
grid[position.x + width * position.y].setFillColor(sf::Color::White);
}
// keyboards input
void clearPath()
{
path.clear();
begin = path.end();
for (auto& tile : grid) // keep wall
{
if (tile.getFillColor() == Gray) continue;
tile.setFillColor(sf::Color::White);
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
}
void clearWall()
{
for (auto& tile : grid)
{
if (tile.getFillColor() == Gray)
tile.setFillColor(sf::Color::White);
}
}
private:
void draw(sf::RenderTarget& target, sf::RenderStates) const
{
for (const auto& tile : grid)
{
target.draw(tile);
}
}
void reset()
{
clearPath();
graph.clear();
for (const auto& tile : grid)
{
const auto& p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size);
if (!passable(p)) continue;
graph.addNode(p);
}
for (const auto& tile : grid)
{
const auto& p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size);
if (!passable(p)) continue;
for (const auto& next : neighbors(p))
graph.addEdge(graph.getRef(p), graph.getRef(next), 1);
}
}
inline bool in_bounds(const sf::Vector2i& id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
inline bool passable(const sf::Vector2i& position) const
{
auto& tile = grid[position.x + width * position.y];
return tile.getFillColor() != Gray;
}
std::vector<sf::Vector2i> neighbors(const sf::Vector2i& id) const
{
static const std::array<sf::Vector2i, 4> Directions{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1},
}};
std::vector<sf::Vector2i> results;
for (auto direction : Directions)
{
const auto& next(id + direction);
if (in_bounds(next) && passable(next))
{
results.emplace_back(next);
}
}
return results;
}
private:
static const sf::Color Gray;
const int width, height;
const sf::Vector2f tile_size;
std::vector<sf::RectangleShape> grid;
std::vector<sf::Vector2i> path;
// for animation
std::vector<sf::Vector2i>::iterator begin;
sf::Vector2i start;
sf::Vector2i goal;
// Algorithm's engine
Algorithm algorithm;
Graph<sf::Vector2i> graph;
PathFinder<Graph<sf::Vector2i>> pathFinder;
};
const sf::Color Grid::Gray = sf::Color{139,137,137};
int main()
{
try{
unsigned width = 45;
unsigned height = 30;
int block_size = 20;
sf::RenderWindow window({width*block_size, height*block_size}, "Path Finders - Algorithms");
window.setFramerateLimit(60);
Grid grid(width, height, block_size, window);
unsigned algorithm = Grid::Dijkstra;
const unsigned toggle = (Grid::Dijkstra ^ Grid::Astar);
enum State
{
None = 0, // always get back to edit state
Start = 1 << 0,
Restart = 1 << 1,
};
enum Input
{
AddWall = 1 << 0,
RemoveWall = 1 << 1,
MoveStart = 1 << 2,
MoveGoal = 1 << 3,
};
unsigned input = None;
unsigned state = None;
std::set<sf::Vector2i> positions;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
const auto& position = sf::Mouse::getPosition(window);
auto coord = static_cast<sf::Vector2i>(window.mapPixelToCoords(position));
coord /= block_size;
switch (event.type)
{
case sf::Event::Closed:window.close(); break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Escape){
window.close();
}
else if (event.key.code == sf::Keyboard::P){
grid.clearPath();
}
else if (event.key.code == sf::Keyboard::R){
state = Restart;
}
else if (event.key.code == sf::Keyboard::S){
state = Start;
}
else if (event.key.code == sf::Keyboard::W){
grid.clearWall();
}
else if (event.key.code == sf::Keyboard::T){
algorithm ^= toggle;
}
break;
case sf::Event::MouseButtonPressed:
if (event.mouseButton.button == sf::Mouse::Left)
{
input = AddWall | MoveStart | MoveGoal;
if ((input & AddWall) && (input & (RemoveWall | MoveStart | MoveGoal))){
grid.addWall(coord);
}
if ((input & MoveStart) && (input & (AddWall | RemoveWall | MoveGoal))){
}
if ((input & MoveGoal) && (input & (AddWall | RemoveWall | MoveStart))){
}
}
if (event.mouseButton.button == sf::Mouse::Right)
{
input = RemoveWall;
if ((input & RemoveWall) && !(input & (AddWall | MoveStart | MoveGoal)))
grid.removeWall(coord);
}
break;
case sf::Event::MouseButtonReleased:
{
positions.clear();
input &= ~(AddWall | RemoveWall | MoveStart | MoveGoal);
}
break;
case sf::Event::MouseMoved:
if ((input & AddWall) && (input & (RemoveWall | MoveStart | MoveGoal))){
grid.addWall(coord);
}
if ((input & RemoveWall) && !(input & (AddWall | MoveStart | MoveGoal))){
grid.removeWall(coord);
}
if ((input & MoveStart) && (input & (AddWall | RemoveWall | MoveGoal))){
}
if ((input & MoveGoal) && (input & (AddWall | RemoveWall | MoveStart))){
}
break;
default:break;
};
}
if ((algorithm & Grid::Dijkstra) && (state & (Start | Restart))){
state &= ~(Start | Restart);
grid.setAlgorithmType(Grid::Dijkstra);
}
else if((algorithm & Grid::Astar) && (state & (Start | Restart))){
state &= ~(Start | Restart);
grid.setAlgorithmType(Grid::Astar);
}
grid.updatePath();
window.clear();
window.draw(grid);
window.display();
}
} catch (std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << '\n';
return 1;
} catch (...)
{
std::cout << "Unknown Exception!\n";
return 1;
}
}
#include <SFML/Graphics.hpp>
#include <iostream>
#include <iomanip>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <set>
#include <algorithm>
#include <random>
namespace sf
{
template <typename T>
bool operator > (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) > std::tie(right.x, right.y);
}
template <typename T>
bool operator < (const Vector2<T>& left, const Vector2<T>& right)
{
return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
template <typename T>
Vector2<T> operator / (const Vector2<T>& left, const Vector2<T>& right)
{
if (right.x == 0 || right.y == 0 ) return left;
T x = left.x / right.x;
T y = left.y / right.y;
return {x, y};
}
}
template <typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> v)
{
out << '(' << v.x << ',' << v.y << ')';
return out;
}
inline double heuristic(const sf::Vector2i& a, const sf::Vector2i b)
{
//static const auto D = 1;
const auto& dx = std::abs(a.x - b.x);
const auto& dy = std::abs(a.y - b.y);
return /*D * */ (dx + dy);
}
template <typename N>
class Graph
{
class Node;
using NodeHolder = std::set<Node>;
public:
using NodeVal = N;
using NodeRef = typename NodeHolder::iterator;
using Edges = std::vector<std::pair<NodeRef, double>>;
public:
NodeRef addNode(const N& data)
{
const auto& result = nodes.emplace(data);
return result.first;
}
NodeRef getRef(const N& data)
{
const auto& found = nodes.find(data);
if (found == nodes.end())
throw std::runtime_error("Graph::getRef: Key not found!\n");
return found;
}
void addEdge(const NodeRef src, const NodeRef dst, double cost)
{
if (src != nodes.end() && dst != nodes.end())
src->addEdge(dst, cost);
}
void clear()
{
nodes.clear();
}
private:
class Node
{
N data;
mutable Edges outedge;
public:
Node(const N& data) : data(data) {}
void addEdge(const NodeRef e, double cost) const
{
outedge.emplace_back(e, cost);
}
const NodeVal& value() const
{
return data;
}
const Edges& getEdges() const
{
return outedge;
}
friend bool operator < (Node const& lhs, Node const& rhs)
{
return lhs.data < rhs.data;
}
};
private:
NodeHolder nodes;
};
template <typename Graph>
class PathFinder
{
using NodeRef = typename Graph::NodeRef;
using NodeVal = typename Graph::NodeVal;
struct QueueHelper : public std::tuple<NodeRef, double, std::vector<NodeRef>>
{
QueueHelper(const QueueHelper&) = default;
QueueHelper(const NodeRef& data, double cost, const std::vector<NodeRef>& route)
: std::tuple<NodeRef, double, std::vector<NodeRef>>(data, cost, route)
{
std::get<2>(*this).emplace_back(data);
}
friend bool operator < (const QueueHelper& lhs, const QueueHelper& rhs)
{
return std::get<1>(lhs) > std::get<1>(rhs);
}
};
public:
PathFinder(const Graph& graph,
sf::RenderWindow& window,
std::vector<sf::RectangleShape>& grid,
int width)
: graph(graph), window(window), grid(grid), width(width)
{}
std::vector<NodeRef> routeAstar(const NodeRef& src, const NodeRef& dst) //const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
const auto& start = src->value();
const auto& goal = dst->value();
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
const auto& value = loop.first->value();
const auto& d1 = value - goal;
const auto& d2 = start - goal;
const auto& cross = std::abs(d1.x*d2.y - d2.x*d1.y);
auto heuristics = heuristic(value, goal);
heuristics += cross*0.001;
if(loop.first != src && loop.first != dst)
paint(value,{127,255,212});
frontier.emplace(loop.first, loop.second + heuristics, result);
}
draw();
}
return {};
}
std::vector<NodeRef> routeDijkstra(const NodeRef& src, const NodeRef& dst) // const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
if( loop.first != src && loop.first != dst)
paint(loop.first->value(),{127,255,212});
frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
}
draw();
}
return {};
}
private:
// debug drawing
void paint(const sf::Vector2i& coord, const sf::Color& color)
{
grid[coord.x + width * coord.y].setFillColor(color);
}
void draw()
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
for (const auto& tile : grid)
{
window.draw(tile);
}
window.display();
}
private:
const Graph& graph;
sf::RenderWindow& window;
std::vector<sf::RectangleShape>& grid;
const int width;
};
class Grid : public sf::Drawable
{
public:
enum Algorithm
{
None = 0,
Dijkstra = 1 << 0,
Astar = 1 << 1,
};
public:
Grid(int width, int height, int block_size, sf::RenderWindow& window)
: width(width), height(height), tile_size(block_size, block_size)
, grid(width * height), start({}), goal({}), needUpdate(false)
, algorithm(Algorithm::Dijkstra), graph()
, pathFinder(graph, window, grid, width)
{
int i=0;
for (auto& tile : grid)
{
sf::Vector2f position(i % width, i / width); i++;
tile.setSize(tile_size);
tile.setOutlineColor(sf::Color(205,201,201));
tile.setOutlineThickness(-1);
tile.setPosition(position.x * tile_size.x, position.y * tile_size.y );
}
std::random_device rd;
std::mt19937 gen(rd());
auto distH = std::uniform_int_distribution<>(1,height/2);
auto distW = std::uniform_int_distribution<>(width/4,width*2);
start.x = distW(gen)%width; start.y = distH(gen)%height;
goal.x = distW(gen)%width; goal.y = distH(gen)%height;
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
begin = path.end();
}
void setAlgorithmType(Algorithm a)
{
algorithm = a;
reset();
std::vector<Graph<sf::Vector2i>::NodeRef> result;
if (algorithm & Algorithm::Dijkstra)
result = pathFinder.routeDijkstra(graph.getRef(start), graph.getRef(goal));
else
result = pathFinder.routeAstar(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](const auto& d){return d->value();});
begin = path.begin();
}
void update()
{
if(needUpdate){
for (auto& tile : grid) // keep wall
{
if (tile.getFillColor() == Gray) continue;
tile.setFillColor(sf::Color::White);
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
needUpdate = false;
}
if (begin == path.end()) return;
if ( *begin != start && *begin != goal )
grid[begin->x + width * begin->y].setFillColor(sf::Color::Yellow);
begin++;
}
// mouse input
bool moveStart(const sf::Vector2i& position)
{
if(!in_bounds(position) || !passable(position)|| position == goal) return false;
start = position;
needUpdate = true;
return needUpdate;
}
bool moveGoal(const sf::Vector2i& position)
{
if(!in_bounds(position) || !passable(position) || position == start) return false;
goal = position;
needUpdate = true;
return needUpdate;
}
bool addWall(const sf::Vector2i& position)
{
if(position == start || position == goal || !in_bounds(position)) return false;
grid[position.x + width * position.y].setFillColor(Gray);
return true;
}
void removeWall(const sf::Vector2i& position)
{
if(position == start || position == goal || !in_bounds(position)) return;
grid[position.x + width * position.y].setFillColor(sf::Color::White);
}
// keyboards input
void clearPath()
{
path.clear();
begin = path.end();
for (auto& tile : grid) // keep wall
{
if (tile.getFillColor() == Gray) continue;
tile.setFillColor(sf::Color::White);
}
grid[start.x + width * start.y].setFillColor(sf::Color::Green);
grid[goal.x + width * goal.y].setFillColor(sf::Color::Red);
}
void clearWall()
{
for (auto& tile : grid)
{
if (tile.getFillColor() == Gray)
tile.setFillColor(sf::Color::White);
}
}
private:
void draw(sf::RenderTarget& target, sf::RenderStates) const
{
for (const auto& tile : grid)
{
target.draw(tile);
}
}
void reset()
{
clearPath();
graph.clear();
for (const auto& tile : grid)
{
const auto& p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size);
if (!passable(p)) continue;
graph.addNode(p);
}
for (const auto& tile : grid)
{
const auto& p = static_cast<sf::Vector2i>(tile.getPosition() / tile_size);
if (!passable(p)) continue;
for (const auto& next : neighbors(p))
graph.addEdge(graph.getRef(p), graph.getRef(next), 1);
}
}
inline bool in_bounds(const sf::Vector2i& id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
inline bool passable(const sf::Vector2i& position) const
{
auto& tile = grid[position.x + width * position.y];
return tile.getFillColor() != Gray;
}
std::vector<sf::Vector2i> neighbors(const sf::Vector2i& id) const
{
static const std::array<sf::Vector2i, 4> Directions{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1},
}};
std::vector<sf::Vector2i> results;
for (auto direction : Directions)
{
const auto& next(id + direction);
if (in_bounds(next) && passable(next))
{
results.emplace_back(next);
}
}
return results;
}
private:
static const sf::Color Gray;
const int width, height;
const sf::Vector2f tile_size;
std::vector<sf::RectangleShape> grid;
std::vector<sf::Vector2i> path;
// for animation
std::vector<sf::Vector2i>::iterator begin;
sf::Vector2i start;
sf::Vector2i goal;
bool needUpdate;
// Algorithm's engine
Algorithm algorithm;
Graph<sf::Vector2i> graph;
PathFinder<Graph<sf::Vector2i>> pathFinder;
};
const sf::Color Grid::Gray = sf::Color{139,137,137};
int main()
{
try{
unsigned width = 45;
unsigned height = 30;
int block_size = 20;
sf::RenderWindow window({width*block_size, height*block_size}, "Path Finders - Algorithms");
window.setFramerateLimit(60);
Grid grid(width, height, block_size, window);
unsigned algorithm = Grid::Dijkstra;
const unsigned toggle = (Grid::Dijkstra ^ Grid::Astar);
enum State
{
None = 0, // always get back to edit state
Start = 1 << 0,
Restart = 1 << 1,
};
enum Input
{
AddWall = 1 << 0,
RemoveWall = 1 << 1,
MoveStart = 1 << 2,
MoveGoal = 1 << 3,
};
unsigned input = None;
unsigned state = None;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
const auto& position = sf::Mouse::getPosition(window);
auto coord = static_cast<sf::Vector2i>(window.mapPixelToCoords(position));
coord /= block_size;
switch (event.type)
{
case sf::Event::Closed:window.close(); break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Escape){
window.close();
}
else if (event.key.code == sf::Keyboard::P){
grid.clearPath();
}
else if (event.key.code == sf::Keyboard::R){
state = Restart;
}
else if (event.key.code == sf::Keyboard::S){
state = Start;
}
else if (event.key.code == sf::Keyboard::W){
grid.clearWall();
}
else if (event.key.code == sf::Keyboard::T){
algorithm ^= toggle;
}
break;
case sf::Event::MouseButtonPressed:
if (event.mouseButton.button == sf::Mouse::Left)
{
input = AddWall | MoveStart | MoveGoal;
if (input & AddWall){
if(grid.addWall(coord)){
input = AddWall;
}
}
if (input & MoveStart){
if(grid.moveStart(coord)){
input = MoveStart;
}
}
if (input & MoveGoal){
if(grid.moveGoal(coord)){
input = MoveGoal;
}
}
}
if (event.mouseButton.button == sf::Mouse::Right)
{
input = RemoveWall;
if (input & RemoveWall){
grid.removeWall(coord);
}
}
break;
case sf::Event::MouseButtonReleased:
{
input &= ~(AddWall | RemoveWall | MoveStart | MoveGoal);
}
break;
case sf::Event::MouseMoved:
if (input & AddWall){
grid.addWall(coord);
}
else if (input & RemoveWall){
grid.removeWall(coord);
}
else if (input & MoveStart){
grid.moveStart(coord);
}
else if (input & MoveGoal){
grid.moveGoal(coord);
}
break;
default:break;
};
}
if ((algorithm & Grid::Dijkstra) && (state & (Start | Restart))){
state &= ~(Start | Restart);
grid.setAlgorithmType(Grid::Dijkstra);
}
else if((algorithm & Grid::Astar) && (state & (Start | Restart))){
state &= ~(Start | Restart);
grid.setAlgorithmType(Grid::Astar);
}
grid.update();
window.clear();
window.draw(grid);
window.display();
}
} catch (std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << '\n';
return 1;
} catch (...)
{
std::cout << "Unknown Exception!\n";
return 1;
}
}
#include <SFML/Graphics.hpp>
#include <iostream>
#include <iomanip>
#include <array>
#include <vector>
#include <utility>
#include <queue>
#include <tuple>
#include <set>
#include <algorithm>
#include <random>
namespace sf
{
template <typename T>
inline bool operator < (const Vector2<T>& left, const Vector2<T>& right)
{
return (left.x < right.x) || ((left.x == right.x) && (left.y < right.y));
}
template <typename T>
inline bool operator > (const Vector2<T>& left, const Vector2<T>& right)
{
return (left.x > right.x) || ((left.x == right.x) && (left.y > right.y));
}
template <typename T>
inline Vector2<T> operator / (const Vector2<T>& left, const Vector2<T>& right)
{
if (right.x == 0 || right.y == 0 ) return left;
T x = left.x / right.x;
T y = left.y / right.y;
return {x, y};
}
}
template <typename T>
std::ostream& operator<<(std::ostream& out, sf::Vector2<T> v)
{
out << '(' << v.x << ',' << v.y <<')';
return out;
}
std::ostream& operator<<(std::ostream& out, sf::Color c)
{
out << '(' << (int)c.r << ',' << (int)c.b << ',' << (int)c.g<< ')';
return out;
}
template <typename T>
std::ostream& operator<<(std::ostream& out, sf::Rect<T> r)
{
out << '(' << r.left << ',' << r.top << ',' << r.width << ',' << r.height <<')';
return out;
}
inline double heuristic(const sf::Vector2i& a, const sf::Vector2i b)
{
//static const auto D = 1;
const auto& dx = std::abs(a.x - b.x);
const auto& dy = std::abs(a.y - b.y);
return /*D * */ (dx + dy);
}
template <typename N>
class Graph
{
class Node;
using NodeHolder = std::set<Node>;
public:
using NodeVal = N;
using NodeRef = typename NodeHolder::iterator;
using Edges = std::vector<std::pair<NodeRef, double>>;
public:
NodeRef addNode(const N& data)
{
const auto& result = nodes.emplace(data);
return result.first;
}
NodeRef getRef(const N& data)
{
const auto& found = nodes.find(data);
if (found == nodes.end())
throw std::runtime_error("Graph::getRef: Key not found!\n");
return found;
}
void addEdge(const NodeRef src, const NodeRef dst, double cost)
{
if (src != nodes.end() && dst != nodes.end())
src->addEdge(dst, cost);
}
void clear()
{
nodes.clear();
}
private:
class Node
{
N data;
mutable Edges outedge;
public:
Node(const N& data) : data(data) {}
void addEdge(const NodeRef e, double cost) const
{
outedge.emplace_back(e, cost);
}
const NodeVal& value() const
{
return data;
}
const Edges& getEdges() const
{
return outedge;
}
friend bool operator < (Node const& lhs, Node const& rhs)
{
return lhs.data < rhs.data;
}
};
private:
NodeHolder nodes;
};
typedef unsigned Vertexv;
class Graphv {
public:
typedef std::pair<Vertexv, Vertexv> Edge;
typedef std::set<Edge> Edges;
void addEdge(Vertexv u, Vertexv v) {
edges_.insert({u, v});
}
const Edges& edges() { return edges_; }
private:
Edges edges_;
};
struct cell
{
explicit cell(const sf::Vector2f& coord,unsigned size, const sf::Color& color)
{
quad[0].position = { coord.x * size, coord.y * size};
quad[1].position = {(coord.x + 1) * size, coord.y * size};
quad[2].position = {(coord.x + 1) * size, (coord.y + 1) * size};
quad[3].position = { coord.x * size, (coord.y + 1) * size};
quad[0].color = quad[1].color = quad[2].color = quad[3].color = color;
}
void setFillColor(const sf::Color& color)
{
quad[0].color = quad[1].color = quad[2].color = quad[3].color = color;
}
sf::Color getFillColor() const
{
return quad[0].color;
}
sf::Vector2i getPosition() const
{
return static_cast<sf::Vector2i>(quad[0].position);
}
friend std::ostream& operator<<(std::ostream& out, cell c)
{
out << c.quad[0].position << ' ' << c.quad[1].position << "\n "
<< c.quad[3].position << ' ' << c.quad[2].position << "\n "
<< c.quad[0].color << ' ' << c.quad[1].color << "\n "
<< c.quad[3].color << ' ' << c.quad[2].color << "\n";
return out;
}
std::array<sf::Vertex, 4> quad;
};
template <typename Graph>
class PathFinder
{
using NodeRef = typename Graph::NodeRef;
using NodeVal = typename Graph::NodeVal;
// TODO: tuple is damn slow, consider re-implement it.
// candidates: 1) create warper class for std::push_heap std::pop_heap
// 2) mimic boost implementation!! (trusted open source treasure) :)
struct QueueHelper : public std::tuple<NodeRef, double, std::vector<NodeRef>>
{
QueueHelper(const QueueHelper&) = default;
QueueHelper(const NodeRef& data, double cost, const std::vector<NodeRef>& route)
: std::tuple<NodeRef, double, std::vector<NodeRef>>(data, cost, route)
{
std::get<2>(*this).emplace_back(data);
}
friend bool operator < (const QueueHelper& lhs, const QueueHelper& rhs)
{
return std::get<1>(lhs) > std::get<1>(rhs);
}
};
public:
PathFinder(const Graph& graph,
sf::RenderWindow& window,
std::vector<cell>& grid,std::vector<sf::Vertex>& outlines,
int width)
: graph(graph), window(window), grid(grid), outlines(outlines),width(width)
{}
// TODO: A* looks like greedy FBS, consider re-implement it.
std::vector<NodeRef> routeAstar(const NodeRef& src, const NodeRef& dst) //const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
const auto& start = src->value();
const auto& goal = dst->value();
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
const auto& value = loop.first->value();
const auto& d1 = value - goal;
const auto& d2 = start - goal;
const auto& cross = std::abs(d1.x*d2.y - d2.x*d1.y);
auto heuristics = heuristic(value, goal);
heuristics += cross*0.001;
if(loop.first != src && loop.first != dst)
paint(value,{127,255,212});
frontier.emplace(loop.first, loop.second + heuristics, result);
}
draw();
}
return {};
}
std::vector<NodeRef> routeDijkstra(const NodeRef& src, const NodeRef& dst) // const
{
std::set<NodeVal> found;
std::priority_queue<QueueHelper> frontier;
frontier.emplace(src, 0, std::vector<NodeRef>{});
while (!frontier.empty())
{
auto next = frontier.top(); frontier.pop();
const auto& current = std::get<0>(next);
const auto& value = current->value();
if (found.find(value) != found.end()) continue;
if (current != src && current != dst) paint(value,{102,205,170});
found.emplace(value);
const auto& result = std::get<2>(next);
if (current == dst) return result;
for (const auto& loop: current->getEdges())
{
if( loop.first != src && loop.first != dst)
paint(loop.first->value(),{127,255,212});
frontier.emplace(loop.first, std::get<1>(next) + loop.second, result);
}
draw();
}
return {};
}
private:
// debug drawing
void paint(const sf::Vector2i& coord, const sf::Color& color)
{
//window.clear();
grid[coord.y + width * coord.x].setFillColor(color);
//window.draw(grid[coord.x + width * coord.y]);
//window.display();
}
void draw()
{
// TODO: 1) add observer pattern to handle input.
// 2) use vertex array to optimize drawing. [x]-done
// NOTE: SFML doesn't support in-place drawing.
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed
|| (event.type == sf::Event::KeyPressed
&& event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear();
window.draw(grid[0].quad.data(), (sizeof(cell)/sizeof(sf::Vertex))*grid.size(), sf::Quads);
window.draw(outlines.data(), outlines.size(), sf::Lines);
window.display();
}
private:
const Graph& graph;
sf::RenderWindow& window;
std::vector<cell>& grid;
std::vector<sf::Vertex>& outlines;
const int width;
};
std::vector<sf::Vertex>
make_outLines(unsigned width, unsigned height, float size, const sf::Color& color=sf::Color{139,137,137})
{
std::vector<sf::Vertex> vertices((width + height + 2) * 2);
static const auto e = 0.001f;
// make vertical lines
for (unsigned i = 0; i <= width; ++i) {
sf::Vertex* line = &vertices[i * 2];
line[0].position = {((i==0) ? i+e : i) * size, 0};
line[1].position = {((i==0) ? i+e : i) * size, height * size};
line[0].color = line[1].color = color;
}
// make horizontal lines
for (unsigned i = 0; i <= height; ++i) {
sf::Vertex* line = &vertices[((i+1 ) + width) * 2];
line[0].position = {0, ((i==height) ? i-e : i) * size};
line[1].position = {width * size, ((i==height) ? i-e : i) * size};
line[0].color = line[1].color = color;
}
return vertices;
}
class MapEditor final : public sf::Drawable
{
public:
enum Algorithm
{
None = 0,
Dijkstra = 1 << 0,
Astar = 1 << 1,
};
public:
explicit MapEditor(int width, int height, int block_size, sf::RenderWindow& window)
: width(width), height(height), tile_size(block_size, block_size)
, grid(), outlines(make_outLines(width, height, block_size))
, start({}), goal({}), needUpdate(false), algorithm(Algorithm::None), graph()
, pathFinder(graph, window, grid, outlines, height)
{
grid.reserve(width*height);
for (int i = 0; i < width; ++i)
for (int j = 0; j < height; ++j)
{
grid.emplace_back(sf::Vector2f(i, j), block_size, sf::Color::White);
}
std::random_device rd;
std::mt19937 gen(rd());
auto distH = std::uniform_int_distribution<>(1,height/2);
auto distW = std::uniform_int_distribution<>(width/4,width*2);
start.x = distW(gen)%width; start.y = distH(gen)%height;
goal.x = distW(gen)%width; goal.y = distH(gen)%height;
grid[start.y + height * start.x].setFillColor(sf::Color::Green);
grid[goal.y + height * goal.x].setFillColor(sf::Color::Red);
begin = path.end();
}
void setAlgorithmType(Algorithm a)
{
// if ((algorithm & a) && !needUpdate){
// clearPath();
// } else {
// algorithm = a;
// reset();
// }
algorithm = a;
reset();
std::vector<Graph<sf::Vector2i>::NodeRef> result;
if (algorithm & Algorithm::Dijkstra)
result = pathFinder.routeDijkstra(graph.getRef(start), graph.getRef(goal));
else
result = pathFinder.routeAstar(graph.getRef(start), graph.getRef(goal));
std::transform(std::begin(result), std::end(result),
std::back_inserter(path), [](const auto& d){return d->value();});
begin = path.begin();
}
void update()
{
if(needUpdate){
for (auto& tile : grid) // keep wall
{
if (tile.getFillColor() == Gray) continue;
tile.setFillColor(sf::Color::White);
}
grid[start.y + height * start.x].setFillColor(sf::Color::Green);
grid[goal.y + height * goal.x].setFillColor(sf::Color::Red);
needUpdate = false;
}
if (begin == path.end()) return;
if ( *begin != start && *begin != goal )
grid[begin->y + height * begin->x].setFillColor(sf::Color::Yellow);
begin++;
}
// mouse input
bool moveStart(const sf::Vector2i& position)
{
if(!in_bounds(position) || !passable(position)|| position == goal) return false;
start = position;
needUpdate = true;
return needUpdate;
}
bool moveGoal(const sf::Vector2i& position)
{
if(!in_bounds(position) || !passable(position) || position == start) return false;
goal = position;
needUpdate = true;
return needUpdate;
}
bool addWall(const sf::Vector2i& position)
{
if(position == start || position == goal || !in_bounds(position)) return false;
grid[position.y + height * position.x].setFillColor(Gray);
return true;
}
void removeWall(const sf::Vector2i& position)
{
if(position == start || position == goal || !in_bounds(position)) return;
grid[position.y + height * position.x].setFillColor(sf::Color::White);
needUpdate = true;
}
// keyboards input
void clearPath()
{
for (auto& tile : grid) // keep wall
{
if (tile.getFillColor() == Gray) continue;
tile.setFillColor(sf::Color::White);
}
grid[start.y + height * start.x].setFillColor(sf::Color::Green);
grid[goal.y + height * goal.x].setFillColor(sf::Color::Red);
}
void clearWall()
{
for (auto& tile : grid)
{
if (tile.getFillColor() == Gray)
tile.setFillColor(sf::Color::White);
}
needUpdate = true;
}
// void setColor(int x, int y, const sf::Color& color)
// {
// if(0 > x || x >= width || 0 > y || y >= height)
// throw std::out_of_range("Grid::setColor(): parameters out of range");
//
// grid[y + x * height].setFillColor(color);
// }
//
// sf::Color getColor(unsigned x, unsigned y) const
// {
// if(0 > x || x >= width || 0 > y || y >= height)
// throw std::out_of_range("Grid::getColor(): parameters out of range");
//
// return grid[y + x * height].getFillColor();
// }
//
// sf::Vector2i getPosition(unsigned x, unsigned y) const
// {
// return grid[y + x * height].getPosition();
// }
private:
void draw(sf::RenderTarget& target, sf::RenderStates) const
{
target.draw(grid[0].quad.data(), (sizeof(cell)/sizeof(sf::Vertex))*grid.size(), sf::Quads);
target.draw(outlines.data(), outlines.size(), sf::Lines);
}
void reset()
{
clearPath();
path.clear();
begin = path.end();
graph.clear();
for (const auto& tile : grid)
{
const auto& p = tile.getPosition() / tile_size;
if (!passable(p)) continue;
graph.addNode(p);
}
for (const auto& tile : grid)
{
const auto& p = tile.getPosition() / tile_size;
if (!passable(p)) continue;
for (const auto& next : neighbors(p))
graph.addEdge(graph.getRef(p), graph.getRef(next), 1);
}
}
inline bool in_bounds(const sf::Vector2i& id) const
{
return 0 <= id.x && id.x < width && 0 <= id.y && id.y < height;
}
inline bool passable(const sf::Vector2i& position) const
{
const auto& tile = grid[position.y + height * position.x];
return tile.getFillColor() != Gray;
}
std::vector<sf::Vector2i> neighbors(const sf::Vector2i& id) const
{
static const std::array<sf::Vector2i, 4> Directions{{
{ 1, 0},
{ 0, -1},
{-1, 0},
{ 0, 1},
}};
std::vector<sf::Vector2i> results;
for (const auto& direction : Directions)
{
const auto& next(id + direction);
if (in_bounds(next) && passable(next))
{
results.emplace_back(next);
}
}
return results;
}
private:
static const sf::Color Gray;
const int width, height;
const sf::Vector2i tile_size;
std::vector<cell> grid;
std::vector<sf::Vertex> outlines;
std::vector<sf::Vector2i> path;
// for animation
std::vector<sf::Vector2i>::iterator begin;
sf::Vector2i start;
sf::Vector2i goal;
bool needUpdate;
// Algorithm's engine
Algorithm algorithm;
Graph<sf::Vector2i> graph;
PathFinder<Graph<sf::Vector2i>> pathFinder;
};
const sf::Color MapEditor::Gray = sf::Color{139,137,137};
int main()
{
try{
unsigned width = 45;
unsigned height = 30;
int block_size = 20;
sf::RenderWindow window({width*block_size, height*block_size}, "Path Finders - Algorithms");
window.setFramerateLimit(60);
MapEditor editor(width, height, block_size, window);
unsigned algorithm = MapEditor::Dijkstra;
unsigned toggle = (MapEditor::Dijkstra ^ MapEditor::Astar);
enum Input
{
None = 0,
AddWall = 1 << 0,
RemoveWall = 1 << 1,
MoveStart = 1 << 2,
MoveGoal = 1 << 3,
};
unsigned input = None;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
const auto& position = sf::Mouse::getPosition(window);
auto coord = static_cast<sf::Vector2i>(window.mapPixelToCoords(position));
coord /= block_size;
switch (event.type)
{
case sf::Event::Closed:window.close(); break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Escape){
window.close();
}
else if (event.key.code == sf::Keyboard::P){
editor.clearPath();
}
else if (event.key.code == sf::Keyboard::R){
editor.setAlgorithmType(static_cast<MapEditor::Algorithm>(algorithm));
}
else if (event.key.code == sf::Keyboard::S){
editor.setAlgorithmType(static_cast<MapEditor::Algorithm>(algorithm));
}
else if (event.key.code == sf::Keyboard::W){
editor.clearWall();
}
else if (event.key.code == sf::Keyboard::T){
algorithm ^= toggle;
}
break;
case sf::Event::MouseButtonPressed:
if (event.mouseButton.button == sf::Mouse::Left)
{
input = AddWall | MoveStart | MoveGoal;
if ((input & AddWall) && editor.addWall(coord)){
input = AddWall;
}
if ((input & MoveStart) && editor.moveStart(coord)){
input = MoveStart;
}
if ((input & MoveGoal) && editor.moveGoal(coord)){
input = MoveGoal;
}
}
if (event.mouseButton.button == sf::Mouse::Right)
{
input = RemoveWall;
if (input & RemoveWall){
editor.removeWall(coord);
}
}
break;
case sf::Event::MouseButtonReleased:
{
input &= ~(AddWall | RemoveWall | MoveStart | MoveGoal);
}
break;
case sf::Event::MouseMoved:
if (input & AddWall){
editor.addWall(coord);
}
else if (input & RemoveWall){
editor.removeWall(coord);
}
else if (input & MoveStart){
editor.moveStart(coord);
}
else if (input & MoveGoal){
editor.moveGoal(coord);
}
break;
default:break;
};
}
editor.update();
window.clear();
window.draw(editor);
window.display();
}
} catch (std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << '\n';
return 1;
} catch(std::out_of_range& e)
{
std::cout << "Exception: " << e.what() << '\n';
return 1;
}catch (...)
{
std::cout << "Unknown Exception!\n";
return 1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment