Math Maze






3.67/5 (3 votes)
Mar 29, 2006
4 min read

52436

1610
A maze with invisible walls for you to solve!
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