Files
source/Tactical/AStarPathfinder.cpp
T

186 lines
5.6 KiB
C++

//#include "stdafx.h"
#include "profiler.h"
using namespace std;
void AStarPathfinder::ResetAStarList()
{
for each (CSmallPoint node in ClosedList) {
SetAStarStatus(node, AStar_Init);
SetAStarGCost(node, 0.0f);
SetAStarGTime(node, 0.0f);
}
for (int size = OpenHeap.size(); size > 0; --size) {
CSmallPoint node = OpenHeap.peekElement(size).data;
SetAStarStatus(node, AStar_Init);
SetAStarGCost(node, 0.0f);
SetAStarGTime(node, 0.0f);
}
ClosedList.clear();
OpenHeap.clear();
return;
}//end ResetAStarList
AStarHeap AStarPathfinder::AStar(const CSmallPoint& StartCity,
const vector<AStarHeap>& acceptableDestCities,
const ePlayer player,
const unsigned int maxNodes)
{
AStarHeap TopHeap(StartCity, 0);
CSmallPoint BestNode = StartCity;
for (;;) {
//this peg is now closed
//SetAStarStatus(BestNode, AStar_Closed);
ClosedList.push_back(BestNode);
if (isNodeDestCity(BestNode, acceptableDestCities) == true) {
//the best path to date leads to a city, other cities are not as good, so return this city
return TopHeap;
}
//if we have run out of time and the best node isn't a city
if (OpenHeap.size() + ClosedList.size() > maxNodes) {
float best = -99999999.0f;
int bestIndex = 0;
for (int index = 1; index <= OpenHeap.size(); ++index) {
float key = OpenHeap.peekElement(index).key;
if (key > best) {
bestIndex = index;
best = key;
}
}
if (bestIndex) {
//but at least one city was opened, it might not be A* efficient, but it's a path
return OpenHeap.peekElement(bestIndex);
}
else {
//no cities were opened
return AStarHeap(CSmallPoint(0,0), 111);
}
}
//this adds to the heap nodes that might be on a path to a city
ExecuteAStarLogic(StartCity, BestNode, acceptableDestCities, player);
if (OpenHeap.size() == 0) {
return AStarHeap(CSmallPoint(0,0), -222);
}
//get the best point so far from the heap
TopHeap = OpenHeap.popTopHeap();
BestNode = TopHeap.data;
//ASSERT(GetAStarStatus(BestNode) == AStar_Open);
}
return TopHeap;
}//end AStar
void AStarPathfinder::ExecuteAStarLogic(const CSmallPoint& StartCity,
const CSmallPoint& BestNode,
const vector<AStarHeap>& acceptableDestCities,
const ePlayer player)
{
float baseGCost = GetAStarGCost(BestNode);
float baseGTime = GetAStarGTime(BestNode);
for (int index = 0; index < 8; ++index) {
CSmallPoint ThisNode = BestNode + offsets[index];
if (CWorldManager::isNodeInWorld(ThisNode) == false ||
GetAStarStatus(ThisNode) == AStar_Closed) {
continue;
}
float AStarGCost = baseGCost + CalcGCost(BestNode, ThisNode, player);
float AStarGTime = baseGTime + CalcGTime(BestNode, ThisNode);
if (GetAStarStatus(ThisNode) == AStar_Open
&& AStarGCost >= GetAStarGCost(ThisNode)
&& AStarGTime >= GetAStarGTime(ThisNode)) {
//no matter what Dest City, this path is less profitable
continue;
}
CSmallPoint bestCity(0,0);
float AStarF = 0.0f;
for each (AStarHeap city in acceptableDestCities) {
//is this path and Dest City combo profitable?
if (city.key > AStarGCost) {
//find the best H
float AStarHTime = CalcHTime(ThisNode, city.data);
float AStarHProfit = city.key;
float estimatedF = (AStarHProfit - AStarGCost) / (AStarHTime + AStarGTime);
if (AStarF < estimatedF) {
bestCity = city.data;
AStarF = estimatedF;
}
}
}
if (AStarF == 0.0f) {
//no profitable paths remain from this node
continue;
}
//insert this node onto the heap
if (GetAStarStatus(ThisNode) == AStar_Init) {
OpenHeap.insertElement(ThisNode, -AStarF);
}
else {
if (GetAStarF(ThisNode) > AStarF) {
//the node is already open and this path is not more cost efficient
//we had to do the whole calculation and not just compare the GCost / GTime ratio
//because changing them affect the Dest City and AStarF
continue;
}
OpenHeap.editElement(ThisNode, -AStarF);
}
SetAStarStatus(ThisNode, AStar_Open);
SetAStarF(ThisNode, AStarF);
SetAStarGCost(ThisNode, AStarGCost);
SetAStarGTime(ThisNode, AStarGTime);
SetAStarParent(ThisNode, BestNode);
SetAStarCity(ThisNode, bestCity);
}
return;
}//end ExecuteAStarLogic
bool AStarPathfinder::isNodeDestCity(const CSmallPoint& BestNode,
const vector<AStarHeap>& acceptableDestCities)
{
for each (AStarHeap city in acceptableDestCities) {
if (city.data == BestNode) return true;
}
return false;
}//end isNodeDestCity
float AStarPathfinder::CalcHTime(const CSmallPoint& start, const CSmallPoint& dest)
{
int x = abs(start.x - dest.x);
int y = abs(start.y - dest.y);
int diagonal, straight;
if (x > y) {
diagonal = x;
straight = x - y;
}
else {
diagonal = y;
straight = y - x;
}
//this can be made more exact by incorporating the traversal time
//of the start half of the next square, * 1.42f to be sure
return ((diagonal * 1.42f + straight) * MAX_TRAVERSAL_TIME);
}//end CalcHTime
float AStarPathfinder::CalcGCost(const CSmallPoint& start, const CSmallPoint& dest, const ePlayer player)
{
return 0.0f;
}//end CalcGCost
float AStarPathfinder::CalcGTime(const CSmallPoint& start, const CSmallPoint& dest)
{
//traversal costs are half the cost of traversing the whole node
//we are going from midpoint to midpoint
float traversalTime = CWorldManager::GetTerrainType(start) + CWorldManager::GetTerrainType(dest);
if (abs(start.x - dest.x) + abs(start.y + dest.y) == 2) traversalTime *= 1.42f;
return (traversalTime);
}//end CalcGTime