-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bb504f7
commit b90bf90
Showing
5 changed files
with
45 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,103 +1,96 @@ | ||
// Std includes | ||
#include <array> | ||
#include <queue> | ||
#include <stdexcept> | ||
#include <unordered_map> | ||
|
||
// Custom includes | ||
#include "generation/astar.hpp" | ||
|
||
// ----- STRUCTURES ------------------------------ | ||
/// Represents a grid position and its costs from the start position | ||
/// | ||
/// @param cost - The cost to traverse to this neighbour. | ||
/// @param destination - The destination position in the grid. | ||
/// Represents a grid position and its distance from the start position. | ||
struct Neighbour { | ||
// std::priority_queue uses a max heap, but we want a min heap, so the operator needs to be reversed | ||
inline bool operator<(const Neighbour &neighbour) const { return cost > neighbour.cost; } | ||
|
||
/// The cost to traverse to this neighbour. | ||
int cost; | ||
|
||
/// The destination position in the grid. | ||
Position destination; | ||
|
||
inline bool operator<(const Neighbour nghbr) const { | ||
// The priority_queue data structure gets the maximum priority, so we need | ||
// to override that functionality to get the minimum priority | ||
return cost > nghbr.cost; | ||
} | ||
/// Initialise the object. | ||
Neighbour() = default; | ||
|
||
/// Initialise the object. | ||
/// | ||
/// @param cost - The cost to traverse to this neighbour. | ||
/// @param destination - The destination position in the grid. | ||
Neighbour(int cost, Position destination) : cost(cost), destination(destination) {} | ||
}; | ||
|
||
// ----- CONSTANTS ------------------------------ | ||
// Represents the north, south, east, west, north-east, north-west, south-east | ||
// and south-west directions on a compass | ||
const std::array<Position, 8> INTERCARDINAL_OFFSETS = { | ||
Position{-1, -1}, Position{0, -1}, Position{1, -1}, Position{-1, 0}, | ||
Position{1, 0}, Position{-1, 1}, Position{0, 1}, Position{1, 1}, | ||
}; | ||
// Represents the north, south, east, west, north-east, north-west, south-east and south-west directions on a compass | ||
const std::array<Position, 8> INTERCARDINAL_OFFSETS = {Position{-1, -1}, Position{0, -1}, Position{1, -1}, | ||
Position{-1, 0}, Position{1, 0}, Position{-1, 1}, | ||
Position{0, 1}, Position{1, 1}}; | ||
|
||
// ----- FUNCTIONS ------------------------------ | ||
std::vector<Position> calculate_astar_path(Grid &grid, const Position start, const Position end) { | ||
// Check if the grid size is not zero, if not, set up a few variables needed | ||
// for the pathfinding | ||
std::vector<Position> calculate_astar_path(const Grid &grid, const Position &start, const Position &end) { | ||
// Check if the grid size is not zero | ||
if (!grid.width) { | ||
throw std::length_error("Grid size must be bigger than 0."); | ||
} | ||
|
||
// Initialise the result vector, priority queue and neighbours map which will be used during the algorithm | ||
std::vector<Position> result; | ||
std::priority_queue<Neighbour> queue; | ||
std::unordered_map<Position, Neighbour> neighbours{{start, {0, start}}}; | ||
queue.push({0, start}); | ||
queue.emplace(0, start); | ||
|
||
// Loop until the priority queue is empty | ||
// Loop until we have explored every neighbour or until we've reached the end | ||
while (!queue.empty()) { | ||
// Get the lowest cost pair from the priority queue | ||
Position current = queue.top().destination; | ||
queue.pop(); | ||
|
||
// Check if we've reached our target | ||
// Check if we've reached the end. If so, backtrack through the neighbours to get the resultant path | ||
if (current == end) { | ||
// Backtrack through neighbours to get the path | ||
while (!(neighbours[current].destination == current)) { | ||
// Add the current pair to the result list | ||
while (!(neighbours.at(current).destination == current)) { | ||
result.push_back(current); | ||
|
||
// Get the next pair in the path | ||
current = neighbours[current].destination; | ||
current = neighbours.at(current).destination; | ||
} | ||
|
||
// Add the start position and exit out of the loop | ||
// Add the start position to the result and break out of the loop | ||
result.push_back(start); | ||
break; | ||
} | ||
|
||
// Add all the neighbours to the heap with their cost being f = g + h: | ||
// f - The total cost of traversing the neighbour. | ||
// g - The distance between the start pair and the neighbour pair. | ||
// h - The estimated distance from the neighbour pair to the end pair. | ||
// We're using the Chebyshev distance for this. | ||
for (Position offset : INTERCARDINAL_OFFSETS) { | ||
// Calculate the neighbour's position and check if its valid excluding | ||
// the boundaries as that produces weird paths | ||
// h - The estimated distance from the neighbour pair to the end pair (this uses the Chebyshev distance). | ||
for (const Position &offset : INTERCARDINAL_OFFSETS) { | ||
// Calculate the neighbour's position and check if its valid excluding the boundaries | ||
Position neighbour = current + offset; | ||
if (neighbour.x < 1 || neighbour.x >= grid.width - 1 || neighbour.y < 1 || neighbour.y >= grid.height - 1) { | ||
continue; | ||
} | ||
|
||
// Test if the neighbour is an obstacle or not. If so, skip to the next | ||
// neighbour as we want to move around it | ||
// Move around the neighbour if it is an obstacle as they have an infinite cost | ||
if (grid.get_value(neighbour) == TileType::Obstacle) { | ||
continue; | ||
} | ||
|
||
// Calculate the distance from the start | ||
int distance = neighbours[current].cost + 1; | ||
|
||
// Check if we need to add a new neighbour to the heap | ||
if ((!neighbours.contains(neighbour)) || distance < neighbours[neighbour].cost) { | ||
// Check if we've found a more efficient path to the neighbour and if so, add all of its neighbours to the queue | ||
int distance = neighbours.at(current).cost + 1; | ||
if (!neighbours.contains(neighbour) || distance < neighbours.at(neighbour).cost) { | ||
neighbours[neighbour] = {distance, current}; | ||
|
||
// Add the neighbour to the priority queue | ||
Position diff = end - neighbour; | ||
queue.emplace(distance + std::max(diff.x, diff.y), neighbour); | ||
queue.emplace(distance + std::max(abs(end.x - neighbour.x), abs(end.y - neighbour.y)), neighbour); | ||
} | ||
} | ||
} | ||
|
||
// Return result | ||
// Return the most efficient path | ||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters