Advertisement
Guest User

Random shortest path A*

a guest
Mar 14th, 2018
102
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 6.05 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. class Pathfinding
  7. {
  8.     enum CellState
  9.     {
  10.         Unfound = 0,
  11.         Discovered = 1
  12.     }
  13.  
  14.     struct FrontierElement : IComparable<FrontierElement>
  15.     {
  16.         public HexXY coords;
  17.         public float cost;
  18.         public int youth; //we must expand oldest node first for path randomization, though it is slower
  19.  
  20.         public FrontierElement(HexXY coords, float cost, int youth)
  21.         {
  22.             this.coords = coords;
  23.             this.cost = cost;
  24.             this.youth = youth;
  25.         }
  26.  
  27.         public int CompareTo(FrontierElement other)
  28.         {
  29.             int costComparison = cost.CompareTo(other.cost);
  30.             if (costComparison != 0) return costComparison;
  31.             else return youth.CompareTo(other.youth);
  32.         }
  33.     }
  34.  
  35.     System.Random rng;
  36.  
  37.     int size;
  38.     CellState[] cellStates;
  39.     float[] pathCosts;    
  40.     int[] moveDirs; //Every element of this is a bit mask of possible directions for shortest path randomization
  41.     BinaryHeap<FrontierElement> frontier;
  42.  
  43.     public Pathfinding(int mapSize, int rngSeed)
  44.     {        
  45.         this.size = mapSize;
  46.         this.rng = new System.Random(rngSeed);
  47.         this.cellStates = new CellState[size * size];
  48.         this.moveDirs = new int[size * size];
  49.         this.pathCosts = new float[size * size];
  50.         this.frontier = new BinaryHeap<FrontierElement>(10);
  51.     }
  52.  
  53.     void Clear()
  54.     {
  55.         Array.Clear(cellStates, 0, cellStates.Length);
  56.         Array.Clear(moveDirs, 0, moveDirs.Length);
  57.         Array.Clear(pathCosts, 0, pathCosts.Length);
  58.         frontier.Reset();
  59.     }
  60.  
  61.     void AddDir(int x, int y, int dir)
  62.     {
  63.         moveDirs[y * size + x] |= 1 << dir;
  64.     }
  65.  
  66.     void GetPossibleDirs(int x, int y, List<int> dirs)
  67.     {
  68.         dirs.Clear();
  69.         for (int i = 0; i < 6; i++)        
  70.             if ((moveDirs[y * size + x] & (1 << i)) != 0)
  71.                 dirs.Add(i);        
  72.     }
  73.  
  74.     void UnfoldPath(HexXY start, HexXY finish, List<HexXY> path, bool randomize)
  75.     {
  76.         path.Clear();
  77.         if (start == finish)
  78.             return;            
  79.  
  80.         HexXY c = finish;
  81.         List<int> possibleDirs = new List<int>(5);
  82.  
  83.         do
  84.         {
  85.             path.Add(c);
  86.             GetPossibleDirs(c.x, c.y, possibleDirs);            
  87.             int dir;
  88.             if (randomize)
  89.                 dir = possibleDirs[rng.Next(possibleDirs.Count)];
  90.             else
  91.                 dir = possibleDirs[0];
  92.             c -= HexXY.neighbours[dir];
  93.         } while (c != start);
  94.         path.Add(start);
  95.         path.Reverse();
  96.     }
  97.  
  98.     public static float StandardHeuristic(HexXY goal, HexXY c)
  99.     {
  100.         return HexXY.Dist(goal, c);
  101.     }
  102.  
  103.     public static bool StandardGoal(HexXY goal, HexXY c, int dist = 1)
  104.     {
  105.         return HexXY.Dist(goal, c) <= dist;
  106.     }
  107.  
  108.     bool IsInsideMap(HexXY c)
  109.     {
  110.         return c.x >= 0 && c.x < size && c.y >= 0 && c.y < size;
  111.     }
  112.  
  113.     public bool FindPath(HexXY start,
  114.                          Func<HexXY, float> funcDistToGoalHeuristic,
  115.                          Func<HexXY, bool> predicateGoalReached,
  116.                          Func<HexXY, float> funcCost,
  117.                          float maxCost,
  118.                          bool randomize,
  119.                          List<HexXY> path)
  120.     {
  121.         Clear();
  122.  
  123.         frontier.Enqueue(new FrontierElement(start, funcDistToGoalHeuristic(start), 0));
  124.         cellStates[start.y * size + start.x] = CellState.Discovered;
  125.        
  126.         int expandedNodes = 0;
  127.  
  128.         do
  129.         {
  130.             FrontierElement el = frontier.Dequeue();
  131.             HexXY c = el.coords;
  132.             //Debug.Log("Expanding " + c.ToString() + ", cost + heuristic " + el.cost.ToString());
  133.             if (predicateGoalReached(c))
  134.             {
  135.                 UnfoldPath(start, c, path, randomize);
  136.                 return true;
  137.             }
  138.  
  139.             float currentCost = pathCosts[c.y * size + c.x];
  140.             if (currentCost <= maxCost)
  141.             {
  142.                 for (int i = 0; i < 6; i++)
  143.                 {
  144.                     HexXY n = c + HexXY.neighbours[i];
  145.                     if (!IsInsideMap(n))
  146.                         continue;
  147.  
  148.                     float cost = currentCost + funcCost(n);
  149.                     if (cellStates[n.y * size + n.x] == CellState.Discovered)
  150.                     {
  151.                         //If we can go to this discovered cell with the same cost,
  152.                         //why not use this additional direction for path randomization?
  153.                         //We'll get all shortest paths basically for free
  154.                         //(all possible shortest paths in fact, if we consider that A* with admissable heuristic
  155.                         //iteratively improves lower bound on path length)
  156.                         //P.S. actually we need a particular order of node expanding (expand oldest first) for
  157.                         //this to work
  158.  
  159.                         //DEBUG
  160.                         //Debug.Log("Another dir ---------------");
  161.                         //Debug.Log(n);
  162.                         //Debug.Log(cost);
  163.                         //Debug.Log(pathCosts[n.y * size + n.x]);
  164.  
  165.                         if (pathCosts[n.y * size + n.x] == cost)
  166.                             AddDir(n.x, n.y, i);
  167.                         continue;
  168.                     }
  169.  
  170.                     float h = funcDistToGoalHeuristic(n);
  171.                     float value = cost + h;
  172.  
  173.                     frontier.Enqueue(new FrontierElement(n, value, expandedNodes));
  174.                     AddDir(n.x, n.y, i);
  175.                     cellStates[n.y * size + n.x] = CellState.Discovered;
  176.                     pathCosts[n.y * size + n.x] = cost;
  177.  
  178.                     //Debug.Log("Adding " + n.ToString() + ", cost " + cost);
  179.                 }
  180.             }
  181.            
  182.             expandedNodes++;            
  183.         } while (frontier.Count > 0);
  184.        
  185.         return false;
  186.     }
  187. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement