Virtual Developer Workshop: Containerized Development with Docker
I love games; but I am in no sense of the word a gamer. My wife is the one and only gamer in my family. Also, the word gamer is quite relative. Why? Well, people think of gamers as young people with the best and biggest pcs playing games over the network with each other. This is not my wife, and most definitely not me. Our idea of fun is playing games such as mystery games, and tower defense games. We love having little in-house competitions (which I, strangely enough, win ). Games such as Angry Birds, Mystery P. I., Plants vs. Zombies, and Rise of Atlantis keep us busy through the night!
My wife always asks me if I could make a game or two (now over five) for her, and I always tend to agree to it. This next game I have tackled was a simple yet powerful Tile-matching game. This means that we have to match images that adjoin each other. This is what we'll do today.
Figure 1 - Our Design
First, our variables. Create the following objects inside your General Declarations section:
Private arrImages(4) As Image 'Our pics Private RandGen As Random 'Random Number Generator Private RandIndex As Integer Dim PBTemp() As PictureBox 'Dynamic PictureBox Private intScore As Integer 'Score Private intPairsFound As Integer 'Total Items Found Private timeEnd As DateTime 'Calculates Ending Time Private timeDiff As TimeSpan 'Difference Between Start and End Times
Next, let us create our board, which will host our pictures:
Public Sub DrawPBGrid() 'Draws Grid of 225 blocks - 15 x 15 Dim NewLoc As New Point 'Location of Each New Block arrImages(0) = My.Resources.ant arrImages(1) = My.Resources.butterfly arrImages(2) = My.Resources.dragonfly arrImages(3) = My.Resources.ladybug arrImages(4) = My.Resources.mosquito RandGen = New Random(Now.Millisecond) For CurrRow As Integer = 0 To 14 'Row By Row For CurrCol As Integer = 0 To 14 'Column In Row By Column In Row NewLoc.X = 2 + CurrRow * 30 'Height Of Block NewLoc.Y = 2 + CurrCol * 30 'Width Of Block Dim PB As New PictureBox 'New picturebox ReDim Preserve PBTemp(CurrCol) With PB 'Set Properties .Name = CurrRow.ToString() & "_" & CurrCol.ToString 'Name Ex: 1_2 ( Row 1 _ Col 2 ) .BorderStyle = BorderStyle.FixedSingle 'Block Effect .Location = NewLoc 'Set Position .Width = 30 'Width .Height = 30 'Height .BackColor = System.Drawing.Color.White 'BackColour RandIndex = RandGen.Next(0, 5) .Image = arrImages(RandIndex) AddHandler PB.MouseClick, AddressOf PB_MouseClick 'Event Handler Click End With PBTemp(CurrCol) = New PictureBox PBTemp(CurrCol).Image = PB.Image Me.Controls.Add(PB) 'Add PBs Next Next End Sub
With the above sub, we produce 225 ( 15 x 15 ) blocks. These will host our pictures. We set each particular block's properties and add a random picture into it via our random generators. Most importantly, we added an event handler so that we will be able to click on each of these dynamic pictureboxes during runtime. When this sub is called, and the program is run, it will produce the following screen.
Figure 2 - Board
These pictures were added as resources, feel free to use them as well.
Add the next sub:
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click DrawPBGrid() timeEnd = DateTime.Now 'Get Current Time Dim minute As Double = System.Convert.ToDouble(5) '5 Minutes Dim second As Double = System.Convert.ToDouble(0) '0 Seconds timeEnd = timeEnd.AddMinutes(minute) 'Add Mintues timeEnd = timeEnd.AddSeconds(second) 'Add Seconds tmrLoop.Start() End Sub
This is the code for our start button. It sets everything in motion. It draws the gaming board, and starts the count down timer, which gives 5 minutes.
Now things start to become interesting. What we need to do now is to use the common PictureBox click event handler, to determine our matches. Add the next code:
Private Sub PB_MouseClick(ByVal sender As Object, ByVal e As MouseEventArgs) 'Common Event Handler For PBs Static intCounter As Integer 'How Many Times Clicked? Static pb() As PictureBox 'Selected PictureBox(es) Static colname() As String 'Which Column Is PictureBox(es) In? Static rowname() As String 'Which Row Is PictureBox(es) In? ReDim Preserve pb(intCounter) 'Resize Arrays According To Size of intCounter ReDim Preserve colname(intCounter) ReDim Preserve rowname(intCounter) pb(intCounter) = New PictureBox 'New pictureBox pb(intCounter) = DirectCast(sender, PictureBox) 'Store Selected PictureBox 'Obtain Column Name colname(intCounter) = pb(intCounter).Name.ToString.Substring(0, pb(intCounter).Name.ToString.LastIndexOf("_")) 'Obtain Row Name rowname(intCounter) = pb(intCounter).Name.ToString.Substring(pb(intCounter).Name.ToString.LastIndexOf("_") + 1) If intCounter >= 1 Then 'If Clicked Twice If pb(intCounter - 1).Image Is pb(intCounter).Image Then 'If Clicked Pictures Are The Same 'If Pictures In Same Row / Column If colname(intCounter - 1) = colname(intCounter) OrElse rowname(intCounter - 1) = rowname(intCounter) Then pb(intCounter - 1).BackColor = Color.Black 'Indicate Match pb(intCounter).BackColor = Color.Black pb(intCounter - 1).Image = Nothing 'Clear Pictures pb(intCounter).Image = Nothing intScore += 20 'Update Score lblScore.Text = intScore intPairsFound += 1 Scramble() 'Scramble Pictures End If End If End If intCounter += 1 'Increase intCounter With Each Click End Sub
Not too difficult hey? Inside the above sub we have called the Scramble sub, let us add it now:
Private Sub Scramble() 'Replace Pictures Randomly Dim RandGen As New Random(Now.Millisecond) 'Generate Random Number For Each P As PictureBox In Me.Controls.OfType(Of PictureBox)() 'Loop Through PictureBox Grid Dim RandIndex As Integer = RandGen.Next(0, 5) 'Generate New Random Picture If P.BackColor <> Color.Black Then 'Skip Found Pairs P.Image = arrImages(RandIndex) 'Replace Picture Else P.Image = Nothing End If Next End Sub
Quite straightforward. We loop through all of the pictureboxes on the form. We then replace all of the pictures randomly, except for the pairs that have already been found. Add the final events and subs:
Private Sub ShowSummary() 'Shows Summary After Game Completion Dim strSummary As String 'Summary String 'Compose String For Output strSummary = "Summary" & Environment.NewLine & Environment.NewLine & _ "Pairs Found: " & intPairsFound.ToString & Environment.NewLine & Environment.NewLine & _ "SCORE: " & intScore.ToString lblScore.Text = strSummary 'Write Text End Sub Private Sub tmrLoop_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrLoop.Tick Dim strFinalOutput As String 'Output String timeDiff = timeEnd - DateTime.Now 'Caclulate Difference Between Start and End Dim output As TimeSpan = New TimeSpan(timeDiff.Hours, timeDiff.Minutes, timeDiff.Seconds) strFinalOutput = "Time Left: " & output.ToString().Substring(3) 'Concatenates Time lblTime.Text = strFinalOutput If (timeDiff.Ticks < 0) Then 'If Time is Up tmrLoop.Stop() MessageBox.Show("Time's UP!!") ShowSummary() btnStart.Enabled = True End If End Sub Private Sub btnEnd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEnd.Click tmrLoop.Stop() 'Stop Timer ShowSummary() 'Show Summary End Sub
The ShowSummary sub is responsible for displaying our score, once the game is over or has been stopped. The tmrLoop_Tick event counts down the time (5 minutes we set up inside btnStart_Click). btnEnd stops the game and produces a score summary.
If you were to run this game now, you'd be able to select matching pairs. After a pair has been identified, that pair will turn black and the images will shuffle. This makes things a bit more interesting for the player, and a bit more difficult to match all the pairs. Your game play screen will look something like Figure 3.
Figure 3 - Playing the game
I am including the source code in zipped format below, just in case you have lost your way somehow. I hope you have enjoyed creating this simple yet powerful game and that you have benefited from it. Until next time, cheers!