Mazes For Programmers part 2, some basics

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.

Series Navigation<< Mazes For Programmers part 1, introductionMazes For Programmers part 3, graphical UI >>

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.