EuclideanEmbedding.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. #pragma warning disable 414
  2. using System.Collections.Generic;
  3. namespace PF {
  4. public enum HeuristicOptimizationMode {
  5. None,
  6. Random,
  7. RandomSpreadOut,
  8. }
  9. /** Implements heuristic optimizations.
  10. *
  11. * \see heuristic-opt
  12. * \see Game AI Pro - Pathfinding Architecture Optimizations by Steve Rabin and Nathan R. Sturtevant
  13. *
  14. * \astarpro
  15. */
  16. [System.Serializable]
  17. public class EuclideanEmbedding {
  18. public HeuristicOptimizationMode mode;
  19. public int seed;
  20. public int spreadOutCount = 1;
  21. [System.NonSerialized]
  22. public bool dirty;
  23. /**
  24. * Costs laid out as n*[int],n*[int],n*[int] where n is the number of pivot points.
  25. * Each node has n integers which is the cost from that node to the pivot node.
  26. * They are at around the same place in the array for simplicity and for cache locality.
  27. *
  28. * cost(nodeIndex, pivotIndex) = costs[nodeIndex*pivotCount+pivotIndex]
  29. */
  30. uint[] costs = new uint[8];
  31. int maxNodeIndex;
  32. int pivotCount;
  33. public GraphNode[] pivots;
  34. /*
  35. * Seed for random number generator.
  36. * Must not be zero
  37. */
  38. const uint ra = 12820163;
  39. /*
  40. * Seed for random number generator.
  41. * Must not be zero
  42. */
  43. const uint rc = 1140671485;
  44. /*
  45. * Parameter for random number generator.
  46. */
  47. uint rval;
  48. System.Object lockObj = new object ();
  49. /** Simple linear congruential generator.
  50. * \see http://en.wikipedia.org/wiki/Linear_congruential_generator
  51. */
  52. uint GetRandom () {
  53. rval = (ra*rval + rc);
  54. return rval;
  55. }
  56. void EnsureCapacity (int index) {
  57. if (index > maxNodeIndex) {
  58. lock (lockObj) {
  59. if (index > maxNodeIndex) {
  60. if (index >= costs.Length) {
  61. var newCosts = new uint[System.Math.Max(index*2, pivots.Length*2)];
  62. for (int i = 0; i < costs.Length; i++) newCosts[i] = costs[i];
  63. costs = newCosts;
  64. }
  65. maxNodeIndex = index;
  66. }
  67. }
  68. }
  69. }
  70. public uint GetHeuristic (int nodeIndex1, int nodeIndex2) {
  71. nodeIndex1 *= pivotCount;
  72. nodeIndex2 *= pivotCount;
  73. if (nodeIndex1 >= costs.Length || nodeIndex2 >= costs.Length) {
  74. EnsureCapacity(nodeIndex1 > nodeIndex2 ? nodeIndex1 : nodeIndex2);
  75. }
  76. uint mx = 0;
  77. for (int i = 0; i < pivotCount; i++) {
  78. uint d = (uint)System.Math.Abs((int)costs[nodeIndex1+i] - (int)costs[nodeIndex2+i]);
  79. if (d > mx) mx = d;
  80. }
  81. return mx;
  82. }
  83. /** Pick N random walkable nodes from all nodes in all graphs and add them to the buffer.
  84. *
  85. * Here we select N random nodes from a stream of nodes.
  86. * Probability of choosing the first N nodes is 1
  87. * Probability of choosing node i is min(N/i,1)
  88. * A selected node will replace a random node of the previously
  89. * selected ones.
  90. *
  91. * \see https://en.wikipedia.org/wiki/Reservoir_sampling
  92. */
  93. void PickNRandomNodes (int count, List<GraphNode> buffer) {
  94. int n = 0;
  95. var graphs = PathFindHelper.GetConfig().graphs;
  96. // Loop through all graphs
  97. for (int j = 0; j < graphs.Length; j++) {
  98. // Loop through all nodes in the graph
  99. graphs[j].GetNodes(node => {
  100. if (!node.Destroyed && node.Walkable) {
  101. n++;
  102. if ((GetRandom() % n) < count) {
  103. if (buffer.Count < count) {
  104. buffer.Add(node);
  105. } else {
  106. buffer[(int)(GetRandom()%buffer.Count)] = node;
  107. }
  108. }
  109. }
  110. });
  111. }
  112. }
  113. GraphNode PickAnyWalkableNode () {
  114. var graphs = PathFindHelper.GetConfig().graphs;
  115. GraphNode first = null;
  116. // Find any node in the graphs
  117. for (int j = 0; j < graphs.Length; j++) {
  118. graphs[j].GetNodes(node => {
  119. if (node != null && node.Walkable && first == null) {
  120. first = node;
  121. }
  122. });
  123. }
  124. return first;
  125. }
  126. public void RecalculatePivots () {
  127. if (mode == HeuristicOptimizationMode.None) {
  128. pivotCount = 0;
  129. pivots = null;
  130. return;
  131. }
  132. // Reset the random number generator
  133. rval = (uint)seed;
  134. // Get a List<GraphNode> from a pool
  135. var pivotList = ListPool<GraphNode>.Claim();
  136. switch (mode) {
  137. case HeuristicOptimizationMode.Random:
  138. PickNRandomNodes(spreadOutCount, pivotList);
  139. break;
  140. case HeuristicOptimizationMode.RandomSpreadOut:
  141. // If no pivot points were found, fall back to picking arbitrary nodes
  142. if (pivotList.Count == 0) {
  143. GraphNode first = PickAnyWalkableNode();
  144. if (first != null) {
  145. pivotList.Add(first);
  146. } else {
  147. #if !SERVER
  148. UnityEngine.Debug.LogError("Could not find any walkable node in any of the graphs.");
  149. #endif
  150. ListPool<GraphNode>.Release(ref pivotList);
  151. return;
  152. }
  153. }
  154. // Fill remaining slots with null
  155. int toFill = spreadOutCount - pivotList.Count;
  156. for (int i = 0; i < toFill; i++) pivotList.Add(null);
  157. break;
  158. default:
  159. throw new System.Exception("Invalid HeuristicOptimizationMode: " + mode);
  160. }
  161. pivots = pivotList.ToArray();
  162. ListPool<GraphNode>.Release(ref pivotList);
  163. }
  164. /** Special case necessary for paths to unwalkable nodes right next to walkable nodes to be able to use good heuristics.
  165. *
  166. * This will find all unwalkable nodes in all grid graphs with walkable nodes as neighbours
  167. * and set the cost to reach them from each of the pivots as the minimum of the cost to
  168. * reach the neighbours of each node.
  169. *
  170. * \see ABPath.EndPointGridGraphSpecialCase
  171. */
  172. void ApplyGridGraphEndpointSpecialCase () {
  173. }
  174. }
  175. }