X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;ds=sidebyside;f=CarFire%2FCarFire%2FCarFire%2FPathFinder.cs;fp=CarFire%2FCarFire%2FCarFire%2FPathFinder.cs;h=8b627ace0bed4f8500132ee33da9b6540615940b;hb=8bd8893e7f7516c76558ad7262e9f9350f5192b9;hp=0000000000000000000000000000000000000000;hpb=1368c1af3d7a4a12b0b0577dbe3edbfd254e2d04;p=chaz%2Fcarfire diff --git a/CarFire/CarFire/CarFire/PathFinder.cs b/CarFire/CarFire/CarFire/PathFinder.cs new file mode 100644 index 0000000..8b627ac --- /dev/null +++ b/CarFire/CarFire/CarFire/PathFinder.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; +using Microsoft.Xna.Framework; + +namespace CarFire +{ + /// + /// A class to navigate from here to there through a grid of + /// open and closed cells. + /// + public class PathFinder + { + #region Public Types + + /// + /// The heuristic function should return some value representing + /// the distance between two points. A common approach is to + /// return the manhattan distance. + /// + /// A point. + /// The endpoint. + /// The heuristic. + public delegate int Heuristic(Point a, Point b); + + /// + /// The cost function should take two points representing two + /// adjacent cells and return a cost measure of how expensive it + /// is to move from one cell to the other. + /// + /// A point. + /// Another point. + /// The cost. + public delegate int CostFunction(Point a, Point b); + + #endregion + + + #region Public Methods + + /// + /// Construct a path finder with a grid. The grid is a matrix + /// of boolean values, true meaning the cell is walkable and false + /// meaning the cell is closed. + /// + /// The grid to find paths on. + public PathFinder(bool[,] grid) + { + Debug.Assert(grid != null); + + mGrid = grid; + mGridWidth = mGrid.GetUpperBound(0) + 1; + mGridHeight = mGrid.GetUpperBound(1) + 1; + } + + + /// + /// The A* algorithm for finding the best path through a grid of cells. + /// The manhattan distance heuristic and a simple distance-based cost + /// function will be used. + /// + /// The cell to start at. + /// The desired destination. + /// A list of points representing the path through the grid, + /// ends points not included, or null if no path could be found. + public List GetPath(Point start, Point finish) + { + return GetPath(start, finish, GetManhattanDistance, GetCost); + } + + /// + /// The A* algorithm for finding the best path through a grid of cells. + /// A simple distance-based cost function will be used. + /// + /// The cell to start at. + /// The desired destination. + /// The heuristic function. + /// A list of points representing the path through the grid, + /// ends points not included, or null if no path could be found. + public List GetPath(Point start, Point finish, Heuristic heuristic) + { + return GetPath(start, finish, heuristic, GetCost); + } + + /// + /// The A* algorithm for finding the best path through a grid of cells. + /// The manhattan distance heuristic will be used. + /// + /// The cell to start at. + /// The desired destination. + /// The cost function + /// A list of points representing the path through the grid, + /// ends points not included, or null if no path could be found. + public List GetPath(Point start, Point finish, CostFunction costFunction) + { + return GetPath(start, finish, GetManhattanDistance, costFunction); + } + + /// + /// The A* algorithm for finding the best path through a grid of cells. + /// + /// The cell to start at. + /// The desired destination. + /// The heuristic function. + /// The cost function. + /// A list of points representing the path through the grid, + /// ends points not included, or null if no path could be found. + public List GetPath(Point start, Point finish, Heuristic heuristic, CostFunction costFunction) + { + mFringe = new BinaryHeap(); + mCells = new Cell[mGridWidth, mGridHeight]; + + Cell startCell = new Cell(start, 0, GetManhattanDistance(start, finish)); + mFringe.Add(startCell); + mCells[start.X, start.Y] = startCell; + while (mFringe.Count > 0) + { + Cell cell = mFringe.GetNext(); + cell.IsOpen = false; + + if (cell.Point == finish) + { + List list = new List(); + + cell = cell.Parent; + while (cell.Point != start) + { + list.Add(cell.Point); + cell = cell.Parent; + } + + list.Reverse(); + return list; + } + + List neighbors = new List(8); + neighbors.Add(new Point(cell.Point.X - 1, cell.Point.Y - 1)); + neighbors.Add(new Point(cell.Point.X + 0, cell.Point.Y - 1)); + neighbors.Add(new Point(cell.Point.X + 1, cell.Point.Y - 1)); + neighbors.Add(new Point(cell.Point.X - 1, cell.Point.Y + 0)); + neighbors.Add(new Point(cell.Point.X + 1, cell.Point.Y + 0)); + neighbors.Add(new Point(cell.Point.X - 1, cell.Point.Y + 1)); + neighbors.Add(new Point(cell.Point.X + 0, cell.Point.Y + 1)); + neighbors.Add(new Point(cell.Point.X + 1, cell.Point.Y + 1)); + foreach (Point point in neighbors) + { + Cell inQueue = mCells[point.X, point.Y]; + + if (0 <= point.X && point.X < mGridWidth && 0 <= point.Y && point.Y < mGridHeight && + mGrid[point.X, point.Y]) + { + int cost = cell.G + GetCost(cell.Point, point); + + if (inQueue == null) + { + Cell neighbor = new Cell(point, cost, GetManhattanDistance(point, finish), cell); + mFringe.Add(neighbor); + mCells[point.X, point.Y] = neighbor; + } + else if (inQueue.IsOpen && cost < inQueue.G) + { + inQueue.G = cost; + inQueue.Parent = cell; + mFringe.Promote(inQueue); + } + } + } + } + + return null; + } + + + /// + /// Get the manhattan distance between two points. This is a simple but + /// effective and commonly-used heuristic. + /// + /// A point. + /// Another point. + /// The manhattan distance. + public static int GetManhattanDistance(Point a, Point b) + { + int w = b.X - a.X; + int h = b.Y - a.Y; + if (w < 0) w = -w; + if (h < 0) h = -h; + return w + h; + } + + /// + /// Get the cost to travel from one point to another. This is a simple + /// cost function based purely on distance. On a square grid, diagonal + /// cells are further away than adjacent cells; therefore, adjacent moves + /// are favored. + /// + /// A point. + /// Another point. + /// The cost. + public static int GetCost(Point a, Point b) + { + if (a.X != b.X && a.Y != b.Y) return 14; + return 10; + } + + #endregion + + + #region Private Types + + class Cell : IComparable + { + public Point Point; + public Cell Parent; + public bool IsOpen; + + public int G + { + get { return mG; } + set { mG = value; mF = mG + mH; } + } + + public int H + { + get { return mH; } + set { mH = value; mF = mG + mH; } + } + + public int F { get { return mF; } } + + + public Cell(Point point, int g, int h) + { + Point = point; + IsOpen = true; + mG = g; + mH = h; + mF = g + h; + } + + public Cell(Point point, int g, int h, Cell parent) + { + Point = point; + Parent = parent; + IsOpen = true; + mG = g; + mH = h; + mF = g + h; + } + + public int CompareTo(Cell other) + { + return F - other.F; + } + + + int mG; + int mH; + int mF; + } + + #endregion + + + #region Private Variables + + bool[,] mGrid; + int mGridWidth; + int mGridHeight; + + IPriorityQueue mFringe; + Cell[,] mCells; + + #endregion + } +}