// The actual maze generation is adaptible to any topology private static void MakeMaze(GenerationCell start) { GenerationCell current = start, exit = start, next; // To find the farthest exit, we keep track of “tree” depth // Potential improvement: Store this information in the cell itself, for fancy stuff later on ulong depth = 0; ulong max = 0; while (true) // This ain't Lisp, I ain't recursin' { current.Visited = true; bool natural = true; if (Randomize) { Shuffle <Cell>(current.Links); // This is a destructive function. May want to rewrite to use a temporary clone list for iteration } for (int i = 0; i < current.Links.Count; i++) { next = (GenerationCell)current.Links[i]; // We cast for access to .Previous, which regular cells do not have // Note: This incurs no extra penalty so using it in the main body of a loop is A-OK if (next.Previous == current) // We obviously don't want to mess with a link if we've already gone that way { continue; } if (next.Visited) // It was already visited via some other route, so this is not what we're looking for { current.Links.RemoveAt(i--); continue; } // It's an unexplored link if (++depth > max) // Since we're exploring, and want to keep track of the highest later on { exit = next; max = depth; } next.Previous = current; current = next; natural = false; // Ugly helper variable since we can't continue out of multiple loops that easily break; } if (!natural) // The loop didn't terminate naturally, so we found a new node and are considering from there { continue; } // We explored all links and found none - dead end cell if (current.Previous == null) // The starting cell has no previous cell { // The function can safely end here, since if we've backtraced all the way to the // starting cell and found no more link, the entire maze has been explored. exit.ExitCell = true; return; } current = current.Previous; depth--; } // The endless loop repeats here }
// Generates an array of cells, and a maze to go with them public static Cell[,] GenerateOrthogonal(int Width, int Height, int XEntrance, int YEntrance, bool Cyclic) { if (XEntrance >= Width || YEntrance >= Height) { throw new ArgumentOutOfRangeException(string.Format("Starting coordinates out of range. {0},{1},{2},{3}", Width, Height, XEntrance, YEntrance)); } GenerationCell[,] maze = new GenerationCell[Width, Height]; for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { maze[x, y] = new GenerationCell(); // initialize with non-connected cells } } // Generate links to adjacent cells, since this is an orthogonal maze // To-do: Maybe encapsulate the bounds-checking and link generation process inside a small function, and call it on a list of locations instead? // Not going to bother right now since it won't significantly reduce SLOC count, but for non-2D mazes this would be nice for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { if (x > 0) { maze[x, y].Links.Add(maze[x - 1, y]); } else if (Cyclic) { maze[x, y].Links.Add(maze[Width - 1, y]); } if (y > 0) { maze[x, y].Links.Add(maze[x, y - 1]); } else if (Cyclic) { maze[x, y].Links.Add(maze[x, Height - 1]); } if (x + 1 < Width) { maze[x, y].Links.Add(maze[x + 1, y]); } else if (Cyclic) { maze[x, y].Links.Add(maze[0, y]); } if (y + 1 < Height) { maze[x, y].Links.Add(maze[x, y + 1]); } else if (Cyclic) { maze[x, y].Links.Add(maze[x, 0]); } } } MakeMaze(maze[XEntrance, YEntrance]); return(maze); }