using System;
using System.Collections;
namespace Holo.Virtual.Rooms.Pathfinding
{
///
/// A node class for doing pathfinding on a 2-dimensional map
///
public class AStarNode2D : AStarNode
{
#region Properties
///
/// The X-coordinate of the node
///
public int X
{
get
{
return FX;
}
}
private int FX;
///
/// The Y-coordinate of the node
///
public int Y
{
get
{
return FY;
}
}
private int FY;
#endregion
#region Constructors
///
/// Constructor for a node in a 2-dimensional map
///
/// Parent of the node
/// Goal node
/// Accumulative cost
/// X-coordinate
/// Y-coordinate
public AStarNode2D(AStarNode AParent,AStarNode AGoalNode,double ACost,int AX, int AY) : base(AParent,AGoalNode,ACost)
{
FX = AX;
FY = AY;
}
#endregion
#region Private Methods
///
/// Adds a successor to a list if it is not impassible or the parent node
///
/// List of successors
/// X-coordinate
/// Y-coordinate
private void AddSuccessor(ArrayList ASuccessors,int AX,int AY)
{
int CurrentCost = MainClass.GetMap(AX,AY);
if(CurrentCost == -1)
{
return;
}
AStarNode2D NewNode = new AStarNode2D(this,GoalNode,Cost + CurrentCost,AX,AY);
if(NewNode.IsSameState(Parent))
{
return;
}
ASuccessors.Add(NewNode);
}
#endregion
#region Overidden 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 override bool IsSameState(AStarNode ANode)
{
if(ANode == null)
{
return false;
}
return ((((AStarNode2D)ANode).X == FX) &&
(((AStarNode2D)ANode).Y == FY));
}
///
/// Calculates the estimated cost for the remaining trip to the goal.
///
public override void Calculate()
{
if(GoalNode != null)
{
double xd = FX - ((AStarNode2D)GoalNode).X;
double yd = FY - ((AStarNode2D)GoalNode).Y;
// "Euclidean distance" - Used when search can move at any angle.
//GoalEstimate = Math.Sqrt((xd*xd) + (yd*yd));
// "Manhattan Distance" - Used when search can only move vertically and
// horizontally.
//GoalEstimate = Math.Abs(xd) + Math.Abs(yd);
// "Diagonal Distance" - Used when the search can move in 8 directions.
GoalEstimate = Math.Max(Math.Abs(xd),Math.Abs(yd));
}
else
{
GoalEstimate = 0;
}
}
///
/// Gets all successors nodes from the current node and adds them to the successor list
///
/// List in which the successors will be added
public override void GetSuccessors(ArrayList ASuccessors)
{
ASuccessors.Clear();
AddSuccessor(ASuccessors,FX-1,FY );
AddSuccessor(ASuccessors,FX-1,FY-1);
AddSuccessor(ASuccessors,FX ,FY-1);
AddSuccessor(ASuccessors,FX+1,FY-1);
AddSuccessor(ASuccessors,FX+1,FY );
AddSuccessor(ASuccessors,FX+1,FY+1);
AddSuccessor(ASuccessors,FX ,FY+1);
AddSuccessor(ASuccessors,FX-1,FY+1);
}
///
/// Prints information about the current node
///
public override void PrintNodeInfo()
{
Console.WriteLine("X:\t{0}\tY:\t{1}\tCost:\t{2}\tEst:\t{3}\tTotal:\t{4}",FX,FY,Cost,GoalEstimate,TotalCost);
}
#endregion
}
///
/// Test class for doing A* pathfinding on a 2D map.
///
class MainClass
{
#region Test Maps
static int[,] Map = {
{ 1,-1, 1, 1, 1,-1, 1, 1, 1, 1 },
{ 1,-1, 1,-1, 1,-1, 1, 1, 1, 1 },
{ 1,-1, 1,-1, 1,-1, 1, 1, 1, 1 },
{ 1,-1, 1,-1, 1,-1, 1, 1, 1, 1 },
{ 1,-1, 1,-1, 1,-1, 1, 1, 1, 1 },
{ 1,-1, 1,-1, 1,-1, 1, 1, 1, 1 },
{ 1,-1, 1,-1, 1,-1, 1, 1, 1, 1 },
{ 1,-1, 1,-1, 1,-1, 1, 1, 1, 1 },
{ 1,-1, 1,-1, 1,-1, 1, 2, 1, 1 },
{ 1, 1, 1,-1, 1, 1, 2, 3, 2, 1 }
};
// static int[,] Map = {
// { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
// { 1, 1, 1, 1, 1, 1, 1, 1,-1, 1 },
// { 1, 1, 1, 1, 1, 1, 1, 1,-1, 1 },
// { 1, 1, 1, 1, 1, 1, 1, 1,-1, 1 },
// { 1, 1, 1, 1, 1, 1, 1, 1,-1, 1 },
// { 1, 1, 1, 1, 1, 1, 1, 1,-1, 1 },
// { 1, 1, 1, 1, 1, 1, 1, 1,-1, 1 },
// { 1, 1, 1, 1, 1, 1, 1, 1,-1, 1 },
// { 1,-1,-1,-1,-1,-1,-1,-1,-1, 1 },
// { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
// };
// static int[,] Map = {
// { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
// { 1, 1, 1, 1, 1, 2, 1, 1, 1, 1 },
// { 1, 1, 1, 1, 2, 3, 2, 1, 1, 1 },
// { 1, 1, 1, 2, 3, 4, 3, 2, 1, 1 },
// { 1, 1, 2, 3, 4, 5, 4, 3, 2, 1 },
// { 1, 1, 1, 2, 3, 4, 3, 2, 1, 1 },
// { 1, 1, 1, 1, 2, 3, 2, 1, 1, 1 },
// { 1, 1, 1, 1, 1, 2, 1, 1, 1, 1 },
// { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
// { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
// };
#endregion
#region Public Methods
///
/// Gets movement cost from the 2-dimensional map
///
/// X-coordinate
/// Y-coordinate
/// Returns movement cost at the specified point in the map
static public int GetMap(int x,int y)
{
if((x < 0) || (x > 9))
return(-1);
if((y < 0) || (y > 9))
return(-1);
return(Map[y,x]);
}
///
/// Prints the solution
///
/// The list that holds the solution
static public void PrintSolution(ArrayList ASolution)
{
for(int j=0;j<10;j++)
{
for(int i=0;i<10;i++)
{
bool solution = false;
foreach(AStarNode2D n in ASolution)
{
AStarNode2D tmp = new AStarNode2D(null,null,0,i,j);
solution = n.IsSameState(tmp);
if(solution)
break;
}
if(solution)
Console.Write("S ");
else
if(MainClass.GetMap(i,j) == -1)
Console.Write("X ");
else
Console.Write(". ");
}
Console.WriteLine("");
}
}
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("Starting...");
Games.Pathfinding.AStar astar = new Games.Pathfinding.AStar();
AStarNode2D GoalNode = new AStarNode2D(null,null,0,9,9);
AStarNode2D StartNode = new AStarNode2D(null,GoalNode,0,0,0);
StartNode.GoalNode = GoalNode;
astar.FindPath(StartNode,GoalNode);
PrintSolution(astar.Solution);
Console.ReadLine();
}
#endregion
}
}