Skip to content

Commit

Permalink
Pathfinding: Rename functions
Browse files Browse the repository at this point in the history
Rename functions to better reflect what they do.
  • Loading branch information
glebm committed Nov 10, 2024
1 parent 44d5d2d commit e692acb
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Source/controls/plrctrls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ void FindMeleeTarget()
continue;
}

if (path_solid_pieces({ node.x, node.y }, { dx, dy })) {
if (CanStep({ node.x, node.y }, { dx, dy })) {
queue.push_back({ dx, dy, node.steps + 1 });
visited[dx][dy] = true;
}
Expand Down
47 changes: 25 additions & 22 deletions Source/engine/path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ PathNode PathNodes[MaxPathNodes];
PathNode *Path2Nodes;

/**
* @brief return a node for a position on the frontier, or NULL if not found
* @brief return a node for a position on the frontier, or `PathNode::InvalidIndex` if not found
*/
uint16_t GetNode1(Point targetPosition)
{
Expand Down Expand Up @@ -161,20 +161,32 @@ uint16_t PopActiveStep()
}

/**
* @brief return 2 if pPath is horizontally/vertically aligned with (dx,dy), else 3
* @brief Returns the distance between 2 adjacent nodes.
*
* The distance is 2 for nodes in the same row or column,
* and 3 for diagonally adjacent nodes.
*
* This approximates that diagonal movement on a square grid should have a cost
* of sqrt(2). That's approximately 1.5, so they multiply all step costs by 2,
* except diagonal steps which are times 3
*/
int CheckEqual(Point startPosition, Point destinationPosition)
int GetDistance(Point startPosition, Point destinationPosition)
{
if (startPosition.x == destinationPosition.x || startPosition.y == destinationPosition.y)
return 2;

return 3;
}

/**
* @brief heuristic, estimated cost from startPosition to destinationPosition.
*/
int GetHeuristicCost(Point startPosition, Point destinationPosition)
{
// see GetDistance for why this is times 2
return 2 * startPosition.ManhattanDistance(destinationPosition);
}

/**
* @brief update all path costs using depth-first search starting at pPath
*/
Expand All @@ -190,10 +202,10 @@ void SetCoords(uint16_t pPath)
break;
PathNode &pathAct = PathNodes[childIndex];

if (pathOld.g + CheckEqual(pathOld.position(), pathAct.position()) < pathAct.g) {
if (path_solid_pieces(pathOld.position(), pathAct.position())) {
if (pathOld.g + GetDistance(pathOld.position(), pathAct.position()) < pathAct.g) {
if (CanStep(pathOld.position(), pathAct.position())) {
pathAct.parentIndex = pathOldIndex;
pathAct.g = pathOld.g + CheckEqual(pathOld.position(), pathAct.position());
pathAct.g = pathOld.g + GetDistance(pathOld.position(), pathAct.position());
pathAct.f = pathAct.g + pathAct.h;
PushActiveStep(childIndex);
}
Expand All @@ -219,15 +231,6 @@ int8_t GetPathDirection(Point startPosition, Point destinationPosition)
return PathDirections[3 * (destinationPosition.y - startPosition.y) + 4 + destinationPosition.x - startPosition.x];
}

/**
* @brief heuristic, estimated cost from startPosition to destinationPosition.
*/
int GetHeuristicCost(Point startPosition, Point destinationPosition)
{
// see path_check_equal for why this is times 2
return 2 * startPosition.ManhattanDistance(destinationPosition);
}

/**
* @brief add a step from pPath to destination, return 1 if successful, and update the frontier/visited nodes accordingly
*
Expand All @@ -236,10 +239,10 @@ int GetHeuristicCost(Point startPosition, Point destinationPosition)
* @param destinationPosition where we hope to end up
* @return true if step successfully added, false if we ran out of nodes to use
*/
bool ParentPath(uint16_t pathIndex, Point candidatePosition, Point destinationPosition)
bool ExploreFrontier(uint16_t pathIndex, Point candidatePosition, Point destinationPosition)
{
PathNode &path = PathNodes[pathIndex];
int nextG = path.g + CheckEqual(path.position(), candidatePosition);
int nextG = path.g + GetDistance(path.position(), candidatePosition);

// 3 cases to consider
// case 1: (dx,dy) is already on the frontier
Expand All @@ -248,7 +251,7 @@ bool ParentPath(uint16_t pathIndex, Point candidatePosition, Point destinationPo
path.addChild(dxdyIndex);
PathNode &dxdy = PathNodes[dxdyIndex];
if (nextG < dxdy.g) {
if (path_solid_pieces(path.position(), candidatePosition)) {
if (CanStep(path.position(), candidatePosition)) {
// we'll explore it later, just update
dxdy.parentIndex = pathIndex;
dxdy.g = nextG;
Expand All @@ -261,7 +264,7 @@ bool ParentPath(uint16_t pathIndex, Point candidatePosition, Point destinationPo
if (dxdyIndex != PathNode::InvalidIndex) {
path.addChild(dxdyIndex);
PathNode &dxdy = PathNodes[dxdyIndex];
if (nextG < dxdy.g && path_solid_pieces(path.position(), candidatePosition)) {
if (nextG < dxdy.g && CanStep(path.position(), candidatePosition)) {
// update the node
dxdy.parentIndex = pathIndex;
dxdy.g = nextG;
Expand Down Expand Up @@ -300,8 +303,8 @@ bool GetPath(tl::function_ref<bool(Point)> posOk, uint16_t pathIndex, Point dest
const PathNode &path = PathNodes[pathIndex];
const Point tile = path.position() + dir;
const bool ok = posOk(tile);
if ((ok && path_solid_pieces(path.position(), tile)) || (!ok && tile == destination)) {
if (!ParentPath(pathIndex, tile, destination))
if ((ok && CanStep(path.position(), tile)) || (!ok && tile == destination)) {
if (!ExploreFrontier(pathIndex, tile, destination))
return false;
}
}
Expand Down Expand Up @@ -416,7 +419,7 @@ int FindPath(tl::function_ref<bool(Point)> posOk, Point startPosition, Point des
return 0;
}

bool path_solid_pieces(Point startPosition, Point destinationPosition)
bool CanStep(Point startPosition, Point destinationPosition)
{
// These checks are written as if working backwards from the destination to the source, given
// both tiles are expected to be adjacent this doesn't matter beyond being a bit confusing
Expand Down
2 changes: 1 addition & 1 deletion Source/engine/path.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ int FindPath(tl::function_ref<bool(Point)> posOk, Point startPosition, Point des
*
* @return true if step is allowed
*/
bool path_solid_pieces(Point startPosition, Point destinationPosition);
bool CanStep(Point startPosition, Point destinationPosition);

/** For iterating over the 8 possible movement directions */
const Displacement PathDirs[8] = {
Expand Down
48 changes: 24 additions & 24 deletions test/path_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,46 +58,46 @@ TEST(PathTest, Solid)
EXPECT_FALSE(IsTileNotSolid({ -1, 1 })) << "Out of bounds tiles are also not not solid";
}

TEST(PathTest, SolidPieces)
TEST(PathTest, CanStepTest)
{
dPiece[0][0] = 0;
dPiece[0][1] = 0;
dPiece[1][0] = 0;
dPiece[1][1] = 0;
SOLData[0] = TileProperties::None;
EXPECT_TRUE(path_solid_pieces({ 0, 0 }, { 1, 1 })) << "A step in open space is free of solid pieces";
EXPECT_TRUE(path_solid_pieces({ 1, 1 }, { 0, 0 })) << "A step in open space is free of solid pieces";
EXPECT_TRUE(path_solid_pieces({ 1, 0 }, { 0, 1 })) << "A step in open space is free of solid pieces";
EXPECT_TRUE(path_solid_pieces({ 0, 1 }, { 1, 0 })) << "A step in open space is free of solid pieces";
EXPECT_TRUE(CanStep({ 0, 0 }, { 1, 1 })) << "A step in open space is free of solid pieces";
EXPECT_TRUE(CanStep({ 1, 1 }, { 0, 0 })) << "A step in open space is free of solid pieces";
EXPECT_TRUE(CanStep({ 1, 0 }, { 0, 1 })) << "A step in open space is free of solid pieces";
EXPECT_TRUE(CanStep({ 0, 1 }, { 1, 0 })) << "A step in open space is free of solid pieces";

SOLData[1] = TileProperties::Solid;
dPiece[1][0] = 1;
EXPECT_TRUE(path_solid_pieces({ 0, 1 }, { 1, 0 })) << "Can path to a destination which is solid";
EXPECT_TRUE(path_solid_pieces({ 1, 0 }, { 0, 1 })) << "Can path from a starting position which is solid";
EXPECT_TRUE(path_solid_pieces({ 0, 1 }, { 1, 1 })) << "Stepping in a cardinal direction ignores solid pieces";
EXPECT_TRUE(path_solid_pieces({ 1, 0 }, { 1, 1 })) << "Stepping in a cardinal direction ignores solid pieces";
EXPECT_TRUE(path_solid_pieces({ 0, 0 }, { 1, 0 })) << "Stepping in a cardinal direction ignores solid pieces";
EXPECT_TRUE(path_solid_pieces({ 1, 1 }, { 1, 0 })) << "Stepping in a cardinal direction ignores solid pieces";

EXPECT_FALSE(path_solid_pieces({ 0, 0 }, { 1, 1 })) << "Can't cut a solid corner";
EXPECT_FALSE(path_solid_pieces({ 1, 1 }, { 0, 0 })) << "Can't cut a solid corner";
EXPECT_TRUE(CanStep({ 0, 1 }, { 1, 0 })) << "Can path to a destination which is solid";
EXPECT_TRUE(CanStep({ 1, 0 }, { 0, 1 })) << "Can path from a starting position which is solid";
EXPECT_TRUE(CanStep({ 0, 1 }, { 1, 1 })) << "Stepping in a cardinal direction ignores solid pieces";
EXPECT_TRUE(CanStep({ 1, 0 }, { 1, 1 })) << "Stepping in a cardinal direction ignores solid pieces";
EXPECT_TRUE(CanStep({ 0, 0 }, { 1, 0 })) << "Stepping in a cardinal direction ignores solid pieces";
EXPECT_TRUE(CanStep({ 1, 1 }, { 1, 0 })) << "Stepping in a cardinal direction ignores solid pieces";

EXPECT_FALSE(CanStep({ 0, 0 }, { 1, 1 })) << "Can't cut a solid corner";
EXPECT_FALSE(CanStep({ 1, 1 }, { 0, 0 })) << "Can't cut a solid corner";
dPiece[0][1] = 1;
EXPECT_FALSE(path_solid_pieces({ 0, 0 }, { 1, 1 })) << "Can't walk through the boundary between two corners";
EXPECT_FALSE(path_solid_pieces({ 1, 1 }, { 0, 0 })) << "Can't walk through the boundary between two corners";
EXPECT_FALSE(CanStep({ 0, 0 }, { 1, 1 })) << "Can't walk through the boundary between two corners";
EXPECT_FALSE(CanStep({ 1, 1 }, { 0, 0 })) << "Can't walk through the boundary between two corners";
dPiece[1][0] = 0;
EXPECT_FALSE(path_solid_pieces({ 0, 0 }, { 1, 1 })) << "Can't cut a solid corner";
EXPECT_FALSE(path_solid_pieces({ 1, 1 }, { 0, 0 })) << "Can't cut a solid corner";
EXPECT_FALSE(CanStep({ 0, 0 }, { 1, 1 })) << "Can't cut a solid corner";
EXPECT_FALSE(CanStep({ 1, 1 }, { 0, 0 })) << "Can't cut a solid corner";
dPiece[0][1] = 0;

dPiece[0][0] = 1;
EXPECT_FALSE(path_solid_pieces({ 1, 0 }, { 0, 1 })) << "Can't cut a solid corner";
EXPECT_FALSE(path_solid_pieces({ 0, 1 }, { 1, 0 })) << "Can't cut a solid corner";
EXPECT_FALSE(CanStep({ 1, 0 }, { 0, 1 })) << "Can't cut a solid corner";
EXPECT_FALSE(CanStep({ 0, 1 }, { 1, 0 })) << "Can't cut a solid corner";
dPiece[1][1] = 1;
EXPECT_FALSE(path_solid_pieces({ 1, 0 }, { 0, 1 })) << "Can't walk through the boundary between two corners";
EXPECT_FALSE(path_solid_pieces({ 0, 1 }, { 1, 0 })) << "Can't walk through the boundary between two corners";
EXPECT_FALSE(CanStep({ 1, 0 }, { 0, 1 })) << "Can't walk through the boundary between two corners";
EXPECT_FALSE(CanStep({ 0, 1 }, { 1, 0 })) << "Can't walk through the boundary between two corners";
dPiece[0][0] = 0;
EXPECT_FALSE(path_solid_pieces({ 1, 0 }, { 0, 1 })) << "Can't cut a solid corner";
EXPECT_FALSE(path_solid_pieces({ 0, 1 }, { 1, 0 })) << "Can't cut a solid corner";
EXPECT_FALSE(CanStep({ 1, 0 }, { 0, 1 })) << "Can't cut a solid corner";
EXPECT_FALSE(CanStep({ 0, 1 }, { 1, 0 })) << "Can't cut a solid corner";
dPiece[1][1] = 0;
}

Expand Down

0 comments on commit e692acb

Please sign in to comment.