/// <summary> /// Used to create a begin and end for a maze /// </summary> /// <param name="arr">The array of the maze</param> private void makeMazeBeginEnd(Cell[,] arr) { Point temp = new Point(); temp.Y = this.random.Next(this.height); arr[temp.Y, temp.X].LeftWall = false; this.begin = arr[temp.Y, temp.X]; temp.Y = this.random.Next(this.height); temp.X = this.width - 1; arr[temp.Y, temp.X].RightWall = false; this.end = arr[temp.Y, temp.X]; }
/// <summary> /// Solves the maze with the right-hand role recursively - /// DON'T USE! IT IS FOR DEMONSTRATING PURPOSES ONLY! /// </summary> /// <param name="start">The maze begin</param> /// <param name="dir">the initial direction</param> private void rightHandRuleSolve(Cell start, Directions dir) { /*بص على يمينك فاضي؟ خش من غير كلام كثير في حيطة؟ بص قدامك في حيطة برضه؟ طب شوف شمالك كده حيطة؟ ارجع بقى*/ // look at your right (with respect to your direction) // No wall? go in it. // Wall? look at your front (with respect to your direction) // Wall too? look at your left (with respect to your direction) // Wall too? go back (in the reverse of your direction) // note that the right of the right is down, the left of down is right, ect // has arrived, return if (start.Position == this.end.Position) { return; } // mark it as visited for graphics this.maze[start.Position.Y, start.Position.X].Visited = true; // to view the current solving location this.currentSolvePos = start.Position; // to slow operation Thread.SpinWait(this.Sleep * sleepPeriod); bool flag; switch (dir) { case Directions.Right: flag = start.Position.Y + 1 < height; if (!flag || this.maze[start.Position.Y + 1, start.Position.X].UpWall) { flag = start.Position.X + 1 < width; if (!flag || maze[start.Position.Y, start.Position.X + 1].LeftWall) { flag = start.Position.Y - 1 >= 0; if (!flag || maze[start.Position.Y - 1, start.Position.X].DownWall) { rightHandRuleSolve(this.maze[start.Position.Y, start.Position.X], Directions.Left); } else { rightHandRuleSolve(maze[start.Position.Y - 1, start.Position.X], Directions.Up); } } else { rightHandRuleSolve(this.maze[start.Position.Y, start.Position.X + 1], Directions.Right); } } else { rightHandRuleSolve(this.maze[start.Position.Y + 1, start.Position.X], Directions.Down); } break; case Directions.Left: flag = start.Position.Y - 1 >= 0; if (!flag || maze[start.Position.Y - 1, start.Position.X].DownWall) { flag = start.Position.X - 1 >= 0; if (!flag || maze[start.Position.Y, start.Position.X - 1].RightWall) { flag = start.Position.Y + 1 < this.height; if (!flag || maze[start.Position.Y + 1, start.Position.X].UpWall) { rightHandRuleSolve(maze[start.Position.Y, start.Position.X], Directions.Right); } else { rightHandRuleSolve(maze[start.Position.Y + 1, start.Position.X], Directions.Down); } } else { rightHandRuleSolve(maze[start.Position.Y, start.Position.X - 1], Directions.Left); } } else { rightHandRuleSolve(maze[start.Position.Y - 1, start.Position.X], Directions.Up); } break; case Directions.Up: flag = start.Position.X + 1 < this.width; if (!flag || maze[start.Position.Y, start.Position.X + 1].LeftWall) { flag = start.Position.Y - 1 >= 0; if (!flag || maze[start.Position.Y - 1, start.Position.X].DownWall) { flag = start.Position.X - 1 >= 0; if (!flag || maze[start.Position.Y, start.Position.X - 1].RightWall) { rightHandRuleSolve(maze[start.Position.Y, start.Position.X], Directions.Down); } else { rightHandRuleSolve(maze[start.Position.Y, start.Position.X - 1], Directions.Left); } } else { rightHandRuleSolve(maze[start.Position.Y - 1, start.Position.X], Directions.Up); } } else { rightHandRuleSolve(maze[start.Position.Y, start.Position.X + 1], Directions.Right); } break; default: flag = start.Position.X - 1 >= 0; if (!flag || maze[start.Position.Y, start.Position.X - 1].RightWall) { flag = start.Position.Y + 1 < this.height; if (!flag || maze[start.Position.Y + 1, start.Position.X].UpWall) { flag = start.Position.X + 1 < this.width; if (!flag || maze[start.Position.Y, start.Position.X + 1].LeftWall) { rightHandRuleSolve(maze[start.Position.Y, start.Position.X], Directions.Up); } else { rightHandRuleSolve(maze[start.Position.Y, start.Position.X + 1], Directions.Right); } } else { rightHandRuleSolve(maze[start.Position.Y + 1, start.Position.X], Directions.Down); } } else { rightHandRuleSolve(maze[start.Position.Y, start.Position.X - 1], Directions.Left); } break; } }
/// <summary> /// Solves the maze with the right-hand role iteratively /// </summary> /// <param name="start">The maze begin</param> /// <param name="dir">the initial direction</param> private void iterativeRightHandRuleSolve(Cell start, Directions dir) { // look at your right (with respect to your direction) // No wall? go in it. // Wall? look at your front (with respect to your direction) // Wall too? look at your left (with respect to your direction) // Wall too? go back (in the reverse of your direction) // note that the right of the right is down, the left of down is right, ect bool flag; // repeat while you didn't reach the end while (start.Position != this.end.Position) { // for graphics this.maze[start.Position.Y, start.Position.X].Visited = true; // for graphics this.currentSolvePos = start.Position; // to slow operation Thread.SpinWait(this.Sleep * sleepPeriod); switch (dir) { case Directions.Right: // has up wall? flag = start.Position.Y + 1 < height; if (!flag || this.maze[start.Position.Y + 1, start.Position.X].UpWall) { // has left wall? flag = start.Position.X + 1 < width; if (!flag || maze[start.Position.Y, start.Position.X + 1].LeftWall) { // has down wall ? flag = start.Position.Y - 1 >= 0; if (!flag || maze[start.Position.Y - 1, start.Position.X].DownWall) { start = this.maze[start.Position.Y, start.Position.X]; dir = Directions.Left; } else { start = maze[start.Position.Y - 1, start.Position.X]; dir = Directions.Up; } } else { start = this.maze[start.Position.Y, start.Position.X + 1]; dir = Directions.Right; } } else { start = this.maze[start.Position.Y + 1, start.Position.X]; dir = Directions.Down; } break; case Directions.Left: flag = start.Position.Y - 1 >= 0; if (!flag || maze[start.Position.Y - 1, start.Position.X].DownWall) { flag = start.Position.X - 1 >= 0; if (!flag || maze[start.Position.Y, start.Position.X - 1].RightWall) { flag = start.Position.Y + 1 < this.height; if (!flag || maze[start.Position.Y + 1, start.Position.X].UpWall) { start = maze[start.Position.Y, start.Position.X]; dir = Directions.Right; } else { start = maze[start.Position.Y + 1, start.Position.X]; dir = Directions.Down; } } else { start = maze[start.Position.Y, start.Position.X - 1]; dir = Directions.Left; } } else { start = maze[start.Position.Y - 1, start.Position.X]; dir = Directions.Up; } break; case Directions.Up: flag = start.Position.X + 1 < this.width; if (!flag || maze[start.Position.Y, start.Position.X + 1].LeftWall) { flag = start.Position.Y - 1 >= 0; if (!flag || maze[start.Position.Y - 1, start.Position.X].DownWall) { flag = start.Position.X - 1 >= 0; if (!flag || maze[start.Position.Y, start.Position.X - 1].RightWall) { start = maze[start.Position.Y, start.Position.X]; dir = Directions.Down; } else { start = maze[start.Position.Y, start.Position.X - 1]; dir = Directions.Left; } } else { start = maze[start.Position.Y - 1, start.Position.X]; dir = Directions.Up; } } else { start = maze[start.Position.Y, start.Position.X + 1]; dir = Directions.Right; } break; default: flag = start.Position.X - 1 >= 0; if (!flag || maze[start.Position.Y, start.Position.X - 1].RightWall) { flag = start.Position.Y + 1 < this.height; if (!flag || maze[start.Position.Y + 1, start.Position.X].UpWall) { flag = start.Position.X + 1 < this.width; if (!flag || maze[start.Position.Y, start.Position.X + 1].LeftWall) { start = maze[start.Position.Y, start.Position.X]; dir = Directions.Up; } else { start = maze[start.Position.Y, start.Position.X + 1]; dir = Directions.Right; } } else { start = maze[start.Position.Y + 1, start.Position.X]; dir = Directions.Down; } } else { start = maze[start.Position.Y, start.Position.X - 1]; dir = Directions.Left; } break; } } this.maze[start.Position.Y, start.Position.X].Visited = true; this.currentSolvePos = start.Position; }
/// <summary> /// Knocks wall between two neighbor cellls /// </summary> /// <param name="maze">The maze array</param> /// <param name="current">the current cell</param> /// <param name="next">the next neighbor cell</param> private void knockWall(Cell[,] maze, ref Cell current, ref Cell next) { // The next is down if (current.Position.X == next.Position.X && current.Position.Y > next.Position.Y) { maze[current.Position.Y, current.Position.X].UpWall = false; maze[next.Position.Y, next.Position.X].DownWall = false; } // the next is up else if (current.Position.X == next.Position.X) { maze[current.Position.Y, current.Position.X].DownWall = false; maze[next.Position.Y, next.Position.X].UpWall = false; } // the next is right else if (current.Position.X > next.Position.X) { maze[current.Position.Y, current.Position.X].LeftWall = false; maze[next.Position.Y, next.Position.X].RightWall = false; } // the next is left else { maze[current.Position.Y, current.Position.X].RightWall = false; maze[next.Position.Y, next.Position.X].LeftWall = false; } }
/// <summary> /// Resets a maze array /// </summary> /// <param name="arr">The maze array</param> /// <param name="width">Number of squares in width</param> /// <param name="height">Number of squares in height</param> private void initailze(Cell[,] arr, int width, int height) { this.width = width; this.height = height; for (int i = 0; i < this.height; i++) { for (int j = 0; j < this.width; j++) { arr[i, j] = new Cell(new Point(j * this.unitX, i * this.unitY), new Point(j, i)); } } }
/// <summary> /// Solves a maze with iterative backtracking DFS /// </summary> /// <param name="start">The start of the maze cell</param> /// <param name="end">The end of the maze cell</param> /// <returns>returrns true if the path is found</returns> private unsafe bool iterativeDepthFirstSearchSolve(Cell start, Cell end) { // unsafe indicates that this method uses pointers Stack<Cell> stack = new Stack<Cell>(); stack.Push(start); while (stack.Count > 0) { Cell temp = stack.Pop(); // base condition if (temp.Position == end.Position) { // add end point to foundPath this.foundPath.Add(temp); // dereference all pointers chain until you reach the begin while (temp.Previous != null) { this.foundPath.Add(temp); temp = *temp.Previous; } // add begin point to foundPath this.foundPath.Add(temp); // to view green square on it this.maze[temp.Position.Y, temp.Position.X].Visited = true; return true; } // for the graphics this.currentSolvePos = temp.Position; // mark as visited to prevent infinite loops this.maze[temp.Position.Y, temp.Position.X].Visited = true; // used to slow operation Thread.SpinWait(this.Sleep * sleepPeriod); // Check every neighbor cell // If it exists (not outside the maze bounds) // and if there is no wall between start and it // set the next.Previous to the current cell // push next into stack // else complete // Left if (temp.Position.X - 1 >= 0 && !this.maze[temp.Position.Y, temp.Position.X - 1].RightWall && !this.maze[temp.Position.Y, temp.Position.X - 1].Visited) { // fixed must be used to indicate that current memory-location won't be changed fixed (Cell* cell = &this.maze[temp.Position.Y, temp.Position.X]) this.maze[temp.Position.Y, temp.Position.X - 1].Previous = cell; stack.Push(this.maze[temp.Position.Y, temp.Position.X - 1]); } // Right if (temp.Position.X + 1 < this.width && !this.maze[temp.Position.Y, temp.Position.X + 1].LeftWall && !this.maze[temp.Position.Y, temp.Position.X + 1].Visited) { fixed (Cell* cell = &this.maze[temp.Position.Y, temp.Position.X]) this.maze[temp.Position.Y, temp.Position.X + 1].Previous = cell; stack.Push(this.maze[temp.Position.Y, temp.Position.X + 1]); } // Up if (temp.Position.Y - 1 >= 0 && !this.maze[temp.Position.Y - 1, temp.Position.X].DownWall && !this.maze[temp.Position.Y - 1, temp.Position.X].Visited) { fixed (Cell* cell = &this.maze[temp.Position.Y, temp.Position.X]) this.maze[temp.Position.Y - 1, temp.Position.X].Previous = cell; stack.Push(this.maze[temp.Position.Y - 1, temp.Position.X]); } // Down if (temp.Position.Y + 1 < this.height && !this.maze[temp.Position.Y + 1, temp.Position.X].UpWall && !this.maze[temp.Position.Y + 1, temp.Position.X].Visited) { fixed (Cell* cell = &this.maze[temp.Position.Y, temp.Position.X]) this.maze[temp.Position.Y + 1, temp.Position.X].Previous = cell; stack.Push(this.maze[temp.Position.Y + 1, temp.Position.X]); } } // no solution found return false; }
/// <summary> /// Gets all neighbor cells to a specific cell, /// where those neighbors exist and not visited already /// </summary> /// <param name="arr">The maze array</param> /// <param name="cell">The current cell to get neighbors</param> /// <param name="width">The width of the maze</param> /// <param name="height">The height of the maze</param> /// <returns></returns> private List<Point> getNeighbours(Cell[,] arr, Cell cell, int width, int height) { Point temp = cell.Position; List<Point> availablePlaces = new List<Point>(); // Left temp.X = cell.Position.X - 1; if (temp.X >= 0 && this.allWallsIntact(arr, arr[temp.Y, temp.X])) { availablePlaces.Add(temp); } // Right temp.X = cell.Position.X + 1; if (temp.X < width && this.allWallsIntact(arr, arr[temp.Y, temp.X])) { availablePlaces.Add(temp); } // Up temp.X = cell.Position.X; temp.Y = cell.Position.Y - 1; if (temp.Y >= 0 && this.allWallsIntact(arr, arr[temp.Y, temp.X])) { availablePlaces.Add(temp); } // Down temp.Y = cell.Position.Y + 1; if (temp.Y < height && this.allWallsIntact(arr, arr[temp.Y, temp.X])) { availablePlaces.Add(temp); } return availablePlaces; }
/// <summary> /// Solves a maze with recursive backtracking DFS - /// DON'T USE! IT IS FOR DEMONSTRATING PURPOSES ONLY! /// </summary> /// <param name="start">The start of the maze cell</param> /// <param name="end">The end of the maze cell</param> /// <returns>returrns true if the path is found</returns> private unsafe bool depthFirstSearchSolve(Cell *st, ref Cell end) { // base condition if (st->Position == end.Position) { // make it visited in order to be drawed with green this.maze[st->Position.Y, st->Position.X].Visited = true; // add end point to the foundPath this.foundPath.Add(*st); return true; } // has been visited alread, return if (this.maze[st->Position.Y, st->Position.X].Visited) return false; // for the graphics this.currentSolvePos = st->Position; // used to slow the process Thread.SpinWait(this.Sleep * sleepPeriod); // mark as visited this.maze[st->Position.Y, st->Position.X].Visited = true; // Check every neighbor cell // If it exists (not outside the maze bounds) // and if there is no wall between start and it // recursive call this method with it // if it returns true, add the current start to foundPath and return true too // else complete // Left if (st->Position.X - 1 >= 0 && !this.maze[st->Position.Y, st->Position.X - 1].RightWall) { this.maze[st->Position.Y, st->Position.X].Path = Cell.Paths.Left; fixed (Cell *ptr = &this.maze[st->Position.Y, st->Position.X - 1]) { if (this.depthFirstSearchSolve(ptr, ref end)) { this.foundPath.Add(*st); return true; } } } // for the graphics this.currentSolvePos = st->Position; // used to slow the process Thread.SpinWait(this.Sleep * sleepPeriod); // Right if (st->Position.X + 1 < this.width && !this.maze[st->Position.Y, st->Position.X + 1].LeftWall) { this.maze[st->Position.Y, st->Position.X].Path = Cell.Paths.Right; fixed (Cell* ptr = &this.maze[st->Position.Y, st->Position.X + 1]) { if (this.depthFirstSearchSolve(ptr, ref end)) { this.foundPath.Add(*st); return true; } } } // for the graphics this.currentSolvePos = st->Position; // used to slow the process Thread.SpinWait(this.Sleep * sleepPeriod); // Up if (st->Position.Y - 1 >= 0 && !this.maze[st->Position.Y - 1, st->Position.X].DownWall) { this.maze[st->Position.Y, st->Position.X].Path = Cell.Paths.Up; fixed (Cell* ptr = &this.maze[st->Position.Y - 1, st->Position.X]) { if (this.depthFirstSearchSolve(ptr, ref end)) { this.foundPath.Add(*st); return true; } } } // for the graphics this.currentSolvePos = st->Position; // used to slow the process Thread.SpinWait(this.Sleep * sleepPeriod); // Down if (st->Position.Y + 1 < this.height && !this.maze[st->Position.Y + 1, st->Position.X].UpWall) { this.maze[st->Position.Y, st->Position.X].Path = Cell.Paths.Down; fixed (Cell* ptr = &this.maze[st->Position.Y + 1, st->Position.X]) { if (this.depthFirstSearchSolve(ptr, ref end)) { this.foundPath.Add(*st); return true; } } } this.maze[st->Position.Y, st->Position.X].Path = Cell.Paths.None; return false; }
/// <summary> /// Generate a maze with the Depth-First Search approach /// </summary> /// <param name="arr">the array of cells</param> /// <param name="width">A width for the maze</param> /// <param name="height">A height for the maze</param> private void depthFirstSearchMazeGeneration(Cell[,] arr, int width, int height) { Stack<Cell> stack = new Stack<Cell>(); Random random = new Random(); Cell location = arr[this.random.Next(height), this.random.Next(width)]; stack.Push(location); while (stack.Count > 0) { List<Point> neighbours = this.getNeighbours(arr, location, width, height); if (neighbours.Count > 0) { Point temp = neighbours[random.Next(neighbours.Count)]; this.currentGenerateLocation = temp; this.knockWall(arr, ref location, ref arr[temp.Y, temp.X]); stack.Push(location); location = arr[temp.Y, temp.X]; } else { location = stack.Pop(); } Thread.SpinWait(this.Sleep * sleepPeriod); } this.makeMazeBeginEnd(this.maze); }
/// <summary> /// Determines whether a particular cell has all its walls intact /// </summary> /// <param name="arr">the maze array</param> /// <param name="cell">The cell to check</param> /// <returns></returns> private bool allWallsIntact(Cell[,] arr, Cell cell) { for (int i = 0; i < 4; i++) { if (!arr[cell.Position.Y, cell.Position.X][i]) { return false; } } return true; }
/// <summary> /// Used to reset all cells /// </summary> /// <param name="arr">The maze array to reset elements</param> private void unvisitAll(Cell[,] arr) { for (int i = 0; i < this.maze.GetLength(0); i++) { for (int j = 0; j < this.maze.GetLength(1); j++) { arr[i, j].Visited = false; arr[i, j].Path = Cell.Paths.None; } } }