Browse Source

Pathfinding: Rename functions

Rename functions to better reflect what they do.
pull/7520/head
Gleb Mazovetskiy 1 year ago
parent
commit
e692acb049
  1. 2
      Source/controls/plrctrls.cpp
  2. 47
      Source/engine/path.cpp
  3. 2
      Source/engine/path.h
  4. 48
      test/path_test.cpp

2
Source/controls/plrctrls.cpp

@ -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;
}

47
Source/engine/path.cpp

@ -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)
{
@ -161,13 +161,16 @@ 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;
@ -175,6 +178,15 @@ int CheckEqual(Point startPosition, Point destinationPosition)
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
*/
@ -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);
}
@ -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
*
@ -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
@ -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;
@ -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;
@ -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;
}
}
@ -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

2
Source/engine/path.h

@ -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] = {

48
test/path_test.cpp

@ -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;
}

Loading…
Cancel
Save