/// <summary>
 /// Generate a random maze using the given width and height cell count and the starting position cell and ignored rectangles.
 /// </summary>
 /// <param name="width">The maze width in cells.</param>
 /// <param name="height">The maze height in cells.</param>
 /// <param name="startx">X coordinate of first cell. -1 for random.</param>
 /// <param name="starty">Y coordinate of first cell. -1 for random.</param>  
 ///       
 /// <returns>Returns a wall matrix of dimensions 2*(width+1)*(height+1) where layer 0 represents horizontal walls and layer 1 represents vertical walls.
 ///          0xFF means no wall and 0x0 means a regular wall.
 ///          The extra unused row/column of vertical/horizontal walls is left 0.</returns>
 public static byte[,,] GenerateMaze(short width, short height, ref Node pathroot, Rectangle[] ignore = null, short startx = -1, short starty = -1)
 {
     ran = new Random();
     ReRandomize:
     if (startx < 0) startx = (short)ran.Next(0, width);
     if (starty < 0) starty = (short)ran.Next(0, height);
     if (Array.FindIndex(ignore, r => r.Contains(startx, starty)) > -1)
     {
         startx = starty = -1;
         goto ReRandomize;
     }
     walls = new byte[2, width + 1 /*for extra vertical wall column*/, height + 1 /*for extra horizontal wall row*/];
     visited = new bool[width, height];
     DFSDestroyWS(startx, starty, ref pathroot, ignore);
     return walls;
 }
 private static void DFSDestroyWS(short x, short z, ref Node pathroot, Rectangle[] ignore = null)
 {
     Stack<Node[]> stack = new Stack<Node[]>();
     stack.Push(new Node[] { pathroot = new Node(new Vector3(x, 0, z)), null });
     while (stack.Count > 0)
     {
         Node[] data = stack.Pop();
         int validwallcodes = SuperFactory.GetFactory<Wall>().AvailableTypes-1;
         x = (short)data[0].Value.X; z = (short)data[0].Value.Z;
         if (visited[x, z])
             continue;
         visited[x, z] = true;
         if (data[1] != null) // For first time
         {
             data[0].Neighbours.Add(data[1]);
             data[1].Neighbours.Add(data[0]);
             if (x == data[1].Value.X) // Vertically aligned cells
             {
                 walls[0, x, Math.Max(z, (short)data[1].Value.Z)] = 0xFF; // Remove horizontal wall between them
                 walls[1, x, z] = (byte)ran.Next(0, validwallcodes); // Randomize left wall type
             }
             else if (z == data[1].Value.Z) // Horizontally aligend cells
             {
                 walls[1, Math.Max(x, (short)data[1].Value.X), z] = 0xFF; // Remove vertical wall between them
                 walls[0, x, z] = (byte)ran.Next(0, validwallcodes); // Randomize top wall type
             }
         }
         short startdir = (short)ran.Next(0, 4);
         short sign = (short)(ran.Next(99) > 49 ? 1 : -1);
         for (short i = 0; i < 4; i++)
         {
             // +4 to compensate for counter clock wise rotation. %4 to stay in range
             short dir = (short)((4 + startdir + sign * i) % 4);
             short tx = (short)(x + (dir == 1 ? 1 : dir == 3 ? -1 : 0));
             short tz = (short)(z + (dir == 2 ? 1 : dir == 0 ? -1 : 0));
             // More overhead checking here means less memory usage
             if (tx >= 0 && tz >= 0 && tx < visited.GetLength(0) && tz < visited.GetLength(1) // Check whether target is inside workarea
                 && !visited[tx, tz] // Check if already visited
                 && (ignore == null || Array.TrueForAll(ignore, r => !r.Contains(tx, tz)))) // Finally, make sure it's not in an ignored area
                 stack.Push(new Node[] { new Node(new Vector3(tx, 0, tz)), data[0] });
         }
     }
 }