Implemented Dijkstra's algorithm
This commit is contained in:
parent
5cd3a68e6d
commit
2dd44ab169
@ -15,10 +15,10 @@ Map::Map(int rows, int cols) : m_Cols(cols), m_Rows(rows) {
|
|||||||
if (sw)
|
if (sw)
|
||||||
m_Tiles[row].push_back(&tile_types.at("Grass"));
|
m_Tiles[row].push_back(&tile_types.at("Grass"));
|
||||||
else
|
else
|
||||||
m_Tiles[row].push_back(&tile_types.at("Road"));
|
m_Tiles[row].push_back(&tile_types.at("Water"));
|
||||||
sw = !sw;
|
sw = !sw;
|
||||||
}
|
}
|
||||||
sw = !sw;
|
//sw = !sw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
std::vector<TilePos> GetNeighbors(TilePos center) const;
|
std::vector<TilePos> GetNeighbors(TilePos center) const;
|
||||||
|
float GetCost(TilePos pos) const { return GetTileAt(pos)->cost; }
|
||||||
|
|
||||||
template <typename T> double GetTileVelocityCoeff(T p) const {
|
template <typename T> double GetTileVelocityCoeff(T p) const {
|
||||||
return 1.0 / GetTileAt(p)->cost;
|
return 1.0 / GetTileAt(p)->cost;
|
||||||
|
@ -72,6 +72,130 @@ Path BFS::CalculatePath(WorldPos start_world, WorldPos end_world) {
|
|||||||
std::reverse(path.begin(), path.end());
|
std::reverse(path.begin(), path.end());
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
//Path Dijkstra::CalculatePath(WorldPos start_world, WorldPos end_world) {
|
||||||
|
// if (m_Map == nullptr) return {};
|
||||||
|
//
|
||||||
|
// const TilePos start = m_Map->WorldToTile(start_world);
|
||||||
|
// const TilePos end = m_Map->WorldToTile(end_world);
|
||||||
|
//
|
||||||
|
// if (!m_Map->IsTilePosValid(start) || !m_Map->IsTilePosValid(end))
|
||||||
|
// return {};
|
||||||
|
// if (start == end) {
|
||||||
|
// return {};
|
||||||
|
// }
|
||||||
|
// // clear previous run
|
||||||
|
// m_CameFrom.clear();
|
||||||
|
// m_Distance.clear();
|
||||||
|
//
|
||||||
|
// std::queue<TilePos> frontier;
|
||||||
|
// frontier.push(start);
|
||||||
|
// m_CameFrom[start] = start;
|
||||||
|
// m_Distance[start] = 0.0f;
|
||||||
|
//
|
||||||
|
// // ---------------- build flow-field ----------------
|
||||||
|
// bool early_exit = false;
|
||||||
|
// while (!frontier.empty() && !early_exit) {
|
||||||
|
// TilePos current = frontier.front();
|
||||||
|
// frontier.pop();
|
||||||
|
//
|
||||||
|
// for (TilePos next : m_Map->GetNeighbors(current)) {
|
||||||
|
// if (m_CameFrom.find(next) == m_CameFrom.end()) { // not visited
|
||||||
|
// frontier.push(next);
|
||||||
|
// m_Distance[next] = m_Distance[current] + 1.0f;
|
||||||
|
// m_CameFrom[next] = current;
|
||||||
|
//
|
||||||
|
// if (next == end) { // early exit
|
||||||
|
// early_exit = true;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // --------------- reconstruct path -----------------
|
||||||
|
// if (m_CameFrom.find(end) == m_CameFrom.end())
|
||||||
|
// return {}; // end not reached
|
||||||
|
//
|
||||||
|
// Path path;
|
||||||
|
// TilePos cur = end;
|
||||||
|
// path.push_back(m_Map->TileToWorld(cur));
|
||||||
|
// while (cur != start) {
|
||||||
|
// cur = m_CameFrom[cur];
|
||||||
|
// path.push_back(m_Map->TileToWorld(cur));
|
||||||
|
// }
|
||||||
|
// std::reverse(path.begin(), path.end());
|
||||||
|
// return path;
|
||||||
|
//}
|
||||||
|
|
||||||
|
struct QueueEntry
|
||||||
|
{
|
||||||
|
float cost;
|
||||||
|
TilePos tile;
|
||||||
|
|
||||||
|
// min-heap -> smallest cost on top
|
||||||
|
bool operator>(const QueueEntry& o) const noexcept { return cost > o.cost; }
|
||||||
|
};
|
||||||
|
|
||||||
|
Path Dijkstra::CalculatePath(WorldPos start_world, WorldPos end_world)
|
||||||
|
{
|
||||||
|
if (!m_Map) return {};
|
||||||
|
|
||||||
|
const TilePos start = m_Map->WorldToTile(start_world);
|
||||||
|
const TilePos end = m_Map->WorldToTile(end_world);
|
||||||
|
|
||||||
|
if (!m_Map->IsTilePosValid(start) || !m_Map->IsTilePosValid(end))
|
||||||
|
return {};
|
||||||
|
if (start == end) return {};
|
||||||
|
|
||||||
|
// clear previous run
|
||||||
|
m_CameFrom.clear();
|
||||||
|
m_Cost.clear();
|
||||||
|
|
||||||
|
std::priority_queue<QueueEntry, std::vector<QueueEntry>, std::greater<>> frontier;
|
||||||
|
frontier.push({0.0f, start});
|
||||||
|
m_CameFrom[start] = start; // sentinel
|
||||||
|
m_Cost[start] = 0.0f;
|
||||||
|
|
||||||
|
while (!frontier.empty())
|
||||||
|
{
|
||||||
|
const QueueEntry current = frontier.top();
|
||||||
|
frontier.pop();
|
||||||
|
|
||||||
|
if (current.tile == end) // early exit
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (TilePos next : m_Map->GetNeighbors(current.tile))
|
||||||
|
{
|
||||||
|
// cost of moving to neighbour (uniform 1.0 matches original BFS)
|
||||||
|
const float newCost = m_Cost[current.tile] + m_Map->GetCost(next);
|
||||||
|
|
||||||
|
if (!m_Cost.count(next) || newCost < m_Cost[next])
|
||||||
|
{
|
||||||
|
m_Cost[next] = newCost;
|
||||||
|
m_CameFrom[next] = current.tile;
|
||||||
|
frontier.push({newCost, next});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reconstruct path
|
||||||
|
if (!m_CameFrom.count(end))
|
||||||
|
return {}; // goal never reached
|
||||||
|
|
||||||
|
Path path;
|
||||||
|
TilePos cur = end;
|
||||||
|
path.push_back(m_Map->TileToWorld(cur));
|
||||||
|
|
||||||
|
while (cur != start)
|
||||||
|
{
|
||||||
|
cur = m_CameFrom[cur];
|
||||||
|
path.push_back(m_Map->TileToWorld(cur));
|
||||||
|
}
|
||||||
|
std::reverse(path.begin(), path.end());
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<PathFinderBase> create(PathFinderType type, const Map* map) {
|
std::unique_ptr<PathFinderBase> create(PathFinderType type, const Map* map) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -79,6 +203,8 @@ std::unique_ptr<PathFinderBase> create(PathFinderType type, const Map* map) {
|
|||||||
return std::move(std::make_unique<LinearPathFinder>(map));
|
return std::move(std::make_unique<LinearPathFinder>(map));
|
||||||
case PathFinderType::BFS:
|
case PathFinderType::BFS:
|
||||||
return std::move(std::make_unique<BFS>(map));
|
return std::move(std::make_unique<BFS>(map));
|
||||||
|
case PathFinderType::DIJKSTRA:
|
||||||
|
return std::move(std::make_unique<Dijkstra>(map));
|
||||||
case PathFinderType::COUNT:
|
case PathFinderType::COUNT:
|
||||||
LOG_WARNING("Incorrect pathfinder type");
|
LOG_WARNING("Incorrect pathfinder type");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -14,6 +14,7 @@ using Path = std::vector<WorldPos>;
|
|||||||
enum class PathFinderType {
|
enum class PathFinderType {
|
||||||
LINEAR = 1,
|
LINEAR = 1,
|
||||||
BFS,
|
BFS,
|
||||||
|
DIJKSTRA,
|
||||||
COUNT,
|
COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -60,6 +61,20 @@ private:
|
|||||||
std::unordered_map<TilePos, TilePos, TilePosHash> m_CameFrom;
|
std::unordered_map<TilePos, TilePos, TilePosHash> m_CameFrom;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Dijkstra: public PathFinderBase {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Dijkstra(const Map* m): PathFinderBase(m) {}
|
||||||
|
Path CalculatePath(WorldPos start, WorldPos end) override;
|
||||||
|
const std::string_view& GetName() const override { return m_Name; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string_view m_Name = "Dijkstra's Algorithm";
|
||||||
|
std::unordered_map<TilePos, double, TilePosHash> m_Cost;
|
||||||
|
std::unordered_map<TilePos, TilePos, TilePosHash> m_CameFrom;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<PathFinderBase> create(PathFinderType type, const Map* map);
|
std::unique_ptr<PathFinderBase> create(PathFinderType type, const Map* map);
|
||||||
|
|
||||||
} // pathfinder namespace
|
} // pathfinder namespace
|
||||||
|
Loading…
x
Reference in New Issue
Block a user