Comment Feed for Channel 9 - Getting lost and found with the C# Maze Generator and Solverhttp://files.channel9.msdn.com/thumbnail/5c09f7cb-1c7a-4fe0-9bd4-8a68f851d888.pngChannel 9 - Getting lost and found with the C# Maze Generator and SolverToday's project harkens back to days when we used to love mazes... (Okay, I still do, but that's besides the point). C# Maze Generator and SolverIntroductionThis demo creates and solves mazes using the Breadth-First and Depth-First searches. It can be very useful in demonstrating these algorithms. BackgroundThis article assumes you have a basic knowledge of VC#.NET. A little bit knowledge of pointers, recursion, and GDI+ graphics is appreciated, too. We will frequently use the Stack and Queue data structures. As reminders, recall that the stack is First-In-Last-Out (FILO), while the queue is First-In-First-Out (FIFO). The Maze GenerationDepth-First Search and Breadth-First Search are very useful approaches in many applications. One of them is creating/solving mazes. To generate a maze with DFS, we have this simple algorithm: Have all walls in your maze intact (not broken). Choose a random start point, push it into the stack. Call this initial square 'current square'. Repeat while the stack is not empty: Get list of all neighboring squares to the current square where those neighbors have all their walls intact (unvisited). If there are neighbors (i.e., List.Count > 0): Choose one of the neighbors at random. Call it 'temp'. Knock the wall between 'temp' and the current square. Push the current square into the stack. Make the current square equals 'temp'. Else if there are no neighbors, pop a square from the stack. Make current square equal to it. After executing this algorithm, you will have a 'prefect maze' which indicates that your maze doesn't have 'dead ends' (i.e., unreachable squares) and has a single solution. BFS generation is the same except the stack that will be replaced with a queue. If the generation is with DFS, the program will choose a random wall and knock it, then it moves to the new square. When it reaches an edge (or visited cell), it backs again to the nearest "UNVISITED" square. When generation is with BFS, program will knock the wall in a way similar to DFS, but BFS uses queue, causing to finish near squares first before the far ones. In contrast, DFS uses stack, which causes it to finish far first then back to near. The Maze SolvingAgain, DFS and BFS have many helpful applications. We will now use them to solve the maze they created, as in the following backtracking algorithm: Have an empty list for the found path. Call it 'foundPath'. function DFS(Cell start) : Boolean if start is equal to the maze end Add start to 'foundPath' Mark start as visited Return true Else if start is visited already Return false Else: Mark start as visited For each neighbor of start If the wall between start and neighbor is knocked Recursively call DFS function with the neighbor If the call returns true Add start to 'foundPath' Return true If you reached this point, return falseThis algorithm finds path to the maze end. When it returns true, it adds the current location to 'foundPath', causing all other calls in the stack to return true and add their current locations, too. At finish, we will have a complete list of squares between begin and end. However, we won't use those recursive versions, since they may cause a StackOverFlowException when the calls are too many for that stack. We will instead use the same algorithms but iteratively (i.e., with a loop). Instead of making every recursive call, add its 'start' to 'foundPath', and we will have a pointer to the previous one (as we will see later in the article). The same is about BFS, but again, we will use a queue rather than a stack. For generation, DFS searches at random. When it reaches an edge, it backs to the nearest (UNVISITED) square and repeats the process until it finds the end. On the other hand, BFS searches the near squares first. When it reaches an intersection, it divides into two tracks and discovers the near squares. The process is repeated until the end is found. We have a third method that traverses the maze, the right-hand rule. It considers "putting" your right hand on the wall, never leaving it. Even if the way will be longer, you'll absolutely reach the end, or back again to the beginning if there is no end. We are sure, however, that we have a path to the end, since we are using BFS/DFS that gives perfect mazes. In the right-hand rule, we will consider only traversing the maze without finding the path. Let's take a peek at the project (which ran for me the first time with no problems). The maze generation happens here; private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
object[] args = e.Argument as object[];
int value = (int)args[0];
bool solving = (bool)args[1];
if (!solving)
{
this.maze.Generate(this.pictureBoxDraw.Width / value,
(this.pictureBoxDraw.Height - value) / value,
(int)args[2]);
}
else
{
this.maze.Solve((int)args[2]);
this.hasSolution = true;
}
this.pictureBoxDraw.Invalidate();
}
public void Generate(int width, int height, int method)
{
this.working = true;
this.initailze(this.maze, width, height);
this.mazePen.Dispose();
this.mazePen = this.unitX < 5 ? new Pen(Brushes.WhiteSmoke, 1) : new Pen(Brushes.WhiteSmoke, 3);
if (method == 0)
this.depthFirstSearchMazeGeneration(this.maze, this.width, this.height);
else
this.breadthFirstSearchMazeGeneration(this.maze, this.width, this.height);
this.working = false;
}
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);
}
\ Once you have a maze, you can then have the program solve it too... public unsafe void Solve(int method)
{
this.solving = true;
// initialize
this.foundPath.Clear();
this.unvisitAll(this.maze);
// selecting the method
if (method == 0)
{
if (this.height * this.width < 40 * 80)
fixed (Cell* ptr = &this.begin)
this.depthFirstSearchSolve(ptr, ref this.end);
else
this.iterativeDepthFirstSearchSolve(this.begin, this.end);
}
else if (method == 1)
this.breadthFirstSearchSolve(this.begin, this.end);
else if (method == 2)
this.iterativeRightHandRuleSolve(this.begin, Directions.Right);
this.solving = false;
}
enTue, 27 Jan 2015 12:36:17 GMTTue, 27 Jan 2015 12:36:17 GMTRev9Re: Getting lost and found with the C# Maze Generator and Solver
I stopped reading the code at: this.initailze

posted by Corrector2

]]>
http://channel9.msdn.com/coding4fun/blog/Getting-lost-and-found-with-the-C-Maze-Generator-and-Solver#c634721927845576453
Wed, 09 May 2012 20:39:44 GMThttp://channel9.msdn.com/coding4fun/blog/Getting-lost-and-found-with-the-C-Maze-Generator-and-Solver#c634721927845576453Corrector2Re: Getting lost and found with the C# Maze Generator and Solver
nice

posted by ptcmariano

]]>
http://channel9.msdn.com/coding4fun/blog/Getting-lost-and-found-with-the-C-Maze-Generator-and-Solver#c634732974076665959
Tue, 22 May 2012 15:30:07 GMThttp://channel9.msdn.com/coding4fun/blog/Getting-lost-and-found-with-the-C-Maze-Generator-and-Solver#c634732974076665959ptcmarianoRe: Getting lost and found with the C# Maze Generator and Solver
have other algorithm?

posted by ptcmariano

]]>
http://channel9.msdn.com/coding4fun/blog/Getting-lost-and-found-with-the-C-Maze-Generator-and-Solver#c634732975721811575
Tue, 22 May 2012 15:32:52 GMThttp://channel9.msdn.com/coding4fun/blog/Getting-lost-and-found-with-the-C-Maze-Generator-and-Solver#c634732975721811575ptcmariano