Advertisement
teleias

Dijkstra's Algorithm

Oct 21st, 2013
214
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.55 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4.  
  5. public class Grid : MonoBehaviour {
  6.  
  7.     public Transform prefab;
  8.     public int sx = 0;
  9.     public int sz = 0;
  10.     void Start () {
  11.         arr = new Node[sx,sz];
  12.         SetUpDirections();
  13.         SetUpGrid(sx,sz);
  14.        
  15.         BeginCalculation(new V2(0, 0));
  16.         Display();
  17.     }
  18.    
  19.     /// <summary>
  20.     /// Query for Mouse events.
  21.     ///  When event is found, trace to the node clicked
  22.     ///  and begin Dijkstra's algorithm from that node.
  23.     /// </summary>
  24.     public void Update()
  25.     {
  26.         //On button press.
  27.         if(Input.GetMouseButtonDown(0))
  28.         {
  29.             //Raycast a ray through the camera.
  30.             Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  31.             RaycastHit hitInfo;
  32.             if(Physics.Raycast(ray, out hitInfo, 100, 1<<8))
  33.             {
  34.                 //If it hits, search through all the nodes until we find which node it is.
  35.                 // This is a somewhat inefficient method, but we aren't assigning any special code to each
  36.                 // GameObject node, so this works fine.
  37.                 Node n = null;
  38.                 foreach(Node arrN in arr)
  39.                 {
  40.                     if(arrN.cell == hitInfo.transform)
  41.                     {
  42.                         n = arrN;
  43.                         break; 
  44.                     }
  45.                 }
  46.                 if(n == null){return;}
  47.                 //Once we figure out which node, we'll begin the calculation from that node.
  48.                 BeginCalculation(n.v2);
  49.             }
  50.         }
  51.     }
  52.    
  53.    
  54.     /// <summary>
  55.     /// Sets up Vector2 directions.
  56.     /// </summary>
  57.     void SetUpDirections()
  58.     {
  59.         directions.Add(new V2(-1,  0));
  60.         directions.Add(new V2( 0, -1));
  61.         directions.Add(new V2( 0,  1));
  62.         directions.Add(new V2( 1,  0));
  63.     }
  64.    
  65.    
  66.     public Node[,] arr;
  67.     /// <summary>
  68.     /// Sets up grid.
  69.     /// </summary>
  70.     /// <param name='sx'>
  71.     /// Max size of X-Dimension (Width).
  72.     /// </param>
  73.     /// <param name='sz'>
  74.     /// Max size of Z-dimension (Length).
  75.     /// </param>
  76.     void SetUpGrid(int sx, int sz)
  77.     {
  78.         //Double For Loop
  79.         for(int ix = 0; ix < sx; ix++)
  80.         {
  81.             for(int iz = 0; iz < sz; iz++)
  82.             {
  83.                 //Instantiate a new prefab.
  84.                 Transform t;
  85.                 t = (Instantiate(prefab, new Vector3(ix, 0, iz)-new Vector3(sx,0,sz)/2f, Quaternion.identity) as Transform);
  86.                 //Rename it.
  87.                 t.name = ix+" "+iz;
  88.                
  89.                 //Add node info that links to the newly created gameObject.
  90.                 Node newNode = new Node();
  91.                 newNode.v2 = new V2(ix,iz);
  92.                 newNode.distance = int.MaxValue;
  93.                 newNode.cell = t;
  94.                 newNode.cell.transform.parent = transform;
  95.                 arr[ix,iz] = newNode;
  96.                 StartCoroutine(UpdateCell(newNode));
  97.                
  98.                 //Assign a weight to the node and make it visually move up correspondingly.
  99.                 newNode.weight = Random.Range(0, 10);
  100.                 newNode.cell.Translate(Vector3.up*newNode.weight/50f);
  101.             }
  102.         }
  103.     }
  104.     public bool finished = true;
  105.     /// <summary>
  106.     /// Sets up information so Dijkstra's can begin.
  107.     /// </summary>
  108.     /// <param name='v2'>
  109.     /// A Vector2. The place to begin Dijkstra's from.
  110.     /// </param>
  111.     public void BeginCalculation(V2 v2)
  112.     {
  113.         //This check ensures that we don't allow the algorithm
  114.         // to be running more than once at the same time.
  115.         if(!finished){return;}
  116.         finished = false;
  117.         //Reset all the values in the array.
  118.         // Their distances all are at the max value, so any value
  119.         // Dijkstra's finds will replace the current info.
  120.         foreach(Node n in arr)
  121.         {
  122.             n.distance = int.MaxValue; 
  123.             UpdateCell(n);
  124.         }
  125.         //Don't worry about this line of code.
  126.         // If we don't include it, the initial node won't be updated.
  127.         queue.Enqueue(arr[v2.x, v2.z]);
  128.         //Begin Dijkstra's call.
  129.         StartCoroutine(Calculate(v2, 0));  
  130.     }
  131.     public Queue<Node> queue = new Queue<Node>();
  132.     /// <summary>
  133.     /// The entire Dijkstra's algorithm is here.
  134.     /// Note that this is MY implementation, and it's quite different
  135.     ///  than what you might see elsewhere.
  136.     /// I staggered a lot of the code so that you can visually see
  137.     ///  the graph grow and expand as new values are found.
  138.     /// </summary>
  139.     /// <param name='v2'>
  140.     /// The Vector2 coordinate of the node we're looking at.
  141.     /// </param>
  142.     /// <param name='d'>
  143.     /// The distance to the Parent node.
  144.     /// </param>
  145.     public IEnumerator Calculate(V2 v2, int d)
  146.     {
  147.         //We're using a Queue based system.
  148.         Queue<Node> newQueue = new Queue<Node>();
  149.         //The initial point has distance of 0 from itself, by definition.
  150.         arr[v2.x,v2.z].distance = 0;
  151.         //Update the cell so that it shows that it has 0 distance.
  152.         UpdateCell(arr[v2.x,v2.z]);
  153.         //We enqueue the initial point.
  154.         newQueue.Enqueue(arr[v2.x,v2.z]);
  155.         //Here's the loop.
  156.         //Hard to understand but bear with me.
  157.         //This first loop checks if the queue is empty.
  158.         // If it is not empty, then it is populated with nodes to look at.
  159.         // At the very start, it is populated with only our initial node.
  160.         // We later dequeue that initial node after looking at it,
  161.         //  and enqueue the neighbors around the initial node that we should
  162.         //  be looking at in the next iteration.
  163.         while(newQueue.Count != 0)
  164.         {
  165.             //Now here's the second loop.
  166.             //Given the above, we KNOW there are nodes in here to look at.
  167.             //These nodes neighbor nodes that we just opened last iteration.
  168.             //So, we opened the initial node at the start, and we add
  169.             // all of its neighbors into this newQueue.
  170.             //We now check everything in newQueue, one by one.
  171.             //But before we do that, we're going to put everything in newQueue
  172.             // (nodes we that neighbor nodes we had in the last iteration)
  173.             // into queue, which is filled with nodes that we're going to examine,
  174.             // and update if possible.
  175.             do
  176.             {
  177.                 //Dequeue the first node from the newQueue.
  178.                 Node n = newQueue.Dequeue();
  179.                 //Print out the name so we can see it in Editor.
  180.                 Debug.Log(n.cell.name+" : ");
  181.                 //For every node adjacent to the node we're looking at.
  182.                 foreach(V2 adj in getAdjacents(n.v2))
  183.                 {
  184.                     //There are up to 4 adj nodes.
  185.                     // We're going to call them [adj], but I'll refer to them as "THAT node".
  186.                     // And the original one as "THIS node".
  187.                     //If THAT node's [distance] variable is greater than
  188.                     // THIS node's [distance] + THAT node's [weight]
  189.                     // *Note that weight is the cost it takes to travel TO that space,
  190.                     //  from a space adjacent. This weight is usually 1, but we set it
  191.                     //  randomly here*
  192.                     // Then, we update THAT node's [distance] with THIS's [distance] + THAT's [weight].
  193.                     // Basically, we found a more efficient path to get here, so we update the path.
  194.                     if(arr[adj.x,adj.z].distance > n.distance+arr[adj.x,adj.z].weight)
  195.                     {
  196.                         //Here's the path update.
  197.                         arr[adj.x,adj.z].distance = n.distance+arr[adj.x,adj.z].weight;
  198.                         //Since it qualifies, we add this adj node back into the queue.
  199.                         // It won't be looked at until we finish the big do{}while loop.
  200.                         // So it's basically adding in info for the NEXT round.
  201.                         queue.Enqueue(arr[adj.x,adj.z]);
  202.                     }
  203.                 }
  204.                 //Continue to do this until the newQueue has no nodes in it.
  205.             }while(newQueue.Count != 0);
  206.            
  207.             //Now, did we add any of the adj's from the newQueue to the queue?
  208.             // (We put it into queue when we found a more efficient path to it)
  209.             // If we didn't, then we exit.
  210.             // If we did, then we dequeue each item from queue, and ..
  211.             //  1. Update its visual.
  212.             //  2. Put it into newQueue to use when we loop again.
  213.             while(queue.Count != 0)
  214.             {
  215.                 Node n = queue.Dequeue();
  216.                 StartCoroutine(UpdateCell(n));
  217.                 newQueue.Enqueue(n);
  218.             }
  219.             yield return null; 
  220.         }
  221.         finished = true;
  222.     }
  223.     public IEnumerator UpdateCell(Node n)
  224.     {
  225.         //Updates the cell/node's text to show the [distance] variable.
  226.         string s = n.distance+"";
  227.         if(n.distance/100f >= 1)
  228.             s = "";
  229.         n.cell.GetComponentInChildren<TextMesh>().text = s;
  230.         n.cell.renderer.material.color = Color.Lerp(Color.green, Color.black, (float)n.distance/(Mathf.Max(sx,sz)*4));
  231.         yield return new WaitForEndOfFrame();
  232.     }
  233.     List<V2> directions = new List<V2>();
  234.     public List<V2> getAdjacents(V2 v2)
  235.     {
  236.         //Gets all nodes around the node with position [v2].
  237.         List<V2> result = new List<V2>();
  238.         foreach(V2 dir in directions)
  239.         {
  240.             int x = v2.x+dir.x;
  241.             int z = v2.z+dir.z;
  242.             //If it's one of these, it's invalid. Like position (-1,0) is not on the grid.
  243.             if( x < 0   ||
  244.                 x>=sx   ||
  245.                 z < 0   ||
  246.                 z >=sz)
  247.             {
  248.                
  249.             }
  250.             else
  251.             {
  252.                 result.Add(new V2(x,z));   
  253.             }
  254.         }
  255.         return result;
  256.     }
  257.     public void Display()
  258.     {
  259.         for(int ix = 0; ix < sx; ix++)
  260.         {
  261.             for(int iz = 0; iz < sz; iz++)
  262.             {
  263.                 UpdateCell(arr[ix,iz]);
  264.             }
  265.         }
  266.     }
  267. }
  268.  
  269. public class V2{ public int x; public int z; public V2(int _x, int _z){ x = _x; z = _z; } }
  270. public class Node{ public V2 v2; public int distance = 0; public Transform cell; public int weight = 1;}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement