using System;
using System.Collections;
namespace Holo.Virtual.Rooms.Pathfinding
{
///
/// Base class for pathfinding nodes, it holds no actual information about the map.
/// An inherited class must be constructed from this class and all virtual methods must be
/// implemented. Note, that calling base() in the overridden methods is not needed.
///
public class AStarNode : IComparable
{
#region Properties
private AStarNode FParent;
///
/// The parent of the node.
///
public AStarNode Parent
{
get
{
return FParent;
}
set
{
FParent = value;
}
}
///
/// The accumulative cost of the path until now.
///
public double Cost
{
set
{
FCost = value;
}
get
{
return FCost;
}
}
private double FCost;
///
/// The estimated cost to the goal from here.
///
public double GoalEstimate
{
set
{
FGoalEstimate = value;
}
get
{
Calculate();
return(FGoalEstimate);
}
}
private double FGoalEstimate;
///
/// The cost plus the estimated cost to the goal from here.
///
public double TotalCost
{
get
{
return(Cost + GoalEstimate);
}
}
///
/// The goal node.
///
public AStarNode GoalNode
{
set
{
FGoalNode = value;
Calculate();
}
get
{
return FGoalNode;
}
}
private AStarNode FGoalNode;
#endregion
#region Constructors
///
/// Constructor.
///
/// The node's parent
/// The goal node
/// The accumulative cost until now
public AStarNode(AStarNode AParent,AStarNode AGoalNode,double ACost)
{
FParent = AParent;
FCost = ACost;
GoalNode = AGoalNode;
}
#endregion
#region Public Methods
///
/// Determines wheather the current node is the goal.
///
/// Returns true if current node is the goal
public bool IsGoal()
{
return IsSameState(FGoalNode);
}
#endregion
#region Virtual Methods
///
/// Determines wheather the current node is the same state as the on passed.
///
/// AStarNode to compare the current node to
/// Returns true if they are the same state
public virtual bool IsSameState(AStarNode ANode)
{
return false;
}
///
/// Calculates the estimated cost for the remaining trip to the goal.
///
public virtual void Calculate()
{
FGoalEstimate = 0.0f;
}
///
/// Gets all successors nodes from the current node and adds them to the successor list
///
/// List in which the successors will be added
public virtual void GetSuccessors(ArrayList ASuccessors)
{
}
///
/// Prints information about the current node
///
public virtual void PrintNodeInfo()
{
}
#endregion
#region Overridden Methods
public override bool Equals(object obj)
{
return IsSameState((AStarNode)obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
#endregion
#region IComparable Members
public int CompareTo(object obj)
{
return(-TotalCost.CompareTo(((AStarNode)obj).TotalCost));
}
#endregion
}
///
/// Class for performing A* pathfinding
///
public sealed class AStar
{
#region Private Fields
private AStarNode FStartNode;
private AStarNode FGoalNode;
private Heap FOpenList;
private Heap FClosedList;
private ArrayList FSuccessors;
#endregion
#region Properties
///
/// Holds the solution after pathfinding is done. FindPath()
///
public ArrayList Solution
{
get
{
return FSolution;
}
}
private ArrayList FSolution;
#endregion
#region Constructors
public AStar()
{
FOpenList = new Heap();
FClosedList = new Heap();
FSuccessors = new ArrayList();
FSolution = new ArrayList();
}
#endregion
#region Private Methods
///
/// Prints all the nodes in a list
///
/// List to print
private void PrintNodeList(object ANodeList)
{
Console.WriteLine("Node list:");
foreach(AStarNode n in (ANodeList as IEnumerable))
{
n.PrintNodeInfo();
}
Console.WriteLine("=====");
}
#endregion
#region Public Methods
///
/// Finds the shortest path from the start node to the goal node
///
/// Start node
/// Goal node
public void FindPath(AStarNode AStartNode,AStarNode AGoalNode)
{
FStartNode = AStartNode;
FGoalNode = AGoalNode;
FOpenList.Add(FStartNode);
while(FOpenList.Count > 0)
{
// Get the node with the lowest TotalCost
AStarNode NodeCurrent = (AStarNode)FOpenList.Pop();
// If the node is the goal copy the path to the solution array
if(NodeCurrent.IsGoal()) {
while(NodeCurrent != null) {
FSolution.Insert(0,NodeCurrent);
NodeCurrent = NodeCurrent.Parent;
}
break;
}
// Get successors to the current node
NodeCurrent.GetSuccessors(FSuccessors);
foreach(AStarNode NodeSuccessor in FSuccessors)
{
// Test if the currect successor node is on the open list, if it is and
// the TotalCost is higher, we will throw away the current successor.
AStarNode NodeOpen = null;
if(FOpenList.Contains(NodeSuccessor))
NodeOpen = (AStarNode)FOpenList[FOpenList.IndexOf(NodeSuccessor)];
if((NodeOpen != null) && (NodeSuccessor.TotalCost > NodeOpen.TotalCost))
continue;
// Test if the currect successor node is on the closed list, if it is and
// the TotalCost is higher, we will throw away the current successor.
AStarNode NodeClosed = null;
if(FClosedList.Contains(NodeSuccessor))
NodeClosed = (AStarNode)FClosedList[FClosedList.IndexOf(NodeSuccessor)];
if((NodeClosed != null) && (NodeSuccessor.TotalCost > NodeClosed.TotalCost))
continue;
// Remove the old successor from the open list
FOpenList.Remove(NodeOpen);
// Remove the old successor from the closed list
FClosedList.Remove(NodeClosed);
// Add the current successor to the open list
FOpenList.Push(NodeSuccessor);
}
// Add the current node to the closed list
FClosedList.Add(NodeCurrent);
}
}
#endregion
}
}