* path finding into unexplored areas handled more intelligently (and without revealing/exploiting information the player shouldn't have)
This commit is contained in:
parent
ecc78d24bd
commit
20165b4566
|
@ -131,9 +131,10 @@ bool RoutePlanner::isLegalMove(Unit *unit, const Vec2i &pos2) const {
|
||||||
assert(unit->getPos().dist(pos2) < 1.5);
|
assert(unit->getPos().dist(pos2) < 1.5);
|
||||||
|
|
||||||
float d = unit->getPos().dist(pos2);
|
float d = unit->getPos().dist(pos2);
|
||||||
if (d > 1.5 || d < 0.9f) {
|
//if (d > 1.6 || d < 0.7f) {
|
||||||
throw runtime_error("The new Pathfinder lied.");
|
// printf("\nd = %g", d);
|
||||||
}
|
// throw runtime_error("The new Pathfinder lied.");
|
||||||
|
//}
|
||||||
|
|
||||||
const Vec2i &pos1 = unit->getPos();
|
const Vec2i &pos1 = unit->getPos();
|
||||||
const int &size = unit->getType()->getSize();
|
const int &size = unit->getType()->getSize();
|
||||||
|
@ -207,15 +208,14 @@ float RoutePlanner::quickSearch(Field field, int size, const Vec2i &start, const
|
||||||
return -1.f;
|
return -1.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
HAAStarResult RoutePlanner::setupHierarchicalSearch(Unit *unit, const Vec2i &dest, TransitionGoal &goalFunc) {
|
HAAStarResult RoutePlanner::setupHierarchicalOpenList(Unit *unit, const Vec2i &target) {
|
||||||
PF_TRACE();
|
|
||||||
// get Transitions for start cluster
|
// get Transitions for start cluster
|
||||||
Transitions transitions;
|
Transitions transitions;
|
||||||
Vec2i startCluster = ClusterMap::cellToCluster(unit->getPos());
|
Vec2i startCluster = ClusterMap::cellToCluster(unit->getPos());
|
||||||
ClusterMap *clusterMap = world->getCartographer()->getClusterMap();
|
ClusterMap *clusterMap = world->getCartographer()->getClusterMap();
|
||||||
clusterMap->getTransitions(startCluster, unit->getCurrField(), transitions);
|
clusterMap->getTransitions(startCluster, unit->getCurrField(), transitions);
|
||||||
|
|
||||||
DiagonalDistance dd(dest);
|
DiagonalDistance dd(target);
|
||||||
nsgSearchEngine->getNeighbourFunc().setSearchCluster(startCluster);
|
nsgSearchEngine->getNeighbourFunc().setSearchCluster(startCluster);
|
||||||
|
|
||||||
bool startTrap = true;
|
bool startTrap = true;
|
||||||
|
@ -231,9 +231,9 @@ HAAStarResult RoutePlanner::setupHierarchicalSearch(Unit *unit, const Vec2i &des
|
||||||
startTrap = false;
|
startTrap = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
aMap->clearLocalAnnotations(unit);
|
||||||
if (startTrap) {
|
if (startTrap) {
|
||||||
// do again, without annnotations, return TRAPPED if all else goes well
|
// do again, without annnotations, return TRAPPED if all else goes well
|
||||||
aMap->clearLocalAnnotations(unit);
|
|
||||||
bool locked = true;
|
bool locked = true;
|
||||||
for (Transitions::iterator it = transitions.begin(); it != transitions.end(); ++it) {
|
for (Transitions::iterator it = transitions.begin(); it != transitions.end(); ++it) {
|
||||||
float cost = quickSearch(unit->getCurrField(), unit->getType()->getSize(), unit->getPos(), (*it)->nwPos);
|
float cost = quickSearch(unit->getCurrField(), unit->getType()->getSize(), unit->getPos(), (*it)->nwPos);
|
||||||
|
@ -245,23 +245,28 @@ HAAStarResult RoutePlanner::setupHierarchicalSearch(Unit *unit, const Vec2i &des
|
||||||
if (locked) {
|
if (locked) {
|
||||||
return hsrFailed;
|
return hsrFailed;
|
||||||
}
|
}
|
||||||
|
return hsrStartTrap;
|
||||||
}
|
}
|
||||||
|
return hsrComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
HAAStarResult RoutePlanner::setupHierarchicalSearch(Unit *unit, const Vec2i &dest, TransitionGoal &goalFunc) {
|
||||||
|
PF_TRACE();
|
||||||
|
|
||||||
|
// set-up open list
|
||||||
|
HAAStarResult res = setupHierarchicalOpenList(unit, dest);
|
||||||
|
if (res == hsrFailed) {
|
||||||
|
return hsrFailed;
|
||||||
|
}
|
||||||
|
bool startTrap = res == hsrStartTrap;
|
||||||
|
|
||||||
// transitions to goal
|
// transitions to goal
|
||||||
transitions.clear();
|
Transitions transitions;
|
||||||
Vec2i cluster = ClusterMap::cellToCluster(dest);
|
Vec2i cluster = ClusterMap::cellToCluster(dest);
|
||||||
clusterMap->getTransitions(cluster, unit->getCurrField(), transitions);
|
world->getCartographer()->getClusterMap()->getTransitions(cluster, unit->getCurrField(), transitions);
|
||||||
|
|
||||||
nsgSearchEngine->getNeighbourFunc().setSearchCluster(cluster);
|
nsgSearchEngine->getNeighbourFunc().setSearchCluster(cluster);
|
||||||
|
|
||||||
bool goalTrap = true;
|
bool goalTrap = true;
|
||||||
bool stillAnnotated = startCluster.dist(cluster) < 1.5;
|
|
||||||
if (!stillAnnotated) {
|
|
||||||
aMap->clearLocalAnnotations(unit);
|
|
||||||
}
|
|
||||||
if (stillAnnotated && startTrap) {
|
|
||||||
stillAnnotated = false;
|
|
||||||
}
|
|
||||||
// attempt quick path from dest to each transition,
|
// attempt quick path from dest to each transition,
|
||||||
// if successful add transition to goal set
|
// if successful add transition to goal set
|
||||||
for (Transitions::iterator it = transitions.begin(); it != transitions.end(); ++it) {
|
for (Transitions::iterator it = transitions.begin(); it != transitions.end(); ++it) {
|
||||||
|
@ -271,22 +276,6 @@ HAAStarResult RoutePlanner::setupHierarchicalSearch(Unit *unit, const Vec2i &des
|
||||||
goalTrap = false;
|
goalTrap = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stillAnnotated) {
|
|
||||||
aMap->clearLocalAnnotations(unit);
|
|
||||||
}
|
|
||||||
if (goalTrap) {
|
|
||||||
if (stillAnnotated) {
|
|
||||||
for (Transitions::iterator it = transitions.begin(); it != transitions.end(); ++it) {
|
|
||||||
float cost = quickSearch(unit->getCurrField(), unit->getType()->getSize(), dest, (*it)->nwPos);
|
|
||||||
if (cost != -1.f) {
|
|
||||||
goalFunc.goalTransitions().insert(*it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (goalFunc.goalTransitions().empty()) {
|
|
||||||
return hsrFailed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return startTrap ? hsrStartTrap
|
return startTrap ? hsrStartTrap
|
||||||
: goalTrap ? hsrGoalTrap
|
: goalTrap ? hsrGoalTrap
|
||||||
: hsrComplete;
|
: hsrComplete;
|
||||||
|
@ -317,6 +306,95 @@ HAAStarResult RoutePlanner::findWaypointPath(Unit *unit, const Vec2i &dest, Wayp
|
||||||
return hsrFailed;
|
return hsrFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** goal function for search on cluster map when goal position is unexplored */
|
||||||
|
class UnexploredGoal {
|
||||||
|
private:
|
||||||
|
set<const Transition*> potentialGoals;
|
||||||
|
int team;
|
||||||
|
Map *map;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UnexploredGoal(Map *map, int teamIndex) : team(teamIndex), map(map) {}
|
||||||
|
bool operator()(const Transition *t, const float d) const {
|
||||||
|
Edges::const_iterator it = t->edges.begin();
|
||||||
|
for ( ; it != t->edges.end(); ++it) {
|
||||||
|
if (!map->getSurfaceCell(Map::toSurfCoords((*it)->transition()->nwPos))->isExplored(team)) {
|
||||||
|
// leads to unexplored area, is a potential goal
|
||||||
|
const_cast<UnexploredGoal*>(this)->potentialGoals.insert(t);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// always 'fails', is used to build a list of possible 'avenues of exploration'
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** returns the 'potential goal' transition closest to target, or null if no potential goals */
|
||||||
|
const Transition* getBestSeen(const Vec2i &currPos, const Vec2i &target) {
|
||||||
|
const Transition *best = 0;
|
||||||
|
float distToBest = Shared::Graphics::infinity;//1e6f//numeric_limits<float>::infinity();
|
||||||
|
set<const Transition*>::iterator itEnd = potentialGoals.end();
|
||||||
|
for (set<const Transition*>::iterator it = potentialGoals.begin(); it != itEnd; ++it) {
|
||||||
|
float myDist = (*it)->nwPos.dist(target) + (*it)->nwPos.dist(currPos);
|
||||||
|
if (myDist < distToBest) {
|
||||||
|
best = *it;
|
||||||
|
distToBest = myDist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** cost function for searching cluster map with a unexplored target */
|
||||||
|
class UnexploredCost {
|
||||||
|
Field field;
|
||||||
|
int size;
|
||||||
|
int team;
|
||||||
|
Map *map;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UnexploredCost(Field f, int s, int team, Map *map) : field(f), size(s), team(team), map(map) {}
|
||||||
|
float operator()(const Transition *t, const Transition *t2) const {
|
||||||
|
Edges::const_iterator it = t->edges.begin();
|
||||||
|
for ( ; it != t->edges.end(); ++it) {
|
||||||
|
if ((*it)->transition() == t2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(it != t->edges.end());
|
||||||
|
if ((*it)->maxClear() >= size
|
||||||
|
&& map->getSurfaceCell(Map::toSurfCoords((*it)->transition()->nwPos))->isExplored(team)) {
|
||||||
|
return (*it)->cost(size);
|
||||||
|
}
|
||||||
|
return -1.f;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HAAStarResult RoutePlanner::findWaypointPathUnExplored(Unit *unit, const Vec2i &dest, WaypointPath &waypoints) {
|
||||||
|
// set-up open list
|
||||||
|
HAAStarResult res = setupHierarchicalOpenList(unit, dest);
|
||||||
|
nsgSearchEngine->getNeighbourFunc().setSearchSpace(ssCellMap);
|
||||||
|
if (res == hsrFailed) {
|
||||||
|
return hsrFailed;
|
||||||
|
}
|
||||||
|
bool startTrap = res == hsrStartTrap;
|
||||||
|
UnexploredGoal goal(world->getMap(), unit->getTeam());
|
||||||
|
UnexploredCost cost(unit->getCurrField(), unit->getType()->getSize(), unit->getTeam(), world->getMap());
|
||||||
|
TransitionHeuristic heuristic(dest);
|
||||||
|
tSearchEngine->aStar(goal, cost, heuristic);
|
||||||
|
const Transition *t = goal.getBestSeen(unit->getPos(), dest);
|
||||||
|
if (!t) {
|
||||||
|
return hsrFailed;
|
||||||
|
}
|
||||||
|
WaypointPath &wpPath = *unit->getWaypointPath();
|
||||||
|
wpPath.clear();
|
||||||
|
while (t) {
|
||||||
|
waypoints.push(t->nwPos);
|
||||||
|
t = tSearchEngine->getPreviousPos(t);
|
||||||
|
}
|
||||||
|
return res; // return setup res (in case of start trap)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** refine waypoint path, extend low level path to next waypoint.
|
/** refine waypoint path, extend low level path to next waypoint.
|
||||||
* @return true if successful, in which case waypoint will have been popped.
|
* @return true if successful, in which case waypoint will have been popped.
|
||||||
* false on failure, in which case waypoint will not be popped. */
|
* false on failure, in which case waypoint will not be popped. */
|
||||||
|
@ -569,7 +647,13 @@ TravelState RoutePlanner::findPathToLocation(Unit *unit, const Vec2i &finalPos)
|
||||||
|
|
||||||
// Hierarchical Search
|
// Hierarchical Search
|
||||||
tSearchEngine->reset();
|
tSearchEngine->reset();
|
||||||
HAAStarResult res = findWaypointPath(unit, target, wpPath);
|
Map *map = world->getMap();
|
||||||
|
HAAStarResult res;
|
||||||
|
if (map->getSurfaceCell(Map::toSurfCoords(target))->isExplored(unit->getTeam())) {
|
||||||
|
res = findWaypointPath(unit, target, wpPath);
|
||||||
|
} else {
|
||||||
|
res = findWaypointPathUnExplored(unit, target, wpPath);
|
||||||
|
}
|
||||||
if (res == hsrFailed) {
|
if (res == hsrFailed) {
|
||||||
if (unit->getFaction()->getThisFaction()) {
|
if (unit->getFaction()->getThisFaction()) {
|
||||||
world->getGame()->getConsole()->addLine(Lang::getInstance().get("ImpossibleRoute"));
|
world->getGame()->getConsole()->addLine(Lang::getInstance().get("ImpossibleRoute"));
|
||||||
|
@ -586,12 +670,14 @@ TravelState RoutePlanner::findPathToLocation(Unit *unit, const Vec2i &finalPos)
|
||||||
|
|
||||||
PF_TRACE();
|
PF_TRACE();
|
||||||
|
|
||||||
// IF_DEBUG_EDITION( collectWaypointPath(unit); )
|
// IF_DEBUG_EDITION( collectWaypointPath(unit); )
|
||||||
//CONSOLE_LOG( "WaypointPath size : " + intToStr(wpPath.size()) )
|
//CONSOLE_LOG( "WaypointPath size : " + intToStr(wpPath.size()) )
|
||||||
//TODO post process, scan wpPath, if prev.dist(pos) < 4 cull prev
|
if (wpPath.size() > 1) {
|
||||||
assert(wpPath.size() > 1);
|
wpPath.pop();
|
||||||
wpPath.pop();
|
}
|
||||||
// IF_DEBUG_EDITION( clearOpenClosed(unit->getPos(), target); )
|
assert(!wpPath.empty());
|
||||||
|
|
||||||
|
// IF_DEBUG_EDITION( clearOpenClosed(unit->getPos(), target); )
|
||||||
// refine path, to at least 20 steps (or end of path)
|
// refine path, to at least 20 steps (or end of path)
|
||||||
AnnotatedMap *aMap = world->getCartographer()->getMasterMap();
|
AnnotatedMap *aMap = world->getCartographer()->getMasterMap();
|
||||||
aMap->annotateLocal(unit);
|
aMap->annotateLocal(unit);
|
||||||
|
|
|
@ -180,8 +180,10 @@ private:
|
||||||
bool refinePath(Unit *unit);
|
bool refinePath(Unit *unit);
|
||||||
void smoothPath(Unit *unit);
|
void smoothPath(Unit *unit);
|
||||||
|
|
||||||
|
HAAStarResult setupHierarchicalOpenList(Unit *unit, const Vec2i &target);
|
||||||
HAAStarResult setupHierarchicalSearch(Unit *unit, const Vec2i &dest, TransitionGoal &goalFunc);
|
HAAStarResult setupHierarchicalSearch(Unit *unit, const Vec2i &dest, TransitionGoal &goalFunc);
|
||||||
HAAStarResult findWaypointPath(Unit *unit, const Vec2i &dest, WaypointPath &waypoints);
|
HAAStarResult findWaypointPath(Unit *unit, const Vec2i &dest, WaypointPath &waypoints);
|
||||||
|
HAAStarResult findWaypointPathUnExplored(Unit *unit, const Vec2i &dest, WaypointPath &waypoints);
|
||||||
|
|
||||||
World *world;
|
World *world;
|
||||||
SearchEngine<NodePool> *nsgSearchEngine;
|
SearchEngine<NodePool> *nsgSearchEngine;
|
||||||
|
|
|
@ -235,7 +235,7 @@ public:
|
||||||
* @param heuristic heuristic function object
|
* @param heuristic heuristic function object
|
||||||
*/
|
*/
|
||||||
template< typename GoalFunc, typename CostFunc, typename Heuristic >
|
template< typename GoalFunc, typename CostFunc, typename Heuristic >
|
||||||
AStarResult aStar(GoalFunc goalFunc, CostFunc costFunc, Heuristic heuristic) {
|
AStarResult aStar(GoalFunc &goalFunc, CostFunc &costFunc, Heuristic &heuristic) {
|
||||||
expanded = 0;
|
expanded = 0;
|
||||||
DomainKey minPos(invalidKey);
|
DomainKey minPos(invalidKey);
|
||||||
vector<DomainKey> neighbours;
|
vector<DomainKey> neighbours;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user