diff --git a/cpp/src/map.cpp b/cpp/src/map.cpp index 12d3b4d..3c065d6 100644 --- a/cpp/src/map.cpp +++ b/cpp/src/map.cpp @@ -15,10 +15,10 @@ Map::Map(int rows, int cols) : m_Cols(cols), m_Rows(rows) { if (sw) m_Tiles[row].push_back(&tile_types.at("Grass")); 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; } } diff --git a/cpp/src/map.hpp b/cpp/src/map.hpp index 7650157..ced9085 100644 --- a/cpp/src/map.hpp +++ b/cpp/src/map.hpp @@ -34,6 +34,7 @@ public: std::vector GetNeighbors(TilePos center) const; + float GetCost(TilePos pos) const { return GetTileAt(pos)->cost; } template double GetTileVelocityCoeff(T p) const { return 1.0 / GetTileAt(p)->cost; diff --git a/cpp/src/pathfinder.cpp b/cpp/src/pathfinder.cpp index 0e44dad..2d44061 100644 --- a/cpp/src/pathfinder.cpp +++ b/cpp/src/pathfinder.cpp @@ -72,6 +72,130 @@ Path BFS::CalculatePath(WorldPos start_world, WorldPos end_world) { std::reverse(path.begin(), path.end()); 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 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, 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 create(PathFinderType type, const Map* map) { switch (type) { @@ -79,6 +203,8 @@ std::unique_ptr create(PathFinderType type, const Map* map) { return std::move(std::make_unique(map)); case PathFinderType::BFS: return std::move(std::make_unique(map)); + case PathFinderType::DIJKSTRA: + return std::move(std::make_unique(map)); case PathFinderType::COUNT: LOG_WARNING("Incorrect pathfinder type"); return nullptr; diff --git a/cpp/src/pathfinder.hpp b/cpp/src/pathfinder.hpp index 72eefe3..1957923 100644 --- a/cpp/src/pathfinder.hpp +++ b/cpp/src/pathfinder.hpp @@ -14,6 +14,7 @@ using Path = std::vector; enum class PathFinderType { LINEAR = 1, BFS, + DIJKSTRA, COUNT, }; @@ -60,6 +61,20 @@ private: std::unordered_map 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 m_Cost; + std::unordered_map m_CameFrom; +}; + + std::unique_ptr create(PathFinderType type, const Map* map); } // pathfinder namespace