Solved

# Memory Game algorithm in VB.Net 2010, help

Posted on 2010-11-08
1,793 Views
I'm starting with a memory match game, it is a 6x6 blocks (images). the thing is i need to populate the matrix but i dont know how:

I was trying to figure out this way:

first randomize from 1 to 36
assign an image to each number
assign each number a cell in the 6x6 matrix
compare clicked image1 with clicked image 2

That is how i can picture it by now. What i would need from you guys is a help to convert this poor algorithm into VB.Net 2010 code.

Thanks,

Oscar
0
Question by:José Perez
• 7
• 5
• 5
• +2

LVL 39

Expert Comment

0

LVL 74

Expert Comment

I think it would be even simpler than that. Just create your grid, randomly each image to two different cells, then, during the game, compare the references of each image to see if they point to the same image. This should be a start. Currently, it displays all the images--you'll need to develop the logic to only display them when they are selected. I left it this way so you can get a feel for what the code is doing.
``````Public Class Form1
Private firstSelection As PictureBox

Private Sub generateButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles generateButton.Click
Dim pb As PictureBox
Dim cellCount As Integer = 12

' Clear existing
Me.FlowLayoutPanel1.Controls.Clear()

' Create the cells
For i As Integer = 0 To cellCount - 1
pb = New PictureBox()

pb.BackColor = Color.AliceBlue
Next

Dim rand As New Random(Now.Millisecond)
Dim images() As Image = {New Bitmap("img1.bmp"), New Bitmap("img2.bmp"), New Bitmap("img3.bmp"), _
New Bitmap("img4.bmp"), New Bitmap("img5.bmp"), New Bitmap("img6.bmp")}
Dim populatedCount As Integer = 0
Dim imgIndex As Integer

' Loop through available images
For imgIndex = 0 To images.Length - 1
' Fill one image into two cells
For loopTwice As Integer = 0 To 1
Do
Dim randomInt As Integer = rand.Next(0, cellCount)

pb = DirectCast(Me.FlowLayoutPanel1.Controls(randomInt), PictureBox)
Loop While pb.Image IsNot Nothing

' Set the image
pb.Image = images(imgIndex)
Next
Next
End Sub

Private Sub PictureBox_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim clickedBox As PictureBox = DirectCast(sender, PictureBox)

If Me.firstSelection Is Nothing Then
clickedBox.BorderStyle = BorderStyle.Fixed3D
Me.firstSelection = clickedBox
Else
If Me.firstSelection IsNot clickedBox AndAlso Me.firstSelection.Image Is clickedBox.Image Then
Me.firstSelection.Image = Nothing
clickedBox.Image = Nothing

MessageBox.Show("Match")
End If

Me.firstSelection.BorderStyle = BorderStyle.None
Me.firstSelection = Nothing
End If
End Sub
End Class
``````
0

LVL 74

Expert Comment

I should have attached a screenshot of the code in action  :)
untitled.PNG
0

LVL 2

Author Comment

ok, it is working, but what changes do i have to do to support 6x6 (36 images)?
0

LVL 85

Expert Comment

Here's a quick Star Wars memory game I made for my son:
http://dl.dropbox.com/u/5131616/StarWarsMemory.zip

*The visual shuffle runs much smoother than in the screencast...the recorder isn't fast enough to capture it properly.
``````Public Class Form1

Private Class Card

Public Img As Image

Public Name As String

Public Sub New(ByVal img As Image, ByVal name As String)

Me.Img = img

Me.Name = name

End Sub

End Class

Private R As New Random

Private Cards As New List(Of Card)

Private ClickCount As Integer = 0

Private FirstCard, SecondCard As Card

Private FirstPB, SecondPB As PictureBox

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

Cards.Clear()

For Each pb As PictureBox In Me.Controls.OfType(Of PictureBox)()

Next

End Sub

Private Sub Form1_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown

Shuffle()

End Sub

Private Sub Shuffle()

If Not BackgroundWorker1.IsBusy Then

My.Computer.Audio.Play(My.Resources.R2D2, AudioPlayMode.BackgroundLoop)

BackgroundWorker1.RunWorkerAsync()

End If

End Sub

Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

Dim C As Card

Dim index As Integer

' shuffle them for awhile while R2D2 sounds play in the background

Dim targetDT As DateTime = DateTime.Now.AddSeconds(3)

While targetDT > Now

For i As Integer = 0 To Cards.Count - 1

index = R.Next(0, Cards.Count)

C = Cards(i)

Cards(i) = Cards(index)

Cards(index) = C

Next

BackgroundWorker1.ReportProgress(0)

End While

End Sub

Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged

' show shuffled cards

For i As Integer = 0 To Cards.Count - 1

CType(Me.Controls("PictureBox" & (i + 1)), PictureBox).Tag = True

CType(Me.Controls("PictureBox" & (i + 1)), PictureBox).Visible = True

CType(Me.Controls("PictureBox" & (i + 1)), PictureBox).Image = Cards(i).Img

Next

End Sub

Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted

' cover them up

For i As Integer = 1 To Cards.Count

CType(Me.Controls("PictureBox" & i), PictureBox).Image = My.Resources.StarWarsLogo

Me.Controls("PictureBox" & i).Tag = True

Next

My.Computer.Audio.Stop()

End Sub

Private Sub pb_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Dim index As Integer

Dim prefix As String = "PictureBox"

Dim pb As PictureBox = CType(sender, PictureBox)

If pb.Tag Then

pb.Tag = False

If Integer.TryParse(pb.Name.Substring(prefix.Length), index) Then

pb.Image = Cards(index - 1).Img

ClickCount = ClickCount + 1

If ClickCount = 1 Then

FirstCard = Cards(index - 1)

FirstPB = pb

ElseIf ClickCount = 2 Then

Application.DoEvents()

SecondCard = Cards(index - 1)

SecondPB = pb

If FirstCard.Name = SecondCard.Name Then

' a match!

My.Computer.Audio.Play(My.Resources.LAZER_WAV, AudioPlayMode.Background)

FirstPB.Visible = False

FirstPB.Tag = True

SecondPB.Visible = False

SecondPB.Tag = True

Dim GameOver As Boolean = True

For Each ctl As Control In Me.Controls.OfType(Of PictureBox)()

If ctl.Visible Then

GameOver = False

Exit For

End If

Next

If GameOver Then

My.Computer.Audio.Play(My.Resources.Force, AudioPlayMode.WaitToComplete)

Shuffle()

End If

Else

' not a match...

My.Computer.Audio.Play(My.Resources.chewy1, AudioPlayMode.Background)

FirstPB.Image = My.Resources.StarWarsLogo

FirstPB.Tag = True

SecondPB.Image = My.Resources.StarWarsLogo

SecondPB.Tag = True

End If

ClickCount = 0

End If

End If

End If

End Sub

End Class
``````
Idle-Mind-367313.flv
0

LVL 74

Expert Comment

I'm glad you're able to get the screencasts to upload Idle. I have yet  to have one not error out during upload  :\

As far as my example and sizing it for 6 x 6, you would need to add some math to appropriately size the FlowLayoutPanel. As long as you set the width to max out at six cards, then the rest should "flow" into place automatically. You would want to fix the size of the panel (or maybe even the form) because any resizing of the panel will cause the PictureBoxes to re-flow, thereby messing up the layout.

"cellCount" and the image source ("images" in my example) would obviously change as well  :)
0

LVL 74

Expert Comment

I hope George Lucas doesn't come after you for that one  ;)
0

LVL 85

Expert Comment

Hehe...no money is being made from the game so I'd think it'd be ok.  =D

*My son picked out all the images to use from a Google image search.
0

LVL 2

Author Comment

idle_mind, your code is very good but too advance for me to understand it. the backgroundworker really confuses me so i cant understand your code (very integrated to the other staff in your code). do you have something more simplistic?

kaufmed, your code is very close to what i am looking for, if you could update to work with 6x6 i would really appreciate it :)

OscarG
0

LVL 74

Accepted Solution

käµfm³d   👽 earned 150 total points
Here is an updated version. The blanks you see are because I only have 6 images--if the full 18 were provided, then these would have had images in them.
``````Public Class Form1
Private firstSelection As PictureBox

Private Sub generateButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles generateButton.Click
Dim pb As PictureBox
Dim cellCount As Integer
Dim width As Integer
Dim height As Integer
Dim dummy As PictureBox = New PictureBox()

If Not Integer.TryParse(Me.widthBox.Text, width) OrElse Not Integer.TryParse(Me.heightBox.Text, height) Then
MessageBox.Show("Please enter a valid cell width and cell height!")
Return
End If

cellCount = width * height

' Size the FlowLayoutPanel
Me.FlowLayoutPanel1.Width = (dummy.Width + dummy.Margin.Left + dummy.Margin.Right) * width
dummy = Nothing

' Clear existing
Me.FlowLayoutPanel1.Controls.Clear()

' Create the cells
For i As Integer = 0 To cellCount - 1
pb = New PictureBox()

pb.BackColor = Color.AliceBlue
Next

Dim rand As New Random(Now.Millisecond)
Dim images() As Image = {New Bitmap("img1.bmp"), New Bitmap("img2.bmp"), New Bitmap("img3.bmp"), _
New Bitmap("img4.bmp"), New Bitmap("img5.bmp"), New Bitmap("img6.bmp")}
Dim populatedCount As Integer = 0
Dim imgIndex As Integer

' Loop through available images
For imgIndex = 0 To images.Length - 1
' Fill one image into two cells
For loopTwice As Integer = 0 To 1
Do
Dim randomInt As Integer = rand.Next(0, cellCount)

pb = DirectCast(Me.FlowLayoutPanel1.Controls(randomInt), PictureBox)
Loop While pb.Image IsNot Nothing

' Set the image
pb.Image = images(imgIndex)
Next
Next
End Sub

Private Sub PictureBox_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim clickedBox As PictureBox = DirectCast(sender, PictureBox)

If Me.firstSelection Is Nothing Then
clickedBox.BorderStyle = BorderStyle.Fixed3D
Me.firstSelection = clickedBox
Else
If Me.firstSelection IsNot clickedBox AndAlso Me.firstSelection.Image Is clickedBox.Image Then
Me.firstSelection.Image = Nothing
clickedBox.Image = Nothing

MessageBox.Show("Match")
End If

Me.firstSelection.BorderStyle = BorderStyle.None
Me.firstSelection = Nothing
End If
End Sub
End Class
``````
untitled.PNG
0

LVL 29

Expert Comment

Hey these examples are pretty cool, ! It would be perfect to have a designer for these examples. If you think of it as pallete of images you can have one add images to the designer and create a single image pallete that you later could break into the dimensions. Would be flexible and only require maintaining one image the pallete per puzzle but allow the user to generate there own images for a puzzle by loading the pallete.
0

LVL 2

Author Comment

kaufmed, we're very closer!!! just 2 minor thing:

i have an error "not memeber of Form1" in the following:
'widthBox'
'hightBox'

what kind of controls are this?
0

LVL 74

Expert Comment

Those are the two textboxes next to the button you see in my last screenshot. The left box is "widthBox" and the right box is "heightBox".
0

LVL 29

Expert Comment

Here is a start for the basic idea for a designer (no GUI) using a single .TIFF image you have a basis for a easy to use pallete.
// usage:

Dim dlg As New OpenFileDialog
dlg.Multiselect = True
dlg.ShowDialog()
' Save the pallete
PuzzleDesigner.Save("c:\users\medieval\documents\pallete.tiff", 64, 64)

'Dim pallete As List(Of Image) = PuzzleDesigner.GetImages("c:\users\medieval\documents\pallete.tiff")
'For Each img As Image In pallete
' PictureBox1.Image = img
' Application.DoEvents()
'Next

``````Imports System.IO

Imports System.Drawing.Imaging

Public Class PuzzleDesigner

Private Sub New()

End Sub

Private Shared imageItems As New List(Of String)

Public Shared Sub AddImage(ByVal imagePath As String)

End Sub

Public Shared Sub AddImage(ByVal imagePaths() As String)

End Sub

Public Shared Sub Clear()

imageItems.Clear()

End Sub

Public Shared Sub Save(ByVal fileName As String, _

ByVal w As Integer, _

ByVal h As Integer)

Dim i As Integer

Dim imageEncoder As Encoder = Encoder.SaveFlag

Dim imageInfo As ImageCodecInfo = Nothing

Dim imageCodec As ImageCodecInfo

Dim ep As New EncoderParameters(1)

For Each imageCodec In ImageCodecInfo.GetImageEncoders()

If imageCodec.MimeType = "image/tiff" Then

imageInfo = imageCodec

End If

Next imageCodec

ep.Param(0) = New EncoderParameter(imageEncoder, EncoderValue.MultiFrame)

Dim master As New Bitmap(imageItems(0))

master = master.GetThumbnailImage(w, h, Nothing, IntPtr.Zero)

master.Save(fileName, imageInfo, ep)

ep.Param(0) = New EncoderParameter(imageEncoder, EncoderValue.FrameDimensionPage)

For i = 1 To imageItems.Count - 1

master.SaveAdd(New Bitmap(imageItems(i)).GetThumbnailImage(w, h, Nothing, IntPtr.Zero), ep)

Next i

ep.Param(0) = New EncoderParameter(imageEncoder, EncoderValue.Flush)

master.Dispose()

End Sub

Public Shared Function GetImages(ByVal fileName As String) As List(Of Image)

Dim i As Integer

Dim imgItems As New List(Of Image)

Dim fs As FileStream = File.Open(fileName, FileMode.Open, FileAccess.Read)

Dim bm As Bitmap = Bitmap.FromStream(fs)

For i = 0 To bm.GetFrameCount(FrameDimension.Page) - 1

bm.SelectActiveFrame(FrameDimension.Page, i)

Dim bmItem As New Bitmap(bm.Width, bm.Height)

Dim g As Graphics = Graphics.FromImage(bmItem)

g.DrawImageUnscaled(bm, 0, 0)

g.Dispose()

Next i

Return imgItems

End Function

End Class
``````
0

LVL 2

Author Comment

received error "invalid parameter" on this part of the code:

``````Dim images() As Image = {New Bitmap("img1.bmp"), New Bitmap("img2.bmp"), New Bitmap("img3.bmp"), New Bitmap("img4.bmp"), New Bitmap("img5.bmp"), New Bitmap("img6.bmp"), _

New Bitmap("img7.bmp"), New Bitmap("img8.bmp"), New Bitmap("img9.bmp"), New Bitmap("img10.bmp"), New Bitmap("img11.bmp"), New Bitmap("img12.bmp"), _

New Bitmap("img13.bmp"), New Bitmap("img14.bmp"), New Bitmap("img15.bmp"), New Bitmap("img16.bmp"), New Bitmap("img17.bmp"), New Bitmap("img18.bmp"), _

New Bitmap("img19.bmp"), New Bitmap("img20.bmp"), New Bitmap("img21.bmp"), New Bitmap("img22.bmp"), New Bitmap("img23.bmp"), New Bitmap("img24.bmp"), _

New Bitmap("img25.bmp"), New Bitmap("img26.bmp"), New Bitmap("img27.bmp"), New Bitmap("img28.bmp"), New Bitmap("img29.bmp"), New Bitmap("img30.bmp"), _

New Bitmap("img31.bmp"), New Bitmap("img32.bmp"), New Bitmap("img33.bmp"), New Bitmap("img34.bmp"), New Bitmap("img35.bmp"), New Bitmap("img36.bmp")}
``````
0

LVL 85

Expert Comment

It probably can't find the files you're trying to use...

If you're running from the IDE they should be in the \bin folder.
0

LVL 85

Expert Comment

You could load them this way as well:
``````    Dim images As New List(Of Image)

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

Dim imageName As String

For i As Integer = 1 To 36

imageName = "img" & i & ".bmp"

Try

Catch ex As Exception

MessageBox.Show("File: " & imageName & vbCrLf & vbCrLf & ex.ToString, "Error Loading Image", MessageBoxButtons.OK, MessageBoxIcon.Error)

End Try

Next

End Sub
``````
0

LVL 2

Author Comment

in fact, the 'bin' folder. thanks.
0

LVL 29

Expert Comment

IMHO It's a pain to deal with so many images individually this is why a pallete scheme could be very useful to implement you have 1 image per set of images. It will make it easier to to manage them and even be more user friendly if you wanted to add a designer for the user to add there own set of images. *The game may not get old so quick. Keep your kids busy while they make there own puzzle with images. This is the type of features users generally look for in applications it gives them a sense of being part of the game. You could even have a small website or people who build them and upload them as a single file expanding the game giving that extra edge.
0

LVL 29

Expert Comment

LOL... Sometimes you need to talk to the wall
0

LVL 85

Expert Comment

I agree with you bud...but as he's learning still it's probably a bit much to wrap his head around.  ;)
0

LVL 74

Expert Comment

>>  in fact, the 'bin' folder. thanks.

The solution was intended as a quick-and-dirty and I didn't make clear where the images were coming from; my apologies. Glad you got the answer.

I would suggest taking some time to study Idle_Mind's solution as well. His solution has a bit more coder-friendly re-usability in it (class Card and use of Resources for example). Don't be discouraged by his use of BackgroundWorker. It is there (to make the GUI more responsive during the shuffle). If you didn't spawn a separate thread from you GUI (which is what BW does internally), then you would get the infamous "[foo] (Not Responding)" until your shuffle logic completed. Besides, it's freakin' Star Wars  ;)
0

## Featured Post

Microsoft Reports are based on a report definition, which is an XML file that describes data and layout for the report, with a different extension. You can create a client-side report definition language (*.rdlc) file with Visual Studio, and build g…
If you need to start windows update installation remotely or as a scheduled task you will find this very helpful.
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…