// // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER // REMAINS UNCHANGED. // // Email: gustavo_franco@hotmail.com // // Copyright (C) 2006 Franco, Gustavo // //#define DEBUGON using System; using System.Drawing; using System.Collections.Generic; namespace AStar { #region Structs [Author("Franco, Gustavo")] public struct PathFinderNode { #region Variables Declaration public int F; public int G; public int H; // f = gone + heuristic public int X; public int Y; public int PX; // Parent public int PY; #endregion } #endregion #region Enum [Author("Franco, Gustavo")] public enum PathFinderNodeType { Start = 1, End = 2, Open = 4, Close = 8, Current = 16, Path = 32 } public enum HeuristicFormula { Manhattan = 1, MaxDXDY = 2, DiagonalShortCut = 3, Euclidean = 4, EuclideanNoSQR = 5, Custom1 = 6 } #endregion #region Delegates public delegate void PathFinderDebugHandler(int fromX, int fromY, int x, int y, PathFinderNodeType type, int totalCost, int cost); #endregion [Author("Franco, Gustavo")] public class PathFinder : IPathFinder { [System.Runtime.InteropServices.DllImport("KERNEL32.DLL", EntryPoint="RtlZeroMemory")] public unsafe static extern bool ZeroMemory(byte* destination, int length); #region Events public event PathFinderDebugHandler PathFinderDebug; #endregion #region Variables Declaration private byte[,] mGrid = null; private PriorityQueueB mOpen = new PriorityQueueB(new ComparePFNode()); private List mClose = new List(); private bool mStop = false; private bool mStopped = true; private int mHoriz = 0; private HeuristicFormula mFormula = HeuristicFormula.Manhattan; private bool mDiagonals = true; private int mHEstimate = 2; private bool mPunishChangeDirection = false; private bool mTieBreaker = false; private bool mHeavyDiagonals = false; private int mSearchLimit = 2000; private double mCompletedTime = 0; private bool mDebugProgress = false; private bool mDebugFoundPath = false; #endregion #region Constructors public PathFinder(byte[,] grid) { if (grid == null) throw new Exception("Grid cannot be null"); mGrid = grid; } #endregion #region Properties public bool Stopped { get { return mStopped; } } public HeuristicFormula Formula { get { return mFormula; } set { mFormula = value; } } public bool Diagonals { get { return mDiagonals; } set { mDiagonals = value; } } public bool HeavyDiagonals { get { return mHeavyDiagonals; } set { mHeavyDiagonals = value; } } public int HeuristicEstimate { get { return mHEstimate; } set { mHEstimate = value; } } public bool PunishChangeDirection { get { return mPunishChangeDirection; } set { mPunishChangeDirection = value; } } public bool TieBreaker { get { return mTieBreaker; } set { mTieBreaker = value; } } public int SearchLimit { get { return mSearchLimit; } set { mSearchLimit = value; } } public double CompletedTime { get { return mCompletedTime; } set { mCompletedTime = value; } } public bool DebugProgress { get { return mDebugProgress; } set { mDebugProgress = value; } } public bool DebugFoundPath { get { return mDebugFoundPath; } set { mDebugFoundPath = value; } } #endregion #region Methods public void FindPathStop() { mStop = true; } public List FindPath(Point start, Point end) { HighResolutionTime.Start(); PathFinderNode parentNode; bool found = false; int gridX = mGrid.GetUpperBound(0); int gridY = mGrid.GetUpperBound(1); mStop = false; mStopped = false; mOpen.Clear(); mClose.Clear(); #if DEBUGON if (mDebugProgress && PathFinderDebug != null) PathFinderDebug(0, 0, start.X, start.Y, PathFinderNodeType.Start, -1, -1); if (mDebugProgress && PathFinderDebug != null) PathFinderDebug(0, 0, end.X, end.Y, PathFinderNodeType.End, -1, -1); #endif sbyte[,] direction; if (mDiagonals) direction = new sbyte[8,2]{ {0,-1} , {1,0}, {0,1}, {-1,0}, {1,-1}, {1,1}, {-1,1}, {-1,-1}}; else direction = new sbyte[4,2]{ {0,-1} , {1,0}, {0,1}, {-1,0}}; parentNode.G = 0; parentNode.H = mHEstimate; parentNode.F = parentNode.G + parentNode.H; parentNode.X = start.X; parentNode.Y = start.Y; parentNode.PX = parentNode.X; parentNode.PY = parentNode.Y; mOpen.Push(parentNode); while(mOpen.Count > 0 && !mStop) { parentNode = mOpen.Pop(); #if DEBUGON if (mDebugProgress && PathFinderDebug != null) PathFinderDebug(0, 0, parentNode.X, parentNode.Y, PathFinderNodeType.Current, -1, -1); #endif if (parentNode.X == end.X && parentNode.Y == end.Y) { mClose.Add(parentNode); found = true; break; } if (mClose.Count > mSearchLimit) { mStopped = true; return null; } if (mPunishChangeDirection) mHoriz = (parentNode.X - parentNode.PX); //Lets calculate each successors for (int i=0; i<(mDiagonals ? 8 : 4); i++) { PathFinderNode newNode; newNode.X = parentNode.X + direction[i,0]; newNode.Y = parentNode.Y + direction[i,1]; if (newNode.X < 0 || newNode.Y < 0 || newNode.X >= gridX || newNode.Y >= gridY) continue; int newG; if (mHeavyDiagonals && i>3) newG = parentNode.G + (int) (mGrid[newNode.X, newNode.Y] * 2.41); else newG = parentNode.G + mGrid[newNode.X, newNode.Y]; if (newG == parentNode.G) { //Unbrekeable continue; } if (mPunishChangeDirection) { if ((newNode.X - parentNode.X) != 0) { if (mHoriz == 0) newG += 20; } if ((newNode.Y - parentNode.Y) != 0) { if (mHoriz != 0) newG += 20; } } int foundInOpenIndex = -1; for(int j=0; j=0; i--) { if (fNode.PX == mClose[i].X && fNode.PY == mClose[i].Y || i == mClose.Count - 1) { #if DEBUGON if (mDebugFoundPath && PathFinderDebug != null) PathFinderDebug(fNode.X, fNode.Y, mClose[i].X, mClose[i].Y, PathFinderNodeType.Path, mClose[i].F, mClose[i].G); #endif fNode = mClose[i]; } else mClose.RemoveAt(i); } mStopped = true; return mClose; } mStopped = true; return null; } #endregion #region Inner Classes [Author("Franco, Gustavo")] internal class ComparePFNode : IComparer { #region IComparer Members public int Compare(PathFinderNode x, PathFinderNode y) { if (x.F > y.F) return 1; else if (x.F < y.F) return -1; return 0; } #endregion } #endregion } }