65.9K
CodeProject is changing. Read more.
Home

Math Maze

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.67/5 (3 votes)

Mar 29, 2006

4 min read

viewsIcon

52436

downloadIcon

1610

A maze with invisible walls for you to solve!

Sample Image - MathMaze.gif

Introduction

Here is my latest diversion, a little game I wrote for amusement and to help sharpen my programming skills. I hope you will enjoy playing it and perhaps even pick up a programming tip or trick in the process. The goal of the game is to draw a path through the invisible maze using the numbers along the side and top as an indication of how many squares in that row or column have a line going through them. Sometimes there will be more than one solution which satisfies the row and column numbers, but you must draw the one solution that the computer is "thinking of" to win!

Background

I first saw this type of game in one of my wife's puzzle magazines and decided it looked like fun. Once I decided to write it as a computer game I realized I didn't know a lot about how to generate a maze, so With Google's help I found a description of an algorithm called depth-first search which is easy enough to program and will generate a perfect maze every time. I started by building a class to represent one square on the board, with methods and properties to allow drawing line shapes, setting and removing walls, processing mouse and keyboard input and so on.

I then went on to write the main form code which adds an array of these pieces to itself to create the board layout as well as manage the game play, scoring etc. The main form uses a nice custom control written by Mick Doherty called TabControlEx[^], and I hacked together a control based on StatusBar, so changing the Background color on the Options tab changes all the controls (the exceptions being the spin buttons of the NumericUpDown controls and the dropdown arrows on the ComboBox controls).

Inside the code

If you want to see how you can persist application settings for your program check out the ApplicationSettings class (in Settings.vb). In particular look at the Players property, and the assignment of mySerializer in the SaveAppSettings() method which uses an overload specifying an array of additional object types to serialize:

      mySerializer = New XmlSerializer( _
       GetType(ApplicationSettings), New Type() {GetType(PlayerInfoCollection)})
Note also in method frmMathMaze_Load that after the call to LoadAppSettings() the players variable does not merely assign to appsettings.Players. If we did this then any change to players would show up in appsettings.Players. But the ApplicationSettings class looks to see if any of the properties have changed to decide whether to save a new copy by checking the appSettingsChanged variable. Look at the setter in the Players property:
      Set(ByVal Value As PlayerInfoCollection)
         If Not Value.Equals(_players) Then
            _players = Value
            appSettingsChanged = True
         End If
      End Set
Class PlayerInfoCollection uses an overloaded Equals function (as well as an overloaded Contains) to see if the new Value passed is equal to the current collection. So we need to make a deep copy of appsettings.Players when assigning to players in the main form. The line:
      players = New PlayerInfoCollection(.Players)
does just that by invoking:
      Public Sub New(ByVal source As PlayerInfoCollection)
         For Each pi As PlayerInfo In source
            Me.Add(New PlayerInfo(pi.PlayerName, pi.Wins, pi.Losses))
         Next
      End Sub
This facet of reference type objects is often overlooked by beginners and can be the cause of many headaches! When we do A = anObject and then B = A, sometimes we want changes in A to be reflected in B, but when we don't we need to resort to making a deep copy of A.

Take a look over the PlayerInfoCollection class which inherits CollectionBase to see how to create your own strongly typed collection based on a class that you write (in this case it's the PlayerInfo class). Also the Piece class may be of interest with its custom painting code and overridden ProcessCmdKey method to handle the tab and arrow keys.

The data binding of the players collection to two ComboBox controls proved interesting; I kept getting strange errors when using the 'Remove' button on the Options tab until I realized the problem. I had to resort to using a different BindingContext for each to avoid errors when updating players even though I was releasing and refreshing the DataSource of each ComboBox to update each. Otherwise I would see:

Specified argument was out of the range of valid values.

Points of Interest

While writing Marh Maze I included some testing code so I could actually see that the methods for generating the maze and then finding the solution path were working correctly. I left this code in, see if you can find where on the main form to click to activate it when playing the game!

References

Here are a couple of sites I found valuable info on while writing Math Maze.

MazeWorks - How to Build a Maze[^]
Think Labyrinth: Maze Algorithms[^]

History

29 Mar 2006      Initial release
 1 Apr 2006      Fixed sizing issue
 2 Apr 2006      Added 'Hint' button