Skip to content

Commit

Permalink
f
Browse files Browse the repository at this point in the history
  • Loading branch information
etorth committed Apr 24, 2022
1 parent c92548b commit 2a38bb3
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 14 deletions.
2 changes: 1 addition & 1 deletion client/src/clientpathfinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "clientpathfinder.hpp"

ClientPathFinder::ClientPathFinder(const ProcessRun *argProc, bool argCheckGround, int argCheckCreature, int argMaxStep)
: AStarPathFinder(argMaxStep, [this](int argSrcX, int argSrcY, int argSrcDir, int argDstX, int argDstY) -> std::optional<double>
: AStarPathFinder(false, argMaxStep, [this](int argSrcX, int argSrcY, int argSrcDir, int argDstX, int argDstY) -> std::optional<double>
{
fflassert(pathf::hopValid(maxStep(), argSrcX, argSrcY, argDstX, argDstY), maxStep(), argSrcX, argSrcY, pathf::dirName(argSrcDir), argDstX, argDstY);
return m_proc->oneStepCost(this, m_checkGround, m_checkCreature, argSrcX, argSrcY, argSrcDir, argDstX, argDstY);
Expand Down
37 changes: 29 additions & 8 deletions common/src/pathf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,33 @@ std::optional<bool> pathf::AStarPathFinder::search(int srcX, int srcY, int srcDi
m_prevSet.clear();
m_openSet.clear();

m_g[m_srcNode] = 0.0;
m_openSet.add(pathf::AStarPathFinder::InnPQNode
{
.node = m_srcNode,
.f = h(m_srcNode),
});
if(m_checkFirstTurn){
m_g[m_srcNode] = 0.0;
m_openSet.add(pathf::AStarPathFinder::InnPQNode
{
.node = m_srcNode,
.f = h(m_srcNode),
});
}
else{
// don't need to explicitly push an imaginary src point as m_dstDrainNode
// alternatively we can directly push all children of the imaginary src point into open set, which are actually (m_srcNode.x, m_srcNode.y, [DIR_BEGIN, DIR_END))
for(int firstDir = DIR_BEGIN; firstDir < DIR_END; ++firstDir){
const pathf::AStarPathFinder::InnNode firstNode
{
.x = srcX,
.y = srcY,
.dir= firstDir,
};

m_g[firstNode] = 0.0;
m_openSet.add(pathf::AStarPathFinder::InnPQNode
{
.node = firstNode,
.f = h(firstNode), // h(firstNode) are same for all directions
});
}
}

for(size_t c = 0; (searchCount <= 0 || c < searchCount) && !m_openSet.empty(); ++c){
const auto currNode = m_openSet.pick();
Expand All @@ -226,7 +247,7 @@ std::optional<bool> pathf::AStarPathFinder::search(int srcX, int srcY, int srcDi
// all other node in m_openSet mush have parent, except src node
// src node can NOT have parent

if(currNode.node == m_srcNode){
if(currNode.node.eq(m_srcNode.x, m_srcNode.y)){
return {};
}

Expand Down Expand Up @@ -292,7 +313,7 @@ std::vector<pathf::PathNode> pathf::AStarPathFinder::getPathNode() const
.Y = to_d(p->second.y),
});

if(p->second == m_srcNode){
if(p->second.eq(m_srcNode.x, m_srcNode.y)){
std::reverse(result.begin(), result.end());
return result;
}
Expand Down
70 changes: 67 additions & 3 deletions common/src/pathf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,63 @@ namespace pathf
}
};

private:
// why sometime path planning needs to ignore first turn?
// during planning, path finder respects turn by a small cost which helps to smooth the path
// but when a character start to move, usually we would like to ignore the first turn, it can be a direct turn-back
//
// +---+---+---+
// | | A | | in game we take hop cost as: N * 0.10 + 1.0, N is jump size: 1, 2, 3
// +---+---+---+ turn ocst as: D * 0.01 , D is diff of direction: 0, 1, 2, 3, 4
// | | | |
// +---+---+---+
// | | | | we need to plan path from A -> B
// +---+---+---+ the result depends on the initial direction when character at A
// | B | | |
// +---+---+---+
//
// case 1: when character at A initial direction is DIR_RIGHT:
//
// +---+---+---+
// | | A | |
// +---+---+---+
// | | | o | cost = 0.01 + 1.10 + 0.02 + 1.20
// +---+---+---+
// | | o | |
// +---+---+---+
// | B | | |
// +---+---+---+
//
// for this case, what we would expect is:
//
// +---+---+---+
// | | A | |
// +---+---+---+
// | | o | | cost = 0.02 + 1.20 + 0.01 + 1.10
// +---+---+---+
// | | o | | this shows two path actually has same cost
// +---+---+---+ if we would liek the algorithm to prefer this one, we need to ignore the initial turn
// | B | | | means don't consider the first turn as a sharp-turn in the path
// +---+---+---+
//
//
//
// case 2: when character at A initial direction is any other than DIR_RIGHT:
//
// +---+---+---+
// | | A | |
// +---+---+---+
// | o | | | cost = 0.01 * D + 1.10 + 1.20
// +---+---+---+
// | o | | | prettry expected
// +---+---+---+ the path is prettry smooth
// | B | | |
// +---+---+---+
//
// the way to ignore the first turn is same as how we ignore direction at dst point
// we connect all possible (m_srcNode.x, m_srcNode.y, [DIR_BEGIN, DIR_END)) to an imaginary src node
const bool m_checkFirstTurn;

private:
const int m_maxStep;

Expand Down Expand Up @@ -311,7 +368,7 @@ namespace pathf

private:
// special dst node that can never be reached by stepping
// it has zero cost to all (m_dstX, m_dstY, DIR_BEGIN ~ DIR_END) for multi-targeting
// it has zero cost to all (m_dstX, m_dstY, [DIR_BEGIN ~ DIR_END)) for multi-targeting
const InnNode m_dstDrainNode
{
.x = 0,
Expand All @@ -331,15 +388,22 @@ namespace pathf
InnPQ m_openSet;

public:
AStarPathFinder(int argMaxStepSize, std::function<std::optional<double>(int, int, int, int, int)> fnOneStepCost)
: m_maxStep(argMaxStepSize)
AStarPathFinder(bool argCheckFirstTurn, int argMaxStepSize, std::function<std::optional<double>(int, int, int, int, int)> fnOneStepCost)
: m_checkFirstTurn(argCheckFirstTurn)
, m_maxStep(argMaxStepSize)
, m_oneStepCost(std::move(fnOneStepCost))
{
fflassert(m_oneStepCost);
fflassert(m_maxStep >= 1, m_maxStep);
fflassert(m_maxStep <= 3, m_maxStep);
}

public:
bool checkFirstTurn() const
{
return m_checkFirstTurn;
}

public:
int maxStep() const
{
Expand Down
2 changes: 1 addition & 1 deletion server/src/battleobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

extern MonoServer *g_monoServer;
BattleObject::BOPathFinder::BOPathFinder(const BattleObject *boPtr, int checkCO)
: pathf::AStarPathFinder(boPtr->maxStep(), [this](int srcX, int srcY, int srcDir, int dstX, int dstY) -> std::optional<double>
: pathf::AStarPathFinder(false, boPtr->maxStep(), [this](int srcX, int srcY, int srcDir, int dstX, int dstY) -> std::optional<double>
{
fflassert(pathf::hopValid(maxStep(), srcX, srcY, dstX, dstY), maxStep(), srcX, srcY, dstX, dstY);
return m_BO->oneStepCost(this, m_checkCO, srcX, srcY, srcDir, dstX, dstY);
Expand Down
2 changes: 1 addition & 1 deletion server/src/servermap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ ServerMap::ServerMapLuaModule::ServerMapLuaModule(ServerMap *mapPtr)
}

ServerMap::ServerPathFinder::ServerPathFinder(const ServerMap *mapPtr, int argMaxStep, int argCheckCO)
: AStarPathFinder(argMaxStep, [this](int srcX, int srcY, int srcDir, int dstX, int dstY) -> std::optional<double>
: AStarPathFinder(false, argMaxStep, [this](int srcX, int srcY, int srcDir, int dstX, int dstY) -> std::optional<double>
{
// treat checkCO and checkLock same
// from server's view occupied and to-be-occupied are equivlent
Expand Down

0 comments on commit 2a38bb3

Please sign in to comment.