Memory Game

This is a simple Memory Game with random cards, the tutorial features message boxes, arrays and shapes using Windows Presentation Foundation (WPF).

www.cespage.com/vb/vb08tut10.html

Step 1

Start Microsoft Visual Basic 2008 Express Edition, then select File then New Project... Choose WPF Application from the New Project Window, enter a name for the Project and then click OK, see below:

New Project

Step 2

A Blank Window named Window1 should then appear, see below:

Window1

Step 3

Then from the Controls tab on the Toolbox select the Grid component:

Grid Component

Step 4

Draw a Grid on the Window or in the XAML Pane below the "<Grid>" type the following:

<Grid Margin="0,0,0,42" Name="Grid1" />

See below:

Window1 with Grid

Step 5

Select or click on the Grid then goto the Properties box, change the Name to "BoardLayout" without the quotes, see below:

Grid Properties

Step 6

Then from the Controls tab on the Toolbox select the Button component:

Button Component

Step 7

Draw a Button on the Window, by dragging the Button from the Toolbox onto the Window, or in the XAML Pane above the bottom "</Grid>" type the following:

<Button Margin="102,0,100,12" Name="Button1" Height="23" VerticalAlignment="Bottom">Button</Button> 

See below:

Window1 with Grid and Button

Step 8

Select or click on the Button (Button1), then goto the Properties box and change the Name to btnNew and the Content property from Button to New Game, see below:

btnNew Properties

Step 9

Right Click on the Window or the entry for "Window1" in the Solution Explorer and choose the "View Code" option then below "Class Window1" type the following:

Private WithEvents btn1, btn2, btn3, btn4, _
btn5, btn6, btn7, btn8 As New Button
Private WithEventsbtn9, btn10, btn11, btn12, _
btn13, btn14, btn15, btn16 As New Button
Private Moves As Integer = 0
Private FirstCardChoice As Integer = 0
Private SecondCardChoice As Integer = 0
Private FirstButton, SecondButton As Button
Private Board(4, 4) ' Board
Private Match As New ArrayList ' Matched

See Below:

Window1 Declarations

Step 10

While still in the Code View for Window1, above "End Class" type the following:

Private Sub Add(ByRef Grid As Grid, ByRef Button As Button, _
                ByRef Row As Integer, ByRef Column As Integer)
  Button.Margin = New Thickness(5)
  Button.Content = Nothing
  Grid.Children.Add(Button)
  Grid.SetColumn(Button, Column)
  Grid.SetRow(Button, Row)
End Sub

See Below:

Window1 Add Subroutine

Step 11

Again while still in the Code View for Window1, below the "End Sub" for "Private Sub Add(...)", type the following:

Private Sub Layout(ByRef Grid As Grid)
  Grid.ColumnDefinitions.Clear() ' Clear Columns
  Grid.RowDefinitions.Clear() ' Clear Rows
  Grid.Children.Clear() ' Clear the Grid
  Grid.ColumnDefinitions.Add(New ColumnDefinition)
  Grid.ColumnDefinitions.Add(New ColumnDefinition)
  Grid.ColumnDefinitions.Add(New ColumnDefinition)
  Grid.ColumnDefinitions.Add(New ColumnDefinition)
  Grid.RowDefinitions.Add(New RowDefinition)
  Grid.RowDefinitions.Add(New RowDefinition)
  Grid.RowDefinitions.Add(New RowDefinition)
  Grid.RowDefinitions.Add(New RowDefinition)
  Add(Grid, btn1, 0, 0)
  Add(Grid, btn2, 0, 1)
  Add(Grid, btn3, 0, 2)
  Add(Grid, btn4, 0, 3)
  Add(Grid, btn5, 1, 0)
  Add(Grid, btn6, 1, 1)
  Add(Grid, btn7, 1, 2)
  Add(Grid, btn8, 1, 3)
  Add(Grid, btn9, 2, 0)
  Add(Grid, btn10, 2, 1)
  Add(Grid, btn11, 2, 2)
  Add(Grid, btn12, 2, 3)
  Add(Grid, btn13, 3, 0)
  Add(Grid, btn14, 3, 1)
  Add(Grid, btn15, 3, 2)
  Add(Grid, btn16, 3, 3)
End Sub

See Below:

Window1 Layout Subroutine

Step 12

While still in the Code View for Window1, below the "End Sub" for "Private Sub Layout(...)", type the following:

Private Function Shape(ByRef Points As  _
                   System.Windows.Media.PointCollection) As Object
  Dim Polygon As New Polygon
  Polygon.Stretch = Stretch.Uniform
  Polygon.StrokeLineJoin = PenLineJoin.Round
  Polygon.Points = Points
  Polygon.Width = Polygon.Height
  Polygon.Stroke = Brushes.Black
  Polygon.StrokeThickness = 4.0
  Polygon.Margin = New Thickness(5)
  Return Polygon
End Function

See Below:

Window1 Shape Function

Step 13

Again while still in the Code View for Window1, below the "End Sub" for "Private Function Shape(...)", type the following:

Private Function Card(ByRef Type As Integer) As Object
  Dim Points As New System.Windows.Media.PointCollection
  Select Case Type
    Case 1 ' Circle
      Dim CircleGeo As New EllipseGeometry(New Rect(0, 0, 50, 50))
      Dim Circle As New Path
      Circle.Stretch = Stretch.Uniform
      Circle.Data = CircleGeo
      Circle.Stroke = Brushes.Black
      Circle.StrokeThickness = 4.0
      Circle.Margin = New Thickness(5)
      Return Circle
    Case 2 ' Cross
      Dim Lines As New Path
      Dim LineGroup As New GeometryGroup
      LineGroup.Children.Add(New LineGeometry(New Point(0, 0), New Point(50, 50)))
      LineGroup.Children.Add(New LineGeometry(New Point(50, 0), New Point(0, 50)))
      Lines.Stretch = Stretch.Uniform
      Lines.Data = LineGroup
      Lines.Stroke = Brushes.Black
      Lines.StrokeThickness = 4.0
      Lines.Margin = New Thickness(5)
      Return Lines
    Case 3 ' Triangle
      Points.Add(New Point(150, 0))
      Points.Add(New Point(0, 250))
      Points.Add(New Point(300, 250))
      Return Shape(Points)
    Case 4 ' Square
      Points.Add(New Point(0, 0))
      Points.Add(New Point(0, 100))
      Points.Add(New Point(100, 100))
      Points.Add(New Point(100, 0))
      Return Shape(Points)
    Case 5 ' Pentagon
      Points.Add(New Point(0, 125))
      Points.Add(New Point(150, 0))
      Points.Add(New Point(300, 125))
      Points.Add(New Point(250, 300))
      Points.Add(New Point(50, 300))
      Return Shape(Points)
    Case 6 ' Hexagon
      Points.Add(New Point(75, 0))
      Points.Add(New Point(225, 0))
      Points.Add(New Point(300, 150))
      Points.Add(New Point(225, 300))
      Points.Add(New Point(75, 300))
      Points.Add(New Point(0, 150))
      Return Shape(Points)
    Case 7 ' Star
      Points.Add(New Point(9, 2))
      Points.Add(New Point(11, 7))
      Points.Add(New Point(17, 7))
      Points.Add(New Point(12, 10))
      Points.Add(New Point(14, 15))
      Points.Add(New Point(9, 12))
      Points.Add(New Point(4, 15))
      Points.Add(New Point(6, 10))
      Points.Add(New Point(1, 7))
      Points.Add(New Point(7, 7))
      Return Shape(Points)
    Case 8 ' Rhombus
      Points.Add(New Point(50, 0))
      Points.Add(New Point(100, 50))
      Points.Add(New Point(50, 100))
      Points.Add(New Point(0, 50))
      Return Shape(Points)
    Case Else
        Return Type
  End Select
End Function

See Below:

Window1 Card Subroutine

Step 14

Again while still in the Code View for Window1, below the "End Function" for "Private Function Card(...)", type the following:

Private Function Random(ByRef Start As Integer, ByRef Finish As Integer, _
                        ByRef Total As Integer) As ArrayList
  Dim Selections As New ArrayList
  Dim Selection As Integer
  While Selections.Count < Total
    Randomize()
    Selection = Int((Finish * Rnd()) + Start) ' Random Selection
    If Not Selections.Contains(Selection) _
    Or Selections.Count < 1 Then ' If Not Selected
      Selections.Add(Selection) ' Add Selection
    End If
  End While
  Return Selections
  Selections = Nothing
End Function

See Below:

Window1 Random Function

Step 15

While still in the Code View for Window1, below the "End Function" for "Private Function Random(...)", type the following:

Private Sub Choose()
  Dim Numbers, Indices As New ArrayList ' Numbers, Index
  Dim Counter As Integer = 0
  While Numbers.Count < 17 ' Get 16 Numbers ( 2 x 8 )
    Dim Cards As ArrayList = Random(1, 8, 8) ' Random 1 - 8
    For Card As Integer = 0 To 7
      Numbers.Add(Cards.Item(Card)) ' Add to Cards
    Next
  End While
  Indices = Random(1, 16, 16) ' Random 1 - 16
  For Column As Integer = 0 To 3 ' Board Columns
    For Row As Integer = 0 To 3 ' Board Rows
      Board(Column, Row) = Numbers.Item(Indices(Counter) - 1)
      Counter += 1
    Next
  Next
End Sub

See Below:

Window1 Choose Subroutine

Step 16

While still in the Code View for Window1, below the "End Sub" for "Private Sub Choose()", type the following:

Private Sub NewGame()
  Moves = 0
  Match.Clear()
  Choose()
  Layout(BoardLayout)
  Me.Title = "Moves:0"
End Sub

See Below:

Window1 NewGame Subroutine

Step 17

Finally while still in the Code View for Window1, below the "End Sub" for "Private Sub NewGame()", type the following:

Private Sub OnClick(ByVal sender As System.Object, _
                         ByVal e As System.Windows.RoutedEventArgs) Handles _
    btn1.Click, btn2.Click, btn3.Click, btn4.Click, btn5.Click, _
    btn6.Click, btn7.Click, btn8.Click, btn9.Click, btn10.Click, _
    btn11.Click, btn12.Click, btn13.Click, btn14.Click, btn15.Click, _
    btn16.Click
  Dim Button As New Button
  Button = CType(sender, Button)
  If Match.IndexOf(Board(Grid.GetRow(Button), _
                         Grid.GetColumn(Button))) < 0 Then ' No Match
    If FirstCardChoice = 0 Then
      FirstButton = Button
      FirstCardChoice = Board(Grid.GetRow(Button), _
                              Grid.GetColumn(Button))
      Button.Content = Card(Board(Grid.GetRow(Button), _
                                  Grid.GetColumn(Button)))
    ElseIf SecondCardChoice = 0 Then
      SecondButton = Button
      If Not FirstButton.Equals(SecondButton) Then ' Different
        SecondCardChoice = Board(Grid.GetRow(Button), _
                                 Grid.GetColumn(Button))
        Button.Content = Card(Board(Grid.GetRow(Button), _
                                    Grid.GetColumn(Button)))
        If FirstCardChoice.Equals(SecondCardChoice) Then ' Compare
          Match.Add(FirstCardChoice)
          Match.Add(SecondCardChoice)
          MsgBox("Match!", MsgBoxStyle.Information, "Memory Game")
          If Match.Count = 16 Then
            MsgBox("Well Done! You got them all in " & _
                   Moves & " moves!", _
                   MsgBoxStyle.Information, "Memory Game")
            Layout(BoardLayout)
          End If
        Else ' Reset Buttons
          MsgBox("No Match", MsgBoxStyle.Exclamation, "Memory Game")
          FirstButton.Content = Nothing
          SecondButton.Content = Nothing
        End If
        Moves += 1
        Me.Title = "Moves:" & Moves
        FirstCardChoice = 0
        SecondCardChoice = 0
        FirstButton = Nothing
        SecondButton = Nothing
      End If
    End If
  End If
End Sub

See Below:

Window1 OnClick Subroutine

Step 18

Return to the Design view by selecting the [Design] tab or Right Click on the "View Designer" option in Solution Explorer for Window1. Double Click on the "New Game" Button (btnNew) and type the following in the btnNew_Click() Sub:

NewGame()

See Below:

Window1 New Game Click Event

Step 19

While still in Code View, if not Right Click on the Window or the entry for "Window1" in the Solution Explorer and choose "View Code". The top of this window will have two drop-down boxes one with "(General)" in and the other "(Declarations)", click on the first and select the "(Window1 Events)" Option, then from the drop-down next to this select "Loaded", type the following in the Window1_Loaded Sub:

NewGame()

See Below:

Window1 Loaded Event

Step 20

Save the Project as you have now finished the application, then click on Start:

Start

When you do the following will appear:

Application Running

Step 21

Click on the Buttons to show a Shape, match two shapes to make a pair, do this until all the cards are matched to win, see below:

Game Played

Step 22

Click on the Close button Close on the top right of Window1 to end the application.

This Memory Game uses shapes generates using the Card function, try changing it to use different shapes, colours or even patterns! You could also make them look more like cards or anything you like!