Expose a setting for Weighted A*
Replace Constants.CellCost and Constants.DiagonalCellCost with a dynamically calculated value based on the lowest cost terrain to traverse. Using a fixed value meant the pathfinder heuristics would be incorrect. In the four default mods, the minimum cost is in fact 100, not 125. This increase would essentially allow the pathfinder to return suboptimal paths up to 25% longer in the worst case, but it would be quicker to do so. This is exactly what Weighted A* does - overestimate the heuristic by some factor in order to speed up the search by checking fewer routes. This makes the heuristic inadmissible and it may now return suboptimal paths, but their worst case length is bounded by the weight. A weight of 125% will never produce paths more than 25% longer than the shortest, optimal, path. We set the default weight to 25% to effectively maintain the existing, suboptimal, behaviour due to the choice of the old constant - in future it may prove a useful tuning knob for performance.
This commit is contained in:
@@ -75,6 +75,8 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
|
||||
sealed class PathGraph : IGraph<CellInfo>
|
||||
{
|
||||
public const int CostForInvalidCell = int.MaxValue;
|
||||
|
||||
public Actor Actor { get; private set; }
|
||||
public World World { get; private set; }
|
||||
public Func<CPos, bool> CustomBlock { get; set; }
|
||||
@@ -146,7 +148,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
{
|
||||
var neighbor = position + directions[i];
|
||||
var movementCost = GetCostToNode(neighbor, directions[i]);
|
||||
if (movementCost != Constants.InvalidNode)
|
||||
if (movementCost != CostForInvalidCell)
|
||||
validNeighbors.Add(new GraphConnection(neighbor, movementCost));
|
||||
}
|
||||
|
||||
@@ -156,7 +158,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
{
|
||||
var layerPosition = new CPos(position.X, position.Y, cli.First.Index);
|
||||
var entryCost = cli.First.EntryMovementCost(Actor.Info, locomotor.Info, layerPosition);
|
||||
if (entryCost != Constants.InvalidNode)
|
||||
if (entryCost != CostForInvalidCell)
|
||||
validNeighbors.Add(new GraphConnection(layerPosition, entryCost));
|
||||
}
|
||||
}
|
||||
@@ -164,7 +166,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
{
|
||||
var layerPosition = new CPos(position.X, position.Y, 0);
|
||||
var exitCost = customLayerInfo[position.Layer].First.ExitMovementCost(Actor.Info, locomotor.Info, layerPosition);
|
||||
if (exitCost != Constants.InvalidNode)
|
||||
if (exitCost != CostForInvalidCell)
|
||||
validNeighbors.Add(new GraphConnection(layerPosition, exitCost));
|
||||
}
|
||||
|
||||
@@ -177,7 +179,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
if (movementCost != short.MaxValue && !(CustomBlock != null && CustomBlock(destNode)))
|
||||
return CalculateCellCost(destNode, direction, movementCost);
|
||||
|
||||
return Constants.InvalidNode;
|
||||
return CostForInvalidCell;
|
||||
}
|
||||
|
||||
int CalculateCellCost(CPos neighborCPos, CVec direction, int movementCost)
|
||||
@@ -190,8 +192,8 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
if (CustomCost != null)
|
||||
{
|
||||
var customCost = CustomCost(neighborCPos);
|
||||
if (customCost == Constants.InvalidNode)
|
||||
return Constants.InvalidNode;
|
||||
if (customCost == CostForInvalidCell)
|
||||
return CostForInvalidCell;
|
||||
|
||||
cellCost += customCost;
|
||||
}
|
||||
@@ -201,7 +203,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
{
|
||||
var from = neighborCPos - direction;
|
||||
if (Math.Abs(World.Map.Height[neighborCPos] - World.Map.Height[from]) > 1)
|
||||
return Constants.InvalidNode;
|
||||
return CostForInvalidCell;
|
||||
}
|
||||
|
||||
// Directional bonuses for smoother flow!
|
||||
|
||||
Reference in New Issue
Block a user