/// <summary> /// Solves the specified maze. /// </summary> /// <param name="maze">The maze.</param> /// <param name="start">The start.</param> /// <param name="end">The end.</param> /// <returns>The solution.</returns> public IEnumerable<Cell> Solve(bool[,] maze, Cell start, Cell end) { var stack = new Stack<Cell>(); stack.Push(start); var visited = new bool[maze.GetLength(0), maze.GetLength(1)]; var previousCell = new Cell[maze.GetLength(0), maze.GetLength(1)]; Func<Cell, bool> hasRightWall = cell => maze[cell.I, cell.J + 1]; Func<Cell, bool> hasLeftWall = cell => maze[cell.I, cell.J - 1]; Func<Cell, bool> hasTopWall = cell => maze[cell.I - 1, cell.J]; Func<Cell, bool> hasBottomWall = cell => maze[cell.I + 1, cell.J]; while (stack.Count > 0) { var temp = stack.Pop(); if (temp.Equals(end)) { var foundPath = new List<Cell>(); while (true) { foundPath.Add(temp); if (temp.Equals(start)) { break; } temp = previousCell[temp.I, temp.J]; } foundPath.Reverse(); return foundPath; } visited[temp.I, temp.J] = true; if (!hasLeftWall(temp) && !visited[temp.I, temp.J - 2]) { stack.Push(new Cell(temp.I, temp.J - 2)); previousCell[temp.I, temp.J - 2] = temp; } if (!hasRightWall(temp) && !visited[temp.I, temp.J + 2]) { stack.Push(new Cell(temp.I, temp.J + 2)); previousCell[temp.I, temp.J + 2] = temp; } if (!hasTopWall(temp) && !visited[temp.I - 2, temp.J]) { stack.Push(new Cell(temp.I - 2, temp.J)); previousCell[temp.I - 2, temp.J] = temp; } if (!hasBottomWall(temp) && !visited[temp.I + 2, temp.J]) { stack.Push(new Cell(temp.I + 2, temp.J)); previousCell[temp.I + 2, temp.J] = temp; } } // no solution found return null; }
/// <summary> /// Get the position for the specified cell. /// </summary> /// <param name="cell"> /// The cell. /// </param> /// <param name="z"> /// The z. /// </param> /// <returns> /// The position. /// </returns> private Point3D GetPosition(Cell cell, double z) { return this.GetPosition(cell.I, cell.J, z); }
/// <summary> /// Generates the maze. /// </summary> /// <returns>A maze.</returns> public bool[,] Generate() { int width = this.Width; int height = this.Height; if (width % 2 == 0) { throw new InvalidOperationException("Width must be an odd number"); } if (height % 2 == 0) { throw new InvalidOperationException("Height must be an odd number"); } bool[,] maze = new bool[height, width]; // Initialize maze (all walls intact) for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { maze[i, j] = i % 2 == 0 || j % 2 == 0; } } var stack = new Stack<Cell>(); var current = new Cell(1, 1); stack.Push(current); Func<int, int, bool> isVisited = (i, j) => maze[i - 1, j] && maze[i + 1, j] && maze[i, j - 1] && maze[i, j + 1]; var random = new Random(this.Seed); while (stack.Count > 0) { var neighbours = new List<Cell>(); int i = current.I; int j = current.J; // Find unvisited neighbours if (i > 1 && isVisited(i - 2, j)) { neighbours.Add(new Cell(i - 2, j)); } if (j > 1 && isVisited(i, j - 2)) { neighbours.Add(new Cell(i, j - 2)); } if (i + 2 < height && isVisited(i + 2, j)) { neighbours.Add(new Cell(i + 2, j)); } if (j + 2 < width && isVisited(i, j + 2)) { neighbours.Add(new Cell(i, j + 2)); } if (neighbours.Count > 0) { var next = neighbours[random.Next(neighbours.Count)]; // break the wall between current and next cell int ii = (next.I + current.I) / 2; int jj = (next.J + current.J) / 2; maze[ii, jj] = false; stack.Push(current); current = next; } else { current = stack.Pop(); } } return maze; }
/// <summary> /// Creates the maze. /// </summary> private void CreateMaze() { // Generate the maze var mazeGenerator = new MazeGenerator2 { Width = 21, Height = 31 }; this.maze = mazeGenerator.Generate(); // Solve the maze var solver = new MazeSolver1(); var start = new Cell(1, 1); var end = new Cell(this.maze.GetLength(0) - 2, this.maze.GetLength(1) - 2); var solution = solver.Solve(this.maze, start, end); int m = this.maze.GetUpperBound(0) + 1; int n = this.maze.GetUpperBound(1) + 1; this.offsetX = -m * 0.5; this.offsetY = -n * 0.5; this.WallsGeometry = this.CreateMazeGeometry(this.maze); this.GroundGeometry = this.CreateGroundGeometry(this.maze, 1, -0.005); this.SolutionGeometry = this.CreateSolutionGeometry(solution); }