Advertisement
Guest User

A* in C#

a guest
Jul 9th, 2016
118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 4.83 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4.  
  5. public struct Point<T>
  6.  {
  7.     public T x, y;
  8.  
  9.     public Point(T x, T y)
  10.      {
  11.         this.x = x;
  12.         this.y = y;
  13.      }
  14.  }
  15.  
  16. public class AStar : MonoBehaviour
  17.  {
  18.     private class Node
  19.      {
  20.         public enum State
  21.          {
  22.             NONE,
  23.             CLOSED,
  24.             OPEN
  25.          }
  26.  
  27.         public uint x, y;
  28.         public State state;
  29.         public bool walkable;
  30.  
  31.         private Node _parent = null;
  32.         public Node parent
  33.          {
  34.             get { return _parent; }
  35.             set
  36.              {
  37.                 _parent = value;
  38.                 G = value.G + calculateCostGBetween(value.x, value.y, x, y);
  39.              }
  40.          }
  41.  
  42.         public uint G { get; private set; }
  43.         public uint H { get; private set; }
  44.         public uint F { get { return G + H; } }
  45.  
  46.         public Node(uint x, uint y, uint xGoal, uint yGoal, bool walkable)
  47.          {
  48.             // Set the position.
  49.             this.x = x;
  50.             this.y = y;
  51.  
  52.             // Whether walkable.
  53.             this.walkable = walkable;
  54.  
  55.             // Default state.
  56.             this.state = State.NONE;
  57.  
  58.             // Calculate cost to goal heuristic.
  59.             H = calculateCostHBetween(x, y, xGoal, yGoal);
  60.          }
  61.      }
  62.  
  63.     private const uint costStraight = 10u;
  64.     private const uint costDiagonal = 14u;
  65.  
  66.     Node[,] nodes;
  67.     uint xGoal, yGoal;
  68.     Node nodeGoal;
  69.  
  70.     public Point<uint>[] calculatePath(uint xStart, uint yStart, uint xGoal, uint yGoal, int[,] map)
  71.      {
  72.         // If already at the target, do nothing.
  73.         if (xStart == xGoal && yStart == yGoal)
  74.             return null;
  75.  
  76.         // If the points are not walkable, do nothing.
  77.         if (!isWalkable(map[xStart, yStart]) || !isWalkable(map[xGoal, yGoal]))
  78.             return null;
  79.  
  80.         // Store goal before resetting nodes; new nodes need to access it.
  81.         this.xGoal = xGoal;
  82.         this.yGoal = yGoal;
  83.  
  84.         // Clear nodes.
  85.         resetNodes(map);
  86.  
  87.         // Set end node.
  88.         nodeGoal = nodes[xGoal, yGoal];
  89.  
  90.         // Create the first node. It it sure to be walkable by now.
  91.         var nodeCurr = nodes[xStart, yStart];
  92.         nodeCurr.walkable = true;
  93.  
  94.         // Always open first node right away.
  95.         nodeCurr.state = Node.State.OPEN;
  96.  
  97.         // Will store final path.
  98.         var path = new List<Point<uint>>();
  99.  
  100.         // Recurse through and then check for success.
  101.         if (walk(nodeCurr) && nodeGoal != null)
  102.          {
  103.             // Trace backwards from final node.
  104.             Node n = nodeGoal;
  105.             while (n.parent != null)
  106.              {
  107.                 path.Add(new Point<uint>(n.x, n.y));
  108.                 n = n.parent;
  109.              }
  110.          }
  111.  
  112.         // Reverse it to get a forward path.
  113.         path.Reverse();
  114.  
  115.         // Return the result.
  116.         return path.ToArray();
  117.      }
  118.  
  119.     private bool walk(Node node)
  120.      {
  121.         // Close the node to prevent traversing it more than once.
  122.         node.state = Node.State.CLOSED;
  123.  
  124.         // Check adjacent nodes.
  125.         var adjacent = new List<Node>();
  126.         for (uint y = (uint)Mathf.Max(0, (int)node.y - 1); y <= (uint)Mathf.Min(nodes.GetLength(1) - 1, node.y + 1); ++ y)
  127.          {
  128.             for (uint x = (uint)Mathf.Max(0, (int)node.x - 1); x <= (uint)Mathf.Min(nodes.GetLength(0) - 1, node.x + 1); ++ x)
  129.              {
  130.                 var n = nodes[x, y];
  131.  
  132.                 // Avoid the node itself.
  133.                 if (node.x == x && node.y == y)
  134.                     continue;
  135.  
  136.                 // Avoid unwalkable nodes.
  137.                 if (!n.walkable)
  138.                     continue;
  139.  
  140.                 // Avoid closed nodes.
  141.                 if (n.state == Node.State.CLOSED)
  142.                     continue;
  143.  
  144.                 // If the node was already opened, check if this route is faster.
  145.                 if (n.state == Node.State.OPEN)
  146.                  {
  147.                     // Get the new cost.
  148.                     uint cost = node.G + calculateCostGBetween(n.x, n.y, n.parent.x, n.parent.y);
  149.  
  150.                     // Check if cheaper than old cost set by previous parent.
  151.                     if (cost < n.G)
  152.                         // Change parent.
  153.                         n.parent = node;
  154.                  }
  155.                 // Otherwise, open it.
  156.                 else
  157.                  {
  158.                     n.parent = node;
  159.                     n.state  = Node.State.OPEN;
  160.                  }
  161.  
  162.                 // Add it to the list and move on.
  163.                 adjacent.Add(nodes[x, y]);
  164.              }
  165.          }
  166.  
  167.         // If no adjacent nodes were found, we are done.
  168.         if (adjacent.Count == 0)
  169.             return false;
  170.  
  171.         // Otherwise, find the cheapest one. Start by sorting for cost.
  172.         adjacent.Sort((a, b) => a.F.CompareTo(b.F));
  173.         foreach (var n in adjacent)
  174.             // End node or another successful walk returns true.
  175.             if ((n.x == xGoal && n.y == yGoal) || walk(n))
  176.                 return true;
  177.  
  178.         // Dead end.
  179.         return false;
  180.      }
  181.  
  182.     private void resetNodes(int[,] map)
  183.      {
  184.         uint w = (uint)map.GetLength(0), h = (uint)map.GetLength(1);
  185.  
  186.         nodes = null;
  187.         nodes = new Node[w, h];
  188.  
  189.         nodeGoal = null;
  190.  
  191.         for (uint y = 0; y < h; ++ y)
  192.             for (uint x = 0; x < w; ++ x)
  193.                 nodes[x, y] = new Node(x, y, xGoal, yGoal, isWalkable(map[x, y]));
  194.      }
  195.  
  196.     private static uint calculateCostHBetween(uint x1, uint y1, uint x2, uint y2)
  197.      {
  198.         return (uint)(Mathf.Max(x1, x2) - Mathf.Min(x1, x2) + Mathf.Max(y1, y2) - Mathf.Min(y1, y2)) * costStraight;
  199.      }
  200.  
  201.     private static uint calculateCostGBetween(uint x1, uint y1, uint x2, uint y2)
  202.      {
  203.         return (x1 != x2 && y1 != y2) ? costDiagonal : costStraight;
  204.      }
  205.  
  206.     private bool isWalkable(int valueInMap)
  207.      {
  208.         return (valueInMap == 0);
  209.      }
  210.  }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement