#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Pathfinder
{
public interface IPathSearch : IDisposable
{
///
/// The Graph used by the A*
///
IGraph Graph { get; }
///
/// Stores the analyzed nodes by the expand function
///
IEnumerable> Considered { get; }
Player Owner { get; }
int MaxCost { get; }
IPathSearch Reverse();
IPathSearch WithCustomBlocker(Func customBlock);
IPathSearch WithIgnoredActor(Actor b);
IPathSearch WithHeuristic(Func h);
IPathSearch WithHeuristicWeight(int percentage);
IPathSearch WithCustomCost(Func w);
IPathSearch WithoutLaneBias();
IPathSearch FromPoint(CPos from);
///
/// Decides whether a location is a target based on its estimate
/// (An estimate of 0 means that the location and the unit's goal
/// are the same. There could be multiple goals).
///
/// The location to assess
/// Whether the location is a target
bool IsTarget(CPos location);
bool CanExpand { get; }
CPos Expand();
}
public abstract class BasePathSearch : IPathSearch
{
public IGraph Graph { get; set; }
protected IPriorityQueue OpenQueue { get; private set; }
public abstract IEnumerable> Considered { get; }
public Player Owner { get { return Graph.Actor.Owner; } }
public int MaxCost { get; protected set; }
public bool Debug { get; set; }
protected Func heuristic;
protected Func isGoal;
protected int heuristicWeightPercentage;
// This member is used to compute the ID of PathSearch.
// Essentially, it represents a collection of the initial
// points considered and their Heuristics to reach
// the target. It pretty match identifies, in conjunction of the Actor,
// a deterministic set of calculations
protected readonly IPriorityQueue StartPoints;
private readonly int cellCost, diagonalCellCost;
protected BasePathSearch(IGraph graph)
{
Graph = graph;
OpenQueue = new PriorityQueue(GraphConnection.ConnectionCostComparer);
StartPoints = new PriorityQueue(GraphConnection.ConnectionCostComparer);
MaxCost = 0;
heuristicWeightPercentage = 100;
// Determine the minimum possible cost for moving horizontally between cells based on terrain speeds.
// The minimum possible cost diagonally is then Sqrt(2) times more costly.
cellCost = graph.Actor.Trait().Locomotor.Info.TerrainSpeeds.Values.Min(ti => ti.Cost);
diagonalCellCost = cellCost * 141421 / 100000;
}
///
/// Default: Diagonal distance heuristic. More information:
/// http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
///
/// A delegate that calculates the estimation for a node
protected Func DefaultEstimator(CPos destination)
{
return here =>
{
var diag = Math.Min(Math.Abs(here.X - destination.X), Math.Abs(here.Y - destination.Y));
var straight = Math.Abs(here.X - destination.X) + Math.Abs(here.Y - destination.Y);
// According to the information link, this is the shape of the function.
// We just extract factors to simplify.
// Possible simplification: var h = Constants.CellCost * (straight + (Constants.Sqrt2 - 2) * diag);
return (cellCost * straight + (diagonalCellCost - 2 * cellCost) * diag) * heuristicWeightPercentage / 100;
};
}
public IPathSearch Reverse()
{
Graph.InReverse = true;
return this;
}
public IPathSearch WithCustomBlocker(Func customBlock)
{
Graph.CustomBlock = customBlock;
return this;
}
public IPathSearch WithIgnoredActor(Actor b)
{
Graph.IgnoreActor = b;
return this;
}
public IPathSearch WithHeuristic(Func h)
{
heuristic = h;
return this;
}
public IPathSearch WithHeuristicWeight(int percentage)
{
heuristicWeightPercentage = percentage;
return this;
}
public IPathSearch WithCustomCost(Func w)
{
Graph.CustomCost = w;
return this;
}
public IPathSearch WithoutLaneBias()
{
Graph.LaneBias = 0;
return this;
}
public IPathSearch FromPoint(CPos from)
{
if (Graph.World.Map.Contains(from))
AddInitialCell(from);
return this;
}
protected abstract void AddInitialCell(CPos cell);
public bool IsTarget(CPos location)
{
return isGoal(location);
}
public bool CanExpand { get { return !OpenQueue.Empty; } }
public abstract CPos Expand();
protected virtual void Dispose(bool disposing)
{
if (disposing)
Graph.Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}