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 } }