none
Create a Shuffle Game in VB RRS feed

  • Question

  • I would like to create a Shuffle Game in Visual Basic 2017 (Read to the end before you stop reading and exit out of here)

    This shuffle game contains

    One table layout panel (4x4)

    Fifteen buttons named from Button1 to Button15, and the button texts are named from 1 to 15, nested inside the table layout panel, and the sixteenth cell of the panel (the one in the bottom right corner) is an empty cell.

    One Shuffle button (makes all the buttons inside the panel move into random cells, except for the sixteenth cell, which remains blank), and reset click counts (if the game was previously played).

    When I click on a button inside the table layout panel, the button moves into the empty spot next to it in any direction (Up, Left, Right, Bottom), if there's no empty spot next to the button then nothing happens when I click on it.

    The shuffle game ends once all the numbers are in order from 1 to 15, and a message shows that the game has been completed, in whatever number of times I clicked the buttons, e.g. if I arranged all the numbers in order in 17 clicks, the message says: You completed this game in 17 clicks.

    I want to know how to do this, please tell me, and if any of you who answers the question think I need to add a button16 into the empty spot of the table layout panel and make the text of that button blank, tell me.

    P.S

    I have seen similar tutorials on how to create shuffle game in vb on youtube, but in those tutorials they worked through all the  button click event code, and they also worked through all the buttons in the checking the puzzle solved code (which I do not want to do because it would not be convenient).

    Thanks

    Friday, August 10, 2018 7:53 AM

All replies

  • Hi

    Would it be convenient to show the code you have so far?


    Regards Les, Livingston, Scotland

    Friday, August 10, 2018 11:13 AM

  • I have seen similar tutorials on how to create shuffle game in vb on youtube, but in those tutorials they worked through all the  button click event code, and they also worked through all the buttons in the checking the puzzle solved code (which I do not want to do because it would not be convenient).

    Thanks

    You'll need to use the Button.Click event handler to first move the button (if appropriate) and then check for the solution (if moved).  But there only needs to be one handler for all of the buttons since they all work the same way.  I'd expect this to be a short program.

    It sounds like this may be a homework question.  Are you doing this as a class assignment?

    Either way, you should show us what you've done to get started and then indicate the problem or part you would like to improve upon.

    -EDIT-

    If you really want to make it more efficient, don't use the buttons and table panel...  just draw the game yourself directly onto the form without any extra controls.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"


    Friday, August 10, 2018 12:14 PM
    Moderator
  • Hi

    This is not a homework question or assignment, I just want to know how to do it.

    The trouble is that I had to start from scratch because in the tutorials on youtube, they did not use table layout panel, but mine has the table layout panel. The table layout panel is there to hold the buttons(or whatever controls it might be, i.e, labels would do as well) so that the controls won't move beyond its range, like it will only move to the empty spot instead of going outside of the 4x4 range. I didn't code everything yet. 

    Here is a code I had already used, but I don't think it would work without button 16 (given that I had the table layout panel anyway, I want the button to move into empty cells instead of shuffling texts).

    Sub Shuffle()

    Dim a(15), i, j, RN As Integer
           

    Dim flag As Boolean

           
            flag = False
           

    i = 1
           

    a(j) = 1

           

    Do While i <= 15
              

      Randomize()
               

    RN = CInt(Int((15 * Rnd()) + 1))
               

    For j = 1 To i
                   

    If (a(j) = RN) Then
                      

      flag = True
                     

       Exit For
                   

    End If
               

    Next

               

    If flag = True Then
                   

    flag = False

               

    Else
                   

    a(i) = RN

                   

    i = i + 1

               

    End If
           

    Loop


    Form1.Button2.Text = a(2)

    Form1.Button3.Text = a(3)

    Form1.Button4.Text = a(4)

    Form1.Button5.Text = a(5)

    Form1.Button6.Text = a(6)

    Form1.Button7.Text = a(7)

    Form1.Button8.Text = a(8)

    Form1.Button9.Text = a(9)

    Form1.Button10.Text = a(10)

    Form1.Button11.Text = a(11)

    Form1.Button12.Text = a(12)

    Form1.Button13.Text = a(13)

    Form1.Button14.Text = a(14)

    Form1.Button15.Text = a(15)

    Form1.Button16.Text = "" (this one I didn't use because I want to move the button into the empty cell of the table layout panel instead of shuffling button texts)

    As you can see that the above code seemed to only 'swap' the button texts instead of moving it (there was no table layout panel in their code).

    Here is how they did it on youtube regarding moving the button click events (I didn't code this yet).

    Sub CheckButton()

    If Butt2.text = "" then

    butt2.text = butt1.text

    butt2.text = ""

    End if

    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs)

    'Check button 2,5

    Checkbutton(Button1,Button2)

    Checkbutton(Button1,Button5)

    Checksolved()

    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs)

    'Checkbutton 1,3,6

    Checkbutton(Button2,Button1)

    Checkbutton(Button2,Button3)

    Checkbutton(Button2,Button6)

    End Sub

    Private Sub Button3_Click(sender As Object, e As EventArgs)

    'Checkbutton 2,7,4 (same event as last two, but checking different buttons, all the following button click statement are the same thing)

    End Sub

    Private Sub Button4_Click(sender As Object, e As EventArgs)

    'Checkbutton 3,8

    End Sub

    Private Sub Button5_Click(sender As Object, e As EventArgs)

    'Checkbutton 1,6,9

    End Sub

    Private Sub Button6_Click(sender As Object, e As EventArgs)

    'Checkbutton 2,5,10,7

    End Sub

    Private Sub Button7_Click(sender As Object, e As EventArgs)

    'Checkbutton 3,6,11,8 End Sub

    Private Sub Button8_Click(sender As Object, e As EventArgs) 

    'Checkbutton 4,7,12 

    End Sub

    Private Sub Button9_Click(sender As Object, e As EventArgs)

    'Checkbutton 5,10,13 End Sub

    Private Sub Button10_Click(sender As Object, e As EventArgs)

    'Checkbutton 9,11,6,14

    End Sub

    Private Sub Button11_Click(sender As Object, e As EventArgs)

    'Checkbutton 10,12,7,15

    End Sub

    Private Sub Button12_Click(sender As Object, e As EventArgs)

    'Checkbutton 8,11,16

    End Sub

    Private Sub Button13_Click(sender As Object, e As EventArgs)

    'Checkbutton 9.14

    End Sub

    Private Sub Button14_Click(sender As Object, e As EventArgs)

    'Checkbutton 13,10,15

    End Sub

    Private Sub Button15_Click(sender As Object, e As EventArgs)

    'Checkbutton 14,11,16

    End Sub

    Private Sub Button16_Click(sender As Object, e As EventArgs)

    'Checkbutton 12,15

    End Sub

    And also in their description, they used CheckSolved, in the following way.

    Sub CheckSolved()

    If Form1.Button1.text = "1" and Form1.button2.text = "2" and Form1.button3.text = "3" and form1.button4.text = "4" and form1.button5.text = "5" and form1.button6.text = "6" and form1.button7.text = "7" and form1.button8.text = "8" and form1.button9.text = "9" and form1.button10.text = "10" and form1.button11.text = "11" and form1.button12.text = "12" and form1.button13.text = "13" and form1.button14.text = "14"and form1.button15.text = "15" then Msgbox(statement that the game is complete in Number of clicks) End Sub See the above codes?

    The point is that I don't want to do it this way, it would waste too much time. I just have no idea how to code this in the easy way. I have a table layout panel anyway, so even the code that randomize the button texts may not work as I expect as I want the controls to shift rather than changing texts.

    When you say 'draw the game directly onto the form without any extra controls', are you saying that I should design the controls in code instead of manually creating them in the design view? Or does it mean something else?

    I would be glad to try that if it is more efficient.

    By the way, something I forgot to mention, cell 16 (the cell at the bottom right corner) remains empty when the numbers are shuffled, and when the game is complete, the only time cell 16 would not be empty is when I start moving my controls into it

    By the way, it seems that if I draw it in, I will most likely have to dynamically create it. in this case there would be no texts, so I would have to start from scratch anyway.




    Friday, August 10, 2018 10:37 PM
  • I had whipped this up this morning after the first reply... I was waiting for you to get back before showing it.

    You can start a new project and just paste this over the default Form1 code then hit run (there's nothing to do in the designer):

    Public Class Form1
        Private tileSize As Single
        Private gridSize As Integer = 4
        Private moveCount As Integer
        Private tiles As New Dictionary(Of Point, Integer?)
        Private random As New Random
        Private nullTile As Point
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Me.ClientSize = New Size(600, 600)
            Me.DoubleBuffered = True
            ResetPuzzle()
        End Sub
    
        Private Sub Form1_Resize(sender As Object, e As EventArgs) Handles Me.Resize
            Me.ClientSize = New Size(Me.ClientSize.Width, Me.ClientSize.Width)
            tileSize = Me.ClientSize.Width / gridSize
            Invalidate()
        End Sub
    
        Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
            e.Graphics.Clear(Color.White)
            For Each p In tiles.Keys
                If tiles(p) IsNot Nothing Then
                    Dim bounds = New Rectangle(p.X * tileSize, p.Y * tileSize, tileSize, tileSize)
                    ControlPaint.DrawButton(e.Graphics, bounds, ButtonState.Normal)
                    TextRenderer.DrawText(e.Graphics, tiles(p).ToString, Me.Font, bounds, Me.ForeColor, Me.BackColor, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)
                End If
            Next
        End Sub
    
        Private Sub Form1_MouseUp(sender As Object, e As MouseEventArgs) Handles Me.MouseUp
            Dim setNullTile = Sub(np As Point)
                                  tiles(nullTile) = tiles(np)
                                  tiles(np) = Nothing
                                  nullTile = np
                                  moveCount += 1
                              End Sub
            Dim p = New Point(Math.Floor(e.X / tileSize), Math.Floor(e.Y / tileSize))
            If p = nullTile Then Exit Sub
            If nullTile.X = p.X - 1 AndAlso nullTile.Y = p.Y Then
                setNullTile(p)
            ElseIf nullTile.X = p.X + 1 AndAlso nullTile.Y = p.Y Then
                setNullTile(p)
            ElseIf nullTile.X = p.X AndAlso nullTile.Y = p.Y - 1 Then
                setNullTile(p)
            ElseIf nullTile.X = p.X AndAlso nullTile.Y = p.Y + 1 Then
                setNullTile(p)
            End If
            Invalidate()
            If CheckPuzzleComplete() Then
                MessageBox.Show($"You win in {moveCount} moves!", "Game Over")
                ResetPuzzle()
            End If
        End Sub
    
        Private Function CheckPuzzleComplete() As Boolean
            If Not (nullTile.X = gridSize - 1 AndAlso nullTile.Y = gridSize - 1) Then Return False
            For i = 0 To tiles.Keys.Count - 2
                If Not tiles(tiles.Keys.ElementAt(i + 1)) = tiles(tiles.Keys.ElementAt(i)) + 1 Then Return False
            Next
            Return True
        End Function
    
        Private Sub ResetPuzzle()
            Dim values = Enumerable.Range(1, gridSize * gridSize - 1).ToList
            For y = 0 To gridSize - 1
                For x = 0 To gridSize - 1
                    If values.Count = 0 Then Exit For
                    Dim idx = random.Next(values.Count)
                    tiles(New Point(x, y)) = values(idx)
                    values.RemoveAt(idx)
                Next
            Next
            nullTile = New Point(gridSize - 1, gridSize - 1)
            tiles(nullTile) = Nothing
            moveCount = 0
            Invalidate()
        End Sub
    End Class

    This is what I meant by drawing it yourself directly onto the form.  There are no controls to worry about.  The code just uses GDI to draw some boxes (I used the button rendering just to give a similar look as you would get using button controls but you could really just draw rectangles and fill them however you want).

    Note that this code is for VS2017 and if you are using an earlier version there may be some minor refactoring.

    -EDIT-

    Forgot to mention, you can change the default value of the gridSize variable to make puzzles of a different size.  The default is a 4x4 puzzle.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"


    Friday, August 10, 2018 11:36 PM
    Moderator
  • This worked, but the tile size is too big, and not to mention that the font size of each tile is too small to read. how do I set the font size of each tile? Maybe implement some other font properties to the tile?



    Saturday, August 11, 2018 12:20 AM
  • Look at the code... you can make all kinds of adjustments.

    The tile size is based on the form size.  Resize the form and the tile size will change.

    The text is being drawn using the form's font.  Change the font of the form and the text will change.  Or edit the code to use a different font instance than Me.Font.

    Those things should be pretty easy to spot if you take a moment to go through the code line-by-line.  If you have questions about the logic or how part of it works, that I can take more time to explain as needed.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Saturday, August 11, 2018 12:29 AM
    Moderator
  • You could also build a form with a Panel control on it and move the code in Form.Paint into Panel1.Paint (or whatever you name the panel).  You could then modify the sizing code to calculate the tile size based on the size of the panel instead of the size of the form, or code the tiles to be a specific size and adjust the size of the panel to fit the tiles.

    You have lots of options depending on what you want to do.  This was just meant to show the basic logic and steps you could use to make the puzzle work.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Saturday, August 11, 2018 12:31 AM
    Moderator
  • Ok, I got it, I managed to change the form text so it is easier to read, thanks.
    Saturday, August 11, 2018 12:40 AM
  • Here is an example based on service pack's original idea of just using buttons and layout table.

    The concept works well as the buttons are the class structure that stores the data we need in each grid cell and allows us to move that class object and do other with it.

    I agree with Reed that draw your own is best in many cases however for this game one can argue it is not necessarily. And it also depends on the intent which if you are a beginner then using buttons for this is a good exercise in how things work.

    This example is not complete etc but shows some ways to do it with buttons and a layout panel.

    The example makes all the controls. All you need to do is paste the code in an empty form to run it.

    Edit v2: added random setup and image before and after shown.

    Public Class Form4
    
        Private WithEvents tlp1 As New TableLayoutPanel With {.Parent = Me, .Dock = DockStyle.Fill,
            .ColumnCount = 4, .RowCount = 4, .BackColor = Color.Red, .ForeColor = Color.AntiqueWhite}
        Private GridTotal As Integer = 4
        Private ButtonSize As Integer = 60
        Private BmpSource As Bitmap = New Bitmap("c:\bitmaps\rusty.jpg")
    
        Private Sub Form4_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim r, c As Integer
            Dim rand As New Random(Now.Millisecond)
            Dim btn2 As New Control
    
            For i As Integer = 0 To (GridTotal * GridTotal) - 2
    
                Dim Btn As New Button
                With Btn
                    .Name = "Button" & i.ToString
                    .Size = New Size(ButtonSize, ButtonSize)
                    .Font = New Font("tahoma", CInt(0.25 * ButtonSize), FontStyle.Bold)
                    .Tag = IndexToColRow(i)         'col row position of button in layout table
                    .Text = i.ToString '& vbLf & .Tag.ToString
                    .FlatStyle = FlatStyle.Flat
                    .FlatAppearance.BorderSize = 0
                    .Margin = New Padding(0, 0, 0, 0)
                    .Padding = New Padding(0, 0, 0, 0)
    
                    AddHandler Btn.Click, AddressOf Button_Click
    
                    'add the images
                    Dim w As Integer = CInt(BmpSource.Width / GridTotal)
                    Dim h As Integer = CInt(BmpSource.Height / GridTotal)
                    Dim destRect As New Rectangle(0, 0, ButtonSize, ButtonSize)
                    Dim sourceRect As New Rectangle(c * w, r * h, w, h)
                    Dim bmp As New Bitmap(ButtonSize, ButtonSize)
    
                    Using g As Graphics = Graphics.FromImage(bmp)
                        g.DrawImage(BmpSource, destRect, sourceRect, GraphicsUnit.Pixel)
                    End Using
    
                    .Image = bmp
                    .BackgroundImageLayout = ImageLayout.None
    
                    'add to table layout at position c,r
                    c = CInt(Btn.Tag.ToString.Substring(0, 1))
                    r = CInt(Btn.Tag.ToString.Substring(1, 1))
                    tlp1.SetColumn(Btn, c)
                    tlp1.SetRow(Btn, r)
                    tlp1.Controls.Add(Btn)
    
                End With
            Next
    
            'radomize the positions
            For i As Integer = 0 To 15
                c = rand.Next(0, 3)
                r = rand.Next(0, 3)
    
                btn2 = tlp1.GetControlFromPosition(c, r)
                If btn2 IsNot Nothing Then
                    c = rand.Next(0, 3)
                    r = rand.Next(0, 3)
                    tlp1.SetColumn(btn2, c)
                    tlp1.SetRow(btn2, r)
                End If
            Next
    
        End Sub
    
        Private Function IndexToColRow(thisindex As Integer) As String
            Dim x As Double = thisindex / GridTotal
            Dim r As Integer = CInt(Math.Floor(x))
            Dim c As Integer = CInt(GridTotal * (x - r))
            IndexToColRow = c.ToString & r.ToString
        End Function
    
        Private Sub Button_Click(sender As Object, e As EventArgs)
            Dim btn As Button = DirectCast(sender, Button)
    
            'find the empty square
            Dim r, c As Integer
            Dim exitflag As Boolean
    
            For c = 0 To GridTotal - 1
                For r = 0 To GridTotal - 1
                    If tlp1.GetControlFromPosition(c, r) Is Nothing Then
                        exitflag = True
                        Exit For
                    End If
                Next
                If exitflag Then Exit For
            Next
            'move the clicked button to empty square
            tlp1.SetColumn(btn, c)
            tlp1.SetRow(btn, r)
        End Sub
    End Class
    


    Saturday, August 11, 2018 3:20 PM
  • Hi Tommy!

    One problem with your example... its in the first move you make in the GIF.  You can't pop 6 down to the blank... the tiles can only move if they are adjacent to the empty space.

    Did you ever play with the plastic version of this?  The tiles are all fixed in a frame with tongue-and-groove so they can only slide into the empty space.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Saturday, August 11, 2018 4:07 PM
    Moderator
  • Hi Tommy!

    One problem with your example... its in the first move you make in the GIF.  You can't pop 6 down to the blank... the tiles can only move if they are adjacent to the empty space.

    Did you ever play with the plastic version of this?  The tiles are all fixed in a frame with tongue-and-groove so they can only slide into the empty space.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"


    Reed,

    Yes I have used the plastic ones.

    My fav was a USA with all 48 states. Yes 48. What are they called? Plastic sliding thing.

    I did not attempt to write the game logic its just an example. I just realized in starting a graphic version that I was making a button class more or less so I pursued that as OP first wondered.

    And also figured similar to your example.

    Saturday, August 11, 2018 5:09 PM
  • Reed,

    Yes I have used the plastic ones.

    My fav was a USA with all 48 states. Yes 48. What are they called? Plastic sliding thing.

    I did not attempt to write the game logic its just an example. I just realized in starting a graphic version that I was making a button class more or less so I pursued that as OP first wondered.

    And also figured similar to your example.

    Ah, OK.  And I have no idea if they even have a proper name beyond "sliding puzzle" :P

    And yes, I think that between the two examples it shouldn't be too difficult to take the game logic I laid out and translate it into the button movement you demonstrated. :)



    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Saturday, August 11, 2018 6:35 PM
    Moderator
  • Hi Tommy, I just modified the code you posted a little bit, I added a + 1 after the .text= i.ToString so the numbers become 1 to 15 instead of 0 to 14.

    When I clicked on any numbers it just went into the empty spot even if it is not adjacent to the empty spot, I shall look into this problem and try to figure out what to do.

    The buttons didn't move into random locations when the form was loaded either, so I may have to do something about this as well.

     



    Sunday, August 12, 2018 1:57 AM
  • Hi Tommy, I just modified the code you posted a little bit, I added a + 1 after the .text= i.ToString so the numbers become 1 to 15 instead of 0 to 14.

    When I clicked on any numbers it just went into the empty spot even if it is not adjacent to the empty spot, I shall look into this problem and try to figure out what to do.

    The buttons didn't move into random locations when the form was loaded either, so I may have to do something about this as well.

     




    Yes one can add the things according to ones desires.

    I added a random setup and an image to the button example.

    :)


    Sunday, August 12, 2018 4:24 AM