- Mazes For Programmers part 1, introduction
- Mazes For Programmers part 2, some basics
- Mazes For Programmers part 3, graphical UI
- Mazes For Programmers part 4, implementing the first two algorithms
- Mazes For Programmers part 5, Dijkstra’s algorithm
- Mazes For Programmers part 6, paths through the mazes
- Mazes For Programmers part 7, the Aldous-Broder algorithm, and when random is not very random
- Mazes For Programmers part 8, a random walk with Wilson’s algorithm
- Mazes For Programmers part 9, some statistics, and combining the Aldous-Broder and Wilson’s algorithms
- Mazes For Programmers part 10, improving the Aldous-Broder algorithm to remove short dead-ends
- Mazes For Programmers part 11, Ruby vs C# speed tests
- Source code on GitHub
You can see the C# code I wrote in the GitHub repository (as long as you promise not to laugh), so I won’t go into too much detail here. I just want to comment on a couple of places where I did things differently from him.
Enumerating the cells
I added a Cells
property to the Maze
class (which he called Grid
, but I renamed to avoid clashing with the WPF Grid
UI element, see part 3 of this blog series) to allow me to enumerate all the cells in the maze without a nested for
loop…
public IEnumerable<Cell> Cells { get { for (int row = 0; row < Rows; row++) { for (int col = 0; col < Cols; col++) { yield return _cells[row, col]; } } } }
This made some of the later code cleaner and more concise. Being a functional chap, I prefer seeing code that shows what it does, rather than getting bogged down with how it does it.
Boundary cells
At various points in the code, we need to detect if a cell is on the boundary (ie edge) of the maze. He did this by checking if the cell were nil
(Ruby-speak, null
in C#-speak). I added some convenience properties to the Cell
class to make this neater and clearer…
public Cell North { get; set; } public bool NorthernBoundary => North == null; // Similarly for the other three directions...
This allowed me to replace ugly code like this…
if (cell.North == null) {
…with clean, readable code like this…
if (cell.NorthernBoundary) {
Again, not a major change, but it makes the code easier to read.
Text display of the mazes
My override of ToString
ended up a little different from his. Actually, my override itself ended up being extremely short…
public override string ToString() => ToString(c => " ");
…as I added an overload for the method that allowed me to pass in a Func
that would convert a Cell into a string, allowing me to display something inside the cell…
public string ToString(Func<Cell, string> format) { string output = "+" + string.Concat(Enumerable.Repeat("---+", Cols)) + _nl; for (int row = 0; row < Rows; row++) { string cellRow = "|"; string lowerWall = "+"; for (int col = 0; col < Cols; col++) { Cell cell = this[row, col]; cellRow += format(cell) + (cell?.Linked(cell.East) ?? false ? " " : "|"); lowerWall += (cell?.Linked(cell.South) ?? false ? " " : "---") + "+"; } output += cellRow + _nl + lowerWall + _nl; } return output; }
Without any parameters, this displayed a grid much like his…
+---+---+---+---+---+ | | + +---+---+---+ + | | | + + +---+---+---+ | | | +---+ +---+ +---+ | | | + +---+ +---+ + | | | | +---+---+---+---+---+
The Func overload was very useful during debugging. For example, at one point I got confused as to which cell was which. Given that my mazes were no bigger than 10×10, I could conveniently display the cell co-ordinates in three characters, which was the space he allowed for each cell. The following line did that neatly…
Debug.WriteLine(maze.ToString(c => $"{c.Row},{c.Col}"));
The result was…
+---+---+---+---+---+ |0,0 0,1 0,2 0,3 0,4| + + + + +---+ |1,0|1,1|1,2|1,3 1,4| + + +---+---+ + |2,0|2,1|2,2 2,3 2,4| + + + + + + |3,0|3,1|3,2|3,3|3,4| +---+---+---+---+ + |4,0 4,1 4,2 4,3 4,4| +---+---+---+---+---+
You have to squint a bit to distinguish between carved cells, although I could have got around that by just showing all cell borders instead of only the ones left after carving, but it’s still clear enough to show which cell is which. This turned out to be extremely helpful during my first attempt at a graphical UI, as I started off drawing the cell borders on the wrong sides.
Later on, when I implemented Dijkstra’s algorithm (hopefully the subject of a future blog post), I was able to display the distance in the cell by using the following…
Debug.WriteLine(maze.ToString(c => d.ToString("000")));
…where d
is my distances object. This showed something like the following…
+---+---+---+---+---+ |000 001 002 003 004| +---+ +---+---+---+ |003 002 003 004 005| +---+ +---+ + + |004 003 004|005|006| + + + + +---+ |005|004|005|006 007| + +---+ +---+ + |006|007 006 007|008| +---+---+---+---+---+
A couple of helpful extension methods
One of the few Ruby features that I think is missing from C# is the each
method. I’m used to this from jQuery as well, and so generally add a static Extension
class to my projects, with some useful methods such as ForEach
…
public static class Extensions { public static void ForEach<T>(this IEnumerable<T> items, Action<T> action) { if (items == null) { return; } foreach (T item in items) { action(item); } } public static T Rand<T>(this IEnumerable<T> items) => items.Skip(new Random((int)DateTime.Now.Ticks).Next(1000) % items.Count()).First(); }
The second method there allows me to pick a random element from a collection. This was useful when implementing the SideWinder algorithm, where he used Ruby’s sample
method.
Be First to Comment