Creating a Maze Game in .NET, Part 2: Adding Gameplay

Continuing with our game, all that is left to do is to add the physical Maze object and enable it to work with the existing Grid and Game classes and add it to the form. In this part, you will create the maze object. If you haven’t completed “Creating a Maze Game in .NET, Part 1: Structure” yet, please do so before beginning this article.

Let’s jump right in.

Add a new class and name it Maze, for example.

Add the following code for this class:

C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic;
using System.Windows.Forms;
using System.Drawing;

namespace HTG_Maze_VB
{
   class Maze : DataGridView
   {
      private int WM_LBUTTONDOWN = 0x201;
      private int WM_LBUTTONDBLCLK = 0x203;
      private int WM_KEYDOWN = 0x100;

      public Maze()
      {
         this.DoubleBuffered = true;
      }

      private bool blnSolution;

      public bool ShowSolution
      {
         get
         {
            return blnSolution;
         }

         set
         {
            blnSolution = value;
         }

      }


      protected override void OnRowPrePaint(System.Windows.
         .DataGridViewRowPrePaintEventArgs e)
      {
         e.PaintParts = e.PaintParts &
            ~DataGridViewPaintParts.Focus;
         base.OnRowPrePaint(e);
      }

      protected override void OnPaint(System.Windows.Forms
         .PaintEventArgs e)
      {
         base.OnPaint(e);

         if (clsGrid.Cells == null)
            return;

         for (int row = 0; row <= base.Rows.Count - 1; row++)
         {
            for (int col = 0; col <= base.Columns.Count - 1;
               col++)
            {
               int intWidth = 2;
               Pen pRed = new Pen(Color.Red, intWidth);
               Pen pGreen = new Pen(Color.Green, intWidth);
               int intSize = base.Rows[0].Cells[0].Size.Width;

               if (ShowSolution ? Convert.ToBoolean
                     (clsGrid.Solution[col][row]) :
                     Convert.ToBoolean(clsGrid.Cells[col][row]
                    .intNorth > 0))
                  e.Graphics.DrawLine(ShowSolution ? pGreen :
                     pRed, System.Convert.ToInt32(intSize * col +
                     ((intSize - intWidth) / (double)2) + 1),
                     System.Convert.ToInt32(intSize * row),
                     System.Convert.ToInt32(intSize * col +
                     ((intSize - intWidth) / (double)2) + 1),
                     System.Convert.ToInt32(intSize * row +
                    ((intSize - intWidth) / (double)2) + 1));

               if (ShowSolution ? Convert.ToBoolean(clsGrid
                     .Solution[col][row]) : Convert.ToBoolean
                     (clsGrid.Cells[col][row].intSouth > 0))
                  e.Graphics.DrawLine(ShowSolution ? pGreen : pRed,
                     System.Convert.ToInt32(intSize * col +
                     ((intSize - intWidth) / (double)2) + 1),
                     System.Convert.ToInt32(intSize * row +
                     ((intSize - intWidth) / (double)2) + 1),
                     System.Convert.ToInt32(intSize * col +
                     ((intSize - intWidth) / (double)2) + 1),
                     System.Convert.ToInt32(intSize * (row + 1)));

               if (ShowSolution ? Convert.ToBoolean(clsGrid
                     .Solution[col][row]) : Convert.ToBoolean
                     (clsGrid.Cells[col][row].intWest > 0))
                  e.Graphics.DrawLine(ShowSolution ? pGreen : pRed,
                     System.Convert.ToInt32(intSize * col),
                     System.Convert.ToInt32(intSize * row +
                     ((intSize - intWidth) / (double)2) + 1),
                     System.Convert.ToInt32(intSize * col +
                     ((intSize - intWidth) / (double)2) + 1),
                     System.Convert.ToInt32(intSize * row +
                     ((intSize - intWidth) / (double)2) + 1));

               if (ShowSolution ? Convert.ToBoolean(clsGrid
                     .Solution[col][row]) : Convert.ToBoolean
                     (clsGrid.Cells[col][row].intEast > 0))
                  e.Graphics.DrawLine(ShowSolution ? pGreen : pRed,
                     System.Convert.ToInt32(intSize * col +
                     ((intSize - intWidth) / (double)2) - 1),
                     System.Convert.ToInt32(intSize * row +
                     ((intSize - intWidth) / (double)2) + 1),
                     System.Convert.ToInt32(intSize * (col + 1)),
                     System.Convert.ToInt32(intSize * row +
                     ((intSize - intWidth) / (double)2) + 1));
            }
         }
      }

      protected override void WndProc(ref
         System.Windows.Forms.Message m)
      {
         if (m.Msg == WM_KEYDOWN)
         {
            if (ShowSolution)
               return;

            int row = base.CurrentCell.RowIndex;
            int col = base.CurrentCell.ColumnIndex;

            int msgVal = m.WParam.ToInt32();

            switch ((Keys)msgVal)
            {
               case Keys.Up:
                  if (row > 0)
                  {
                     if (clsGrid.Cells[col][row].blnNorth == false)
                     {
                        clsGrid.Cells[col][row].intNorth += 1;
                        clsGrid.Cells[col][row - 1].intSouth += 1;
                        base.CurrentCell =
                           base.Rows[row - 1].Cells[col];
                     }
                  }
                  break;
               case Keys.Down:
                  if (row < base.Rows.Count - 1)
                  {
                     if (clsGrid.Cells[col][row].blnSouth == false)
                     {
                        clsGrid.Cells[col][row].intSouth += 1;
                        clsGrid.Cells[col][row + 1].intNorth += 1;
                        base.CurrentCell =
                           base.Rows[row + 1].Cells[col];
                     }
                  }
                  break;
               case Keys.Left:
                  if (col > 0)
                  {
                     if (clsGrid.Cells[col][row].blnWest == false)
                     {
                        clsGrid.Cells[col][row].intWest += 1;
                        clsGrid.Cells[col - 1][row].intEast += 1;
                        base.CurrentCell =
                           base.Rows[row].Cells[col - 1];
                     }
                  }
                  break;
               case Keys.Right:
                  if (col < base.Columns.Count - 1)
                  {
                     if (clsGrid.Cells[col][row].blnEast == false)
                     {
                        clsGrid.Cells[col][row].intEast += 1;
                        clsGrid.Cells[col + 1][row].intWest += 1;
                        base.CurrentCell =
                           base.Rows[row].Cells[col + 1];
                     }
                  }
                  break;
            }

            base.Refresh();

            return;
         }
         else if (m.Msg == WM_LBUTTONDBLCLK || m.Msg ==
               WM_LBUTTONDOWN)
            return;

         base.WndProc( ref m);
      }
   }
}

VB.NET

Public Class Maze
   Inherits DataGridView

   Dim WM_LBUTTONDOWN As Integer = &H201
   Dim WM_LBUTTONDBLCLK As Integer = &H203
   Dim WM_KEYDOWN As Integer = &H100

   Public Sub New()

      Me.DoubleBuffered = True

   End Sub

   Private blnSolution As Boolean

   Public Property ShowSolution() As Boolean

      Get
         Return blnSolution
      End Get

      Set(ByVal value As Boolean)
         blnSolution = value
      End Set

   End Property


   Protected Overrides Sub OnRowPrePaint(ByVal e As _
         System.Windows.Forms.DataGridViewRowPrePaintEventArgs)

      e.PaintParts = e.PaintParts And Not _
         DataGridViewPaintParts.Focus
      MyBase.OnRowPrePaint(e)

   End Sub

   Protected Overrides Sub OnPaint(ByVal e As _
         System.Windows.Forms.PaintEventArgs)
      MyBase.OnPaint(e)

      If clsGrid.Cells Is Nothing Then Return

      For row As Integer = 0 To MyBase.Rows.Count - 1

         For col As Integer = 0 To MyBase.Columns.Count - 1

            Dim intWidth As Integer = 2
            Dim pRed As New Pen(Color.Red, intWidth)
            Dim pGreen As New Pen(Color.Green, intWidth)
            Dim intSize As Integer = _
               MyBase.Rows(0).Cells(0).Size.Width

            If If(ShowSolution(), clsGrid.Solution(col)(row), _
                  clsGrid.Cells(col)(row)).intNorth > 0 Then
               e.Graphics.DrawLine(If(ShowSolution(), pGreen, _
                  pRed), CInt(intSize * col + ((intSize - _
                  intWidth) / 2) + 1), CInt(intSize * row), _
                  CInt(intSize * col + ((intSize - intWidth) / 2) _
                  + 1), CInt(intSize * row + ((intSize - _
                  intWidth) / 2) + 1))
            End If

            If If(ShowSolution(), clsGrid.Solution(col)(row), _
                  clsGrid.Cells(col)(row)).intSouth > 0 Then
               e.Graphics.DrawLine(If(ShowSolution(), pGreen, _
                  pRed), CInt(intSize * col + ((intSize - _
                  intWidth) / 2) + 1), CInt(intSize * row + _
                  ((intSize - intWidth) / 2) + 1), CInt(intSize * _
                  col + ((intSize - intWidth) / 2) + 1), _
                  CInt(intSize * (row + 1)))
            End If

            If If(ShowSolution(), clsGrid.Solution(col)(row), _
                  clsGrid.Cells(col)(row)).intWest > 0 Then
               e.Graphics.DrawLine(If(ShowSolution(), pGreen, _
                  pRed), CInt(intSize * col), CInt(intSize * row _
                  + ((intSize - intWidth) / 2) + 1), CInt(intSize _
                  * col + ((intSize - intWidth) / 2) + 1), _
                  CInt(intSize * row + ((intSize - intWidth) / 2) _
                  + 1))

            End If

            If If(ShowSolution(), clsGrid.Solution(col)(row), _
                  clsGrid.Cells(col)(row)).intEast > 0 Then
               e.Graphics.DrawLine(If(ShowSolution(), pGreen, _
                  pRed), CInt(intSize * col + ((intSize - _
                  intWidth) / 2) - 1), CInt(intSize * row + _
                  ((intSize - intWidth) / 2) + 1), CInt(intSize * _
                  (col + 1)), CInt(intSize * row + ((intSize - _
                  intWidth) / 2) + 1))
            End If

         Next

      Next

   End Sub

   Protected Overrides Sub WndProc(ByRef m As _
         System.Windows.Forms.Message)

      If m.Msg = WM_KEYDOWN Then

         If ShowSolution Then Return

         Dim row As Integer = MyBase.CurrentCell.RowIndex
         Dim col As Integer = MyBase.CurrentCell.ColumnIndex

         If m.WParam.ToInt32 = Keys.Up Then

            If row > 0 Then
               If clsGrid.Cells(col)(row).blnNorth = False Then
                  clsGrid.Cells(col)(row).intNorth += 1
                  clsGrid.Cells(col)(row - 1).intSouth += 1
                  MyBase.CurrentCell = MyBase.Rows(row - _
                     1).Cells(col)
               End If

            End If

         ElseIf m.WParam.ToInt32 = Keys.Down Then

            If row < MyBase.Rows.Count - 1 Then
               If clsGrid.Cells(col)(row).blnSouth = False Then
                  clsGrid.Cells(col)(row).intSouth += 1
                  clsGrid.Cells(col)(row + 1).intNorth += 1
                  MyBase.CurrentCell = _
                     MyBase.Rows(row + 1).Cells(col)
               End If

            End If
         ElseIf m.WParam.ToInt32 = Keys.Left Then

            If col > 0 Then
               If clsGrid.Cells(col)(row).blnWest = False Then
                  clsGrid.Cells(col)(row).intWest += 1
                  clsGrid.Cells(col - 1)(row).intEast += 1
                  MyBase.CurrentCell = _
                     MyBase.Rows(row).Cells(col - 1)
               End If

            End If

         ElseIf m.WParam.ToInt32 = Keys.Right Then

            If col < MyBase.Columns.Count - 1 Then
               If clsGrid.Cells(col)(row).blnEast = False Then
                  clsGrid.Cells(col)(row).intEast += 1
                  clsGrid.Cells(col + 1)(row).intWest += 1
                  MyBase.CurrentCell = _
                     MyBase.Rows(row).Cells(col + 1)
               End If

            End If

         End If

         MyBase.Refresh()

         Return

      ElseIf m.Msg = WM_LBUTTONDBLCLK OrElse m.Msg = _
            WM_LBUTTONDOWN Then

         Return

      End If

      MyBase.WndProc(m)

   End Sub

End Class

Let’s see what happens here.

You specify the DoubleBuffered property in the class constructor. This prevents flickering when redrawing pictures. You created the ShowSolution property, which will allow you to see the Solution when clicked or play the game when it is not clicked.

You then override the RowPrePaint event which occurs before the DataGridView’s rows are painted, to identify which parts have the focus. The Paint event of the DataGridView is overridden, and this is where the fun happens. We loop through each cell and draw random connecting lines to form a maze.

Lastly, you override WndProc, which is the normal input for a window to intercept the keys that are pressed. This allows us to navigate through the maze in any cardinal direction and keep track of it by drawing a line as we try to navigate through the maze.

Conclusion

We are almost done. We must now add this to a form, and this is what we will do in Part 3. Until then, happy coding!

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read