Advertisement
Guest User

Untitled

a guest
Sep 21st, 2017
278
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 19.34 KB | None | 0 0
  1. //
  2. //  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  3. //  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  4. //  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  5. //  PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
  6. //  REMAINS UNCHANGED.
  7. //
  8. //  Email:  gustavo_franco@hotmail.com
  9. //
  10. //  Copyright (C) 2006 Franco, Gustavo
  11. //
  12. //#define DEBUGON
  13.  
  14. using System;
  15. using System.Drawing;
  16. using System.Collections.Generic;
  17. using System.Runtime.InteropServices;
  18.  
  19. namespace prjMango.Game.Pathfinding
  20. {
  21.     public class PathFinderFast : IPathFinder
  22.     {
  23.         #region Structs
  24.         [StructLayout(LayoutKind.Sequential, Pack=1)]
  25.         internal struct PathFinderNodeFast
  26.         {
  27.             #region Variables Declaration
  28.             public int     F; // f = gone + heuristic
  29.             public int     G;
  30.             public ushort  PX; // Parent
  31.             public ushort  PY;
  32.             public byte    Status;
  33.             #endregion
  34.         }
  35.         #endregion
  36.  
  37.         #region Events
  38.         public event PathFinderDebugHandler PathFinderDebug;
  39.         #endregion
  40.  
  41.         #region Variables Declaration
  42.         // Heap variables are initializated to default, but I like to do it anyway
  43.         private byte[,]                         mGrid                   = null;
  44.         private PriorityQueueB<int>             mOpen                   = null;
  45.         private List<PathFinderNode>            mClose                  = new List<PathFinderNode>();
  46.         private bool                            mStop                   = false;
  47.         private bool                            mStopped                = true;
  48.         private int                             mHoriz                  = 0;
  49.         private HeuristicFormula                mFormula                = HeuristicFormula.DiagonalShortCut;
  50.         private bool                            mDiagonals              = true;
  51.         private int                             mHEstimate              = 2;
  52.         private bool                            mPunishChangeDirection  = false;
  53.         private bool                            mTieBreaker             = false;
  54.         private bool                            mHeavyDiagonals         = true;
  55.         private int                             mSearchLimit            = 2000;
  56.         private double                          mCompletedTime          = 0;
  57.         private bool                            mDebugProgress          = false;
  58.         private bool                            mDebugFoundPath         = false;
  59.         private PathFinderNodeFast[]            mCalcGrid               = null;
  60.         private byte                            mOpenNodeValue          = 1;
  61.         private byte                            mCloseNodeValue         = 2;
  62.        
  63.         //Promoted local variables to member variables to avoid recreation between calls
  64.         private int                             mH                      = 0;
  65.         private int                             mLocation               = 0;
  66.         private int                             mNewLocation            = 0;
  67.         private ushort                          mLocationX              = 0;
  68.         private ushort                          mLocationY              = 0;
  69.         private ushort                          mNewLocationX           = 0;
  70.         private ushort                          mNewLocationY           = 0;
  71.         private int                             mCloseNodeCounter       = 0;
  72.         private ushort                          mGridX                  = 0;
  73.         private ushort                          mGridY                  = 0;
  74.         private ushort                          mGridXMinus1            = 0;
  75.         private ushort                          mGridYLog2              = 0;
  76.         private bool                            mFound                  = false;
  77.         private sbyte[,]                        mDirection              = new sbyte[8,2]{{0,-1} , {1,0}, {0,1}, {-1,0}, {1,-1}, {1,1}, {-1,1}, {-1,-1}};
  78.         private int                             mEndLocation            = 0;
  79.         private int                             mNewG                   = 0;
  80.         #endregion
  81.  
  82.         #region Constructors
  83.         public PathFinderFast(byte[,] grid)
  84.         {
  85.             if (grid == null)
  86.                 throw new Exception("Grid cannot be null");
  87.  
  88.             mGrid           = grid;
  89.             mGridX          = (ushort) (mGrid.GetUpperBound(0) + 1);
  90.             mGridY          = (ushort) (mGrid.GetUpperBound(1) + 1);
  91.             mGridXMinus1    = (ushort) (mGridX - 1);
  92.             mGridYLog2      = (ushort) Math.Log(mGridY, 2);
  93.  
  94.             // This should be done at the constructor, for now we leave it here.
  95.             if (Math.Log(mGridX, 2) != (int) Math.Log(mGridX, 2) ||
  96.                 Math.Log(mGridY, 2) != (int) Math.Log(mGridY, 2))
  97.                 throw new Exception("Invalid Grid, size in X and Y must be power of 2");
  98.  
  99.             if (mCalcGrid == null || mCalcGrid.Length != (mGridX * mGridY))
  100.                 mCalcGrid = new PathFinderNodeFast[mGridX * mGridY];
  101.  
  102.             mOpen   = new PriorityQueueB<int>(new ComparePFNodeMatrix(mCalcGrid));
  103.         }
  104.         #endregion
  105.  
  106.         #region Properties
  107.         public bool Stopped
  108.         {
  109.             get { return mStopped; }
  110.         }
  111.  
  112.         public HeuristicFormula Formula
  113.         {
  114.             get { return mFormula; }
  115.             set { mFormula = value; }
  116.         }
  117.  
  118.         public bool Diagonals
  119.         {
  120.             get { return mDiagonals; }
  121.             set
  122.             {
  123.                 mDiagonals = value;
  124.                 if (mDiagonals)
  125.                     mDirection = new sbyte[8,2]{{0,-1} , {1,0}, {0,1}, {-1,0}, {1,-1}, {1,1}, {-1,1}, {-1,-1}};
  126.                 else
  127.                     mDirection = new sbyte[4,2]{{0,-1} , {1,0}, {0,1}, {-1,0}};
  128.             }
  129.         }
  130.  
  131.         public bool HeavyDiagonals
  132.         {
  133.             get { return mHeavyDiagonals; }
  134.             set { mHeavyDiagonals = value; }
  135.         }
  136.  
  137.         public int HeuristicEstimate
  138.         {
  139.             get { return mHEstimate; }
  140.             set { mHEstimate = value; }
  141.         }
  142.  
  143.         public bool PunishChangeDirection
  144.         {
  145.             get { return mPunishChangeDirection; }
  146.             set { mPunishChangeDirection = value; }
  147.         }
  148.  
  149.         public bool TieBreaker
  150.         {
  151.             get { return mTieBreaker; }
  152.             set { mTieBreaker = value; }
  153.         }
  154.  
  155.         public int SearchLimit
  156.         {
  157.             get { return mSearchLimit; }
  158.             set { mSearchLimit = value; }
  159.         }
  160.  
  161.         public double CompletedTime
  162.         {
  163.             get { return mCompletedTime; }
  164.             set { mCompletedTime = value; }
  165.         }
  166.  
  167.         public bool DebugProgress
  168.         {
  169.             get { return mDebugProgress; }
  170.             set { mDebugProgress = value; }
  171.         }
  172.  
  173.         public bool DebugFoundPath
  174.         {
  175.             get { return mDebugFoundPath; }
  176.             set { mDebugFoundPath = value; }
  177.         }
  178.         #endregion
  179.  
  180.         #region Methods
  181.         public void FindPathStop()
  182.         {
  183.             mStop = true;
  184.         }
  185.  
  186.         public List<PathFinderNode> FindPath(Point start, Point end)
  187.         {
  188.             lock(this)
  189.             {
  190.                 // Timer here maybe?
  191.  
  192.                 // Is faster if we don't clear the matrix, just assign different values for open and close and ignore the rest
  193.                 // I could have user Array.Clear() but using unsafe code is faster, no much but it is.
  194.                 //fixed (PathFinderNodeFast* pGrid = tmpGrid)
  195.                 //    ZeroMemory((byte*) pGrid, sizeof(PathFinderNodeFast) * 1000000);
  196.  
  197.                 mFound              = false;
  198.                 mStop               = false;
  199.                 mStopped            = false;
  200.                 mCloseNodeCounter   = 0;
  201.                 mOpenNodeValue      += 2;
  202.                 mCloseNodeValue     += 2;
  203.                 mOpen.Clear();
  204.                 mClose.Clear();
  205.  
  206.                 #if DEBUGON
  207.                 if (mDebugProgress && PathFinderDebug != null)
  208.                     PathFinderDebug(0, 0, start.X, start.Y, PathFinderNodeType.Start, -1, -1);
  209.                 if (mDebugProgress && PathFinderDebug != null)
  210.                     PathFinderDebug(0, 0, end.X, end.Y, PathFinderNodeType.End, -1, -1);
  211.                 #endif
  212.  
  213.                 mLocation                      = (start.Y << mGridYLog2) + start.X;
  214.                 mEndLocation                   = (end.Y << mGridYLog2) + end.X;
  215.                 mCalcGrid[mLocation].G         = 0;
  216.                 mCalcGrid[mLocation].F         = mHEstimate;
  217.                 mCalcGrid[mLocation].PX        = (ushort) start.X;
  218.                 mCalcGrid[mLocation].PY        = (ushort) start.Y;
  219.                 mCalcGrid[mLocation].Status    = mOpenNodeValue;
  220.  
  221.                 mOpen.Push(mLocation);
  222.                 while(mOpen.Count > 0 && !mStop)
  223.                 {
  224.                     mLocation    = mOpen.Pop();
  225.  
  226.                     //Is it in closed list? means this node was already processed
  227.                     if (mCalcGrid[mLocation].Status == mCloseNodeValue)
  228.                         continue;
  229.  
  230.                     mLocationX   = (ushort) (mLocation & mGridXMinus1);
  231.                     mLocationY   = (ushort) (mLocation >> mGridYLog2);
  232.                    
  233.                     #if DEBUGON
  234.                     if (mDebugProgress && PathFinderDebug != null)
  235.                         PathFinderDebug(0, 0, mLocation & mGridXMinus1, mLocation >> mGridYLog2, PathFinderNodeType.Current, -1, -1);
  236.                     #endif
  237.  
  238.                     if (mLocation == mEndLocation)
  239.                     {
  240.                         mCalcGrid[mLocation].Status = mCloseNodeValue;
  241.                         mFound = true;
  242.                         break;
  243.                     }
  244.  
  245.                     if (mCloseNodeCounter > mSearchLimit)
  246.                     {
  247.                         mStopped = true;
  248.                         // Get time here (maybe?)
  249.                         return null;
  250.                     }
  251.  
  252.                     if (mPunishChangeDirection)
  253.                         mHoriz = (mLocationX - mCalcGrid[mLocation].PX);
  254.  
  255.                     //Lets calculate each successors
  256.                     for (int i=0; i<(mDiagonals ? 8 : 4); i++)
  257.                     {
  258.                         mNewLocationX = (ushort) (mLocationX + mDirection[i,0]);
  259.                         mNewLocationY = (ushort) (mLocationY + mDirection[i,1]);
  260.                         mNewLocation  = (mNewLocationY << mGridYLog2) + mNewLocationX;
  261.  
  262.                         if (mNewLocationX >= mGridX || mNewLocationY >= mGridY)
  263.                             continue;
  264.  
  265.                         // Unbreakeable?
  266.                         if (mGrid[mNewLocationX, mNewLocationY] == 0)
  267.                             continue;
  268.  
  269.                         if (mHeavyDiagonals && i>3)
  270.                             mNewG = mCalcGrid[mLocation].G + (int) (mGrid[mNewLocationX, mNewLocationY] * 2.41);
  271.                         else
  272.                             mNewG = mCalcGrid[mLocation].G + mGrid[mNewLocationX, mNewLocationY];
  273.  
  274.                         if (mPunishChangeDirection)
  275.                         {
  276.                             if ((mNewLocationX - mLocationX) != 0)
  277.                             {
  278.                                 if (mHoriz == 0)
  279.                                     mNewG += Math.Abs(mNewLocationX - end.X) + Math.Abs(mNewLocationY - end.Y);
  280.                             }
  281.                             if ((mNewLocationY - mLocationY) != 0)
  282.                             {
  283.                                 if (mHoriz != 0)
  284.                                     mNewG += Math.Abs(mNewLocationX - end.X) + Math.Abs(mNewLocationY - end.Y);
  285.                             }
  286.                         }
  287.  
  288.                         //Is it open or closed?
  289.                         if (mCalcGrid[mNewLocation].Status == mOpenNodeValue || mCalcGrid[mNewLocation].Status == mCloseNodeValue)
  290.                         {
  291.                             // The current node has less code than the previous? then skip this node
  292.                             if (mCalcGrid[mNewLocation].G <= mNewG)
  293.                                 continue;
  294.                         }
  295.  
  296.                         mCalcGrid[mNewLocation].PX      = mLocationX;
  297.                         mCalcGrid[mNewLocation].PY      = mLocationY;
  298.                         mCalcGrid[mNewLocation].G       = mNewG;
  299.  
  300.                         switch(mFormula)
  301.                         {
  302.                             default:
  303.                             case HeuristicFormula.Manhattan:
  304.                                 mH = mHEstimate * (Math.Abs(mNewLocationX - end.X) + Math.Abs(mNewLocationY - end.Y));
  305.                                 break;
  306.                             case HeuristicFormula.MaxDXDY:
  307.                                 mH = mHEstimate * (Math.Max(Math.Abs(mNewLocationX - end.X), Math.Abs(mNewLocationY - end.Y)));
  308.                                 break;
  309.                             case HeuristicFormula.DiagonalShortCut:
  310.                                 int h_diagonal  = Math.Min(Math.Abs(mNewLocationX - end.X), Math.Abs(mNewLocationY - end.Y));
  311.                                 int h_straight  = (Math.Abs(mNewLocationX - end.X) + Math.Abs(mNewLocationY - end.Y));
  312.                                 mH = (mHEstimate * 2) * h_diagonal + mHEstimate * (h_straight - 2 * h_diagonal);
  313.                                 break;
  314.                             case HeuristicFormula.Euclidean:
  315.                                 mH = (int) (mHEstimate * Math.Sqrt(Math.Pow((mNewLocationY - end.X) , 2) + Math.Pow((mNewLocationY - end.Y), 2)));
  316.                                 break;
  317.                             case HeuristicFormula.EuclideanNoSQR:
  318.                                 mH = (int) (mHEstimate * (Math.Pow((mNewLocationX - end.X) , 2) + Math.Pow((mNewLocationY - end.Y), 2)));
  319.                                 break;
  320.                             case HeuristicFormula.Custom1:
  321.                                 Point dxy       = new Point(Math.Abs(end.X - mNewLocationX), Math.Abs(end.Y - mNewLocationY));
  322.                                 int Orthogonal  = Math.Abs(dxy.X - dxy.Y);
  323.                                 int Diagonal    = Math.Abs(((dxy.X + dxy.Y) - Orthogonal) / 2);
  324.                                 mH = mHEstimate * (Diagonal + Orthogonal + dxy.X + dxy.Y);
  325.                                 break;
  326.                         }
  327.                         if (mTieBreaker)
  328.                         {
  329.                             int dx1 = mLocationX - end.X;
  330.                             int dy1 = mLocationY - end.Y;
  331.                             int dx2 = start.X - end.X;
  332.                             int dy2 = start.Y - end.Y;
  333.                             int cross = Math.Abs(dx1 * dy2 - dx2 * dy1);
  334.                             mH = (int) (mH + cross * 0.001);
  335.                         }
  336.                         mCalcGrid[mNewLocation].F = mNewG + mH;
  337.  
  338.                         #if DEBUGON
  339.                         if (mDebugProgress && PathFinderDebug != null)
  340.                             PathFinderDebug(mLocationX, mLocationY, mNewLocationX, mNewLocationY, PathFinderNodeType.Open, mCalcGrid[mNewLocation].F, mCalcGrid[mNewLocation].G);
  341.                         #endif
  342.  
  343.                         //It is faster if we leave the open node in the priority queue
  344.                         //When it is removed, it will be already closed, it will be ignored automatically
  345.                         //if (tmpGrid[newLocation].Status == 1)
  346.                         //{
  347.                         //    //int removeX   = newLocation & gridXMinus1;
  348.                         //    //int removeY   = newLocation >> gridYLog2;
  349.                         //    mOpen.RemoveLocation(newLocation);
  350.                         //}
  351.  
  352.                         //if (tmpGrid[newLocation].Status != 1)
  353.                         //{
  354.                             mOpen.Push(mNewLocation);
  355.                         //}
  356.                         mCalcGrid[mNewLocation].Status = mOpenNodeValue;
  357.                     }
  358.  
  359.                     mCloseNodeCounter++;
  360.                     mCalcGrid[mLocation].Status = mCloseNodeValue;
  361.  
  362.                     #if DEBUGON
  363.                     if (mDebugProgress && PathFinderDebug != null)
  364.                         PathFinderDebug(0, 0, mLocationX, mLocationY, PathFinderNodeType.Close, mCalcGrid[mLocation].F, mCalcGrid[mLocation].G);
  365.                     #endif
  366.                 }
  367.  
  368.                 // Get time here (maybe?)
  369.  
  370.                 if (mFound)
  371.                 {
  372.                     mClose.Clear();
  373.                     int posX = end.X;
  374.                     int posY = end.Y;
  375.  
  376.                     PathFinderNodeFast fNodeTmp = mCalcGrid[(end.Y << mGridYLog2) + end.X];
  377.                     PathFinderNode fNode;
  378.                     fNode.F  = fNodeTmp.F;
  379.                     fNode.G  = fNodeTmp.G;
  380.                     fNode.H  = 0;
  381.                     fNode.PX = fNodeTmp.PX;
  382.                     fNode.PY = fNodeTmp.PY;
  383.                     fNode.X  = end.X;
  384.                     fNode.Y  = end.Y;
  385.  
  386.                     while(fNode.X != fNode.PX || fNode.Y != fNode.PY)
  387.                     {
  388.                         mClose.Add(fNode);
  389.                         #if DEBUGON
  390.                         if (mDebugFoundPath && PathFinderDebug != null)
  391.                             PathFinderDebug(fNode.PX, fNode.PY, fNode.X, fNode.Y, PathFinderNodeType.Path, fNode.F, fNode.G);
  392.                         #endif
  393.                         posX = fNode.PX;
  394.                         posY = fNode.PY;
  395.                         fNodeTmp = mCalcGrid[(posY << mGridYLog2) + posX];
  396.                         fNode.F  = fNodeTmp.F;
  397.                         fNode.G  = fNodeTmp.G;
  398.                         fNode.H  = 0;
  399.                         fNode.PX = fNodeTmp.PX;
  400.                         fNode.PY = fNodeTmp.PY;
  401.                         fNode.X  = posX;
  402.                         fNode.Y  = posY;
  403.                     }
  404.  
  405.                     mClose.Add(fNode);
  406.                     #if DEBUGON
  407.                     if (mDebugFoundPath && PathFinderDebug != null)
  408.                         PathFinderDebug(fNode.PX, fNode.PY, fNode.X, fNode.Y, PathFinderNodeType.Path, fNode.F, fNode.G);
  409.                     #endif
  410.  
  411.                     mStopped = true;
  412.                     return mClose;
  413.                 }
  414.                 mStopped = true;
  415.                 return null;
  416.             }
  417.         }
  418.         #endregion
  419.  
  420.         #region Inner Classes
  421.         internal class ComparePFNodeMatrix : IComparer<int>
  422.         {
  423.             #region Variables Declaration
  424.             PathFinderNodeFast[] mMatrix;
  425.             #endregion
  426.  
  427.             #region Constructors
  428.             public ComparePFNodeMatrix(PathFinderNodeFast[] matrix)
  429.             {
  430.                 mMatrix = matrix;
  431.             }
  432.             #endregion
  433.  
  434.             #region IComparer Members
  435.             public int Compare(int a, int b)
  436.             {
  437.                 if (mMatrix[a].F > mMatrix[b].F)
  438.                     return 1;
  439.                 else if (mMatrix[a].F < mMatrix[b].F)
  440.                     return -1;
  441.                 return 0;
  442.             }
  443.             #endregion
  444.         }
  445.         #endregion
  446.     }
  447. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement