locked
How to generate a random number sequence, assign the numbers and their positions a variable, and simplify the expression of a statement that would otherwise repeat millions of times

    Question

  • I started writing a matching game in Visual Basic 9, using 24 different pictures, each to repeat once.

    My initial method was to create on each of the 24 images on each of the 48 positions set as .visible = False. Along with an inverted version of them. With a background image, named bb1 through bb48 acting as the button on click. Along with an extra image of each of the 24  placed at the back, whose visible or not status would not be seen by the user,  on each of 48 positions there would be 73 objects. (All of that I know how to do, just saying how I am doing it so people know what I am trying to do)

    The idea i had was to use a random number sequence. Assign each of the numbers a variable, and each position of the numbers a variable.

    So, for example, if it was 1 through 10, labeled aaa through jjj and it went 10, 3, 5, etc. and the first position in the number sequence was named aaaa and last jjjj, then I imagine it would be...

    If aaa = aaaa then
    'do this
    elseif
    aaa = bbbb then
    'do this

    etc.

    And using the random number sequence as a way to randomly define which of the images are made visible at start. I would use the "extra" images to be made visible, which the user doesn't see. So when clicking, saying, bb1,

    if sunflower_1_extra.visible = true then
    sunflower_1.visible = true
    bb1.visible = false.


    Sunflower_1 being the inverted image. Then when the user clicks a matching image, it would make sunflower_1.visible = false along with whatever other sunflower is visible, and then make sunflower1.visible = true along with the other sunflower

    I have the basics worked out in my head how I want this to work, but I have two problems that I need help with.

    The first is, I can't figure out how to generate a random non repeating sequence of numbers and assign each of those numbers a variable and each position in the sequence a variable. That's how I would randomize the images.

    The second problem is this...

    I did the math, to see what I'd have to type in to tell the program how many possible combinations of 2 there would be. There are 1260 possible combinations of 2 for a single image. and 30,240 possible positions for all 24 in matching pairs. Then there are about 1 million 2 hundred thousand posible positions for non matching pairs.

    I clearly can't just type in...

    if sunflower_1.visible = true AND sunflower_2.visible = true
    then
    elseif
    sunflower_1.visible = true AND sunflower_3.visible = true

    and so on and so forthlike that 30 thousand times followed by

    if sunflower_1.visible - true and tire_2.visible = true then

    1 million 2 hundred thousand times.


    So I need a simpler way of expressing that. Since all the images that are the same, have the same name aside from a number at the end, I was hoping there was some way of saying something like:

    if sunflower_*.visible = true

    or something like that. Or maybe a way of grouping all images of the same kind together inside a function so I could say something like, if any image inside sunflower() is visible then do this.

    But I can't seem to figure out the best way to do that.

    So in all, I need to figure out how to do the random number sequence with variables, and a simpler way of expressing things that repeat with only one number difference.

    Any help would be appreciated. thanks.


    Sunday, May 03, 2009 7:36 PM

Answers

  • So you've probably realized that creating 73 times 48 images and writing all the code is not a very good way to do this. I would recommend creating only 48 PictureBoxes, all visible, and only change the image they display as needed. Of course you'd have to store some additional information about your PictureBoxes in local variables, probably an array of length 48 with all the state information that you need.

    There are some more advanced techniques for generating non-repeating random sequences but if you only need a short one you can get away with this code:

            Dim used(23) As Boolean
            Dim result(23) As Integer
            Dim rand As New Random()
            For i As Integer = 0 To 23
                used(i) = False
            Next
            For i As Integer = 0 To 23
                Dim r As Integer
                ' This will generate a random number that has not been used yet
                Do
                    r = rand.Next(24)
                Loop Until Not used(r)
                result(i) = r
                used(r) = True
            Next
    • Marked as answer by Yichun Feng Friday, May 08, 2009 2:40 AM
    Sunday, May 03, 2009 9:12 PM
  • Ok, so step #1. If you don't want to keep the images separately from your exe file, you can add them to your project as resources. The easiest way to do this is to go to My Project (in Solution Explorer) -> Resources -> Add Resource (click the dropdown arrow) -> Add Existing File... and add all your images to your project here. You can rename them to whatever you like. Then you can access them from the program like this:

    PictureBox1.Image = My.Resources.NameOfResource
    • Marked as answer by Yichun Feng Friday, May 08, 2009 2:40 AM
    Sunday, May 03, 2009 10:40 PM
  • Now finally how do we actually use these pictureBoxes and cardImage arrays? Suppose that all the picture boxes start with the back of the card image and what we want to do is flip the card when user clicks it. (So if the card shows back image, it will change to card image and vice versa.) This is how to do it:

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ...
        For i As Integer = 1 To 48
            AddHandler pictureBoxes(i).Click, AddressOf PictureBox_Click
        Next
    End Sub

    Private Sub PictureBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        For i As Integer = 1 To 48
            If sender Is pictureBoxes(i) Then
                Flip(i)
            End If
        Next
    End Sub

    Private Sub Flip(ByVal i As Integer)
        If pictureBoxes(i).Image Is cardImage(i) Then
            pictureBoxes(i).Image = My.Resources.BackImage
        Else
            pictureBoxes(i).Image = cardImage(i)
        End If
    End Sub

    The AddHandler call registers the PictureBox_Click method as event handler for clicking on pictureBox(i). So whenever user clicks on any of the pictureboxes, PictureBox_Click will be called, and the sender argument will contain pointer to whichever picture box was clicked. Now in PictureBox_Click we run a loop to figure out the index of clicked picture box and call the Flip method on it. The Flip method will then examine the image of the picture box and change it appropriately.
    • Marked as answer by Yichun Feng Friday, May 08, 2009 2:41 AM
    Sunday, May 03, 2009 11:20 PM

All replies

  • So you've probably realized that creating 73 times 48 images and writing all the code is not a very good way to do this. I would recommend creating only 48 PictureBoxes, all visible, and only change the image they display as needed. Of course you'd have to store some additional information about your PictureBoxes in local variables, probably an array of length 48 with all the state information that you need.

    There are some more advanced techniques for generating non-repeating random sequences but if you only need a short one you can get away with this code:

            Dim used(23) As Boolean
            Dim result(23) As Integer
            Dim rand As New Random()
            For i As Integer = 0 To 23
                used(i) = False
            Next
            For i As Integer = 0 To 23
                Dim r As Integer
                ' This will generate a random number that has not been used yet
                Do
                    r = rand.Next(24)
                Loop Until Not used(r)
                result(i) = r
                used(r) = True
            Next
    • Marked as answer by Yichun Feng Friday, May 08, 2009 2:40 AM
    Sunday, May 03, 2009 9:12 PM

  • Yeah. lol. Not to mention it makes the file size rather big. All the picture boxes would be the same physical size anyway and it's only 73 images to be used, so how would I go about making the program detect which image is used and change the image displayed? And is there a way to do this, so when it is built there is only the .exe, and not needing to install a set of folders?

    Thanks for the code. That looks like what I need. But how to I make that code write one of it's values to each of 24 variables? So each variable means a different number depending on the order in which the numbers generate?

    aaa1 = first number in sequence
    bbb2 = second number in sequence

    and so on and so forth?
    Sunday, May 03, 2009 9:51 PM
  • Ok, so step #1. If you don't want to keep the images separately from your exe file, you can add them to your project as resources. The easiest way to do this is to go to My Project (in Solution Explorer) -> Resources -> Add Resource (click the dropdown arrow) -> Add Existing File... and add all your images to your project here. You can rename them to whatever you like. Then you can access them from the program like this:

    PictureBox1.Image = My.Resources.NameOfResource
    • Marked as answer by Yichun Feng Friday, May 08, 2009 2:40 AM
    Sunday, May 03, 2009 10:40 PM
  • Ok let's go on. So I would start by adding 48 images to the form -- let's say they're called PictureBox1, ... PictureBox48. You don't want to work with them separately (i.e. write separate code for each one of them) so let's make an array of PictureBoxes:

    Dim pictureBoxes(48) As PictureBox

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        pictureBoxes(1) = PictureBox1
        ...
        pictureBoxes(48) = PictureBox48
    End Sub

    This code declares an array of picture boxes -- but this array doesn't contain any picture boxes yet, it's only filled with null pointers. So what we do in the Form.Load event is we fill the array with the picture boxes from our form. Remember that these are just pointers to picture boxes, not actual separate instances of PictureBox class -- that's why we can do pictureBoxes(1) = PictureBox1 etc.
    To avoid writing 48 lines to fill the array, you can also do this in a for loop:

    Dim pictureBoxes(48) As PictureBox

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        For i As Integer = 1 To 48
            pictureBoxes(i) = Me.Controls("PictureBox" & i.ToString())
        Next
    End Sub

    (You may need to use an additional DirectCast call if you have Option Strict turned on -- which I'd recommend.)
    Sunday, May 03, 2009 10:51 PM
  • Now let's generate the random sequence. In my original post I didn't realize that you actually need to repeat every number twice but that can be easily fixed. This function will return an array of integers 48 elements long, containing numbers 1-24 each twice. (The original code also used zero based indexing which I'm now changing to 1-based, hope it won't confuse you :).)

        Private Function RandomSequence() As Integer()
            Dim rand As New Random()
            Dim result(48) As Integer
            Dim used(24) As Integer
            For i As Integer = 1 To 24
                used(i) = 0
            Next
            For i As Integer = 1 To 48
                Dim r As Integer
                Do
                    r = 1 + rand.Next(48)
                Loop Until used(r) < 2
                result(i) = r
                used(r) += 1
            Next
            Return result
        End Function
    Sunday, May 03, 2009 11:00 PM
  • What do we do with this sequence? We will remember for each picture box which image it's supposed to hold:

    Dim cardImage(48) As Image

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ...
        Dim seq As Integer()
        seq = RandomSequence()
        For i As Integer = 1 To 48
            cardImage(i) = My.Resources.ResourceManager.GetObject("Image" & seq(i).ToString())
        Next
    End Sub

    The i-th element of cardImage array tells us which image should picture box number i show. I'm using My.Resources.ResourceManager.GetObject("resource name") to get resource called "resource name" because we cannot call My.Resources.ResourceName for obvious reasons. (ResourceName is not known at compile time.) Here I assume that your resources are called Image1, ... Image24.
    Sunday, May 03, 2009 11:09 PM
  • Now finally how do we actually use these pictureBoxes and cardImage arrays? Suppose that all the picture boxes start with the back of the card image and what we want to do is flip the card when user clicks it. (So if the card shows back image, it will change to card image and vice versa.) This is how to do it:

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ...
        For i As Integer = 1 To 48
            AddHandler pictureBoxes(i).Click, AddressOf PictureBox_Click
        Next
    End Sub

    Private Sub PictureBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        For i As Integer = 1 To 48
            If sender Is pictureBoxes(i) Then
                Flip(i)
            End If
        Next
    End Sub

    Private Sub Flip(ByVal i As Integer)
        If pictureBoxes(i).Image Is cardImage(i) Then
            pictureBoxes(i).Image = My.Resources.BackImage
        Else
            pictureBoxes(i).Image = cardImage(i)
        End If
    End Sub

    The AddHandler call registers the PictureBox_Click method as event handler for clicking on pictureBox(i). So whenever user clicks on any of the pictureboxes, PictureBox_Click will be called, and the sender argument will contain pointer to whichever picture box was clicked. Now in PictureBox_Click we run a loop to figure out the index of clicked picture box and call the Flip method on it. The Flip method will then examine the image of the picture box and change it appropriately.
    • Marked as answer by Yichun Feng Friday, May 08, 2009 2:41 AM
    Sunday, May 03, 2009 11:20 PM
  • I hope that you'll be able to extend this example to whatever more complicated things you need to do in your application. If you have any questions, don't hesitate to ask. Some parts of my code won't compile with Option Strict On but you can easily fix that by proper casting. My code is also not very efficient but it should be fast enough for 48 images. If you need a faster solution, I can show you some more advanced techniques.
    Sunday, May 03, 2009 11:24 PM
  • Thank you so much for such a detailed explanation!

    So what would I do if I wanted the images to be contained inside the .exe, instead of using My.Resources.NameOfResource ? Or would the above examples do that too?

    But ya, this information will help me do allot of different projects I had in mind much more efficiantly. You just saved me millions upon millions of lines of code :P

    Edit: oh, I read the first step wrong, it does what I want. Silly me. :P
    Sunday, May 03, 2009 11:37 PM
  • ok, one small problem...

    My


    .Resources.BackImage

    produces an error:

    Error 1 'BackImage' is not a member of 'Resources'.

    EDIT: When I changed "backimage" to bb (the name of the image) it worked fine.

    Now there are no errors that appear on the list, but when I click to run it as a test before doing anything major with it, It stops responding and a line is highlighted yellow.

    Loop Until used(r) < 2

    "Index was outside the bounds of the array."

    I am not sure what I did wrong with that. I'll paste the full code being used as it is right now:


    _______

    The pictures boxes are named bb1 through bb48. The images are named Rune_in1.JPG through Rune_24.JPG
    Public Class matching_runes
    
        Dim bb(48) As PictureBox
        Dim rune_in(48) As Image
    
    
        Private Sub matching_runes_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    
            For i As Integer = 1 To 48
                bb(i) = Me.Controls("bb" & i.ToString())
            Next
    
            Dim seq As Integer()
            seq = RandomSequence()
            For i As Integer = 1 To 48
                rune_in(i) = My.Resources.ResourceManager.GetObject("Image" & seq(i).ToString())
            Next
    
            For i As Integer = 1 To 48
                AddHandler bb(i).Click, AddressOf PictureBox_Click
            Next
        End Sub
    
        Private Sub PictureBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
            For i As Integer = 1 To 48
                If sender Is bb(i) Then
                    Flip(i)
                End If
            Next
        End Sub
    
        Private Sub Flip(ByVal i As Integer)
            If bb(i).Image Is rune_in(i) Then
                bb(i).Image = My.Resources.bb
            Else
                bb(i).Image = rune_in(i)
            End If
        End Sub
    
    
        Private Function RandomSequence() As Integer()
            Dim rand As New Random()
            Dim result(48) As Integer
            Dim used(24) As Integer
            For i As Integer = 1 To 24
                used(i) = 0
            Next
            For i As Integer = 1 To 48
                Dim r As Integer
                Do
                    r = 1 + rand.Next(48)
                Loop Until used(r) < 2
                result(i) = r
                used(r) += 1
            Next
            Return result
        End Function
    
    End Class



     



    Monday, May 04, 2009 12:48 AM
  • Well I assumed that you'll add a resource called BackImage to your program. (The bicture of the back of your card.)
    Monday, May 04, 2009 1:09 AM

  • The main thing you taught me here is about using (i) for a long list of numbers. Makes things so much easier. That will realy help me when it comes to defining possible combinations.

    But... what caused the outside of bounds thing?
    Monday, May 04, 2009 1:29 AM
  • Hm that's because the rand.Next line should in fact read:

    r = 1 + rand.Next(24)

    My mistake. (You only want to generate numbers from 1 to 24, not from 1 to 48.)
    Monday, May 04, 2009 1:34 AM
  • ahh. I see. thanks.

    Running the program with just that change and nothing else, it's very interesting, that flip function. And much faster than I would have expected.

    Right now the only problem I have with it is that when I click the image, the background color is used. No image appears. Clicking again reverses that though.

    So something is wrong with the rune_in(i) somewhere I think. I changed a line to:

    rune_in(i) =

    My.Resources.ResourceManager.GetObject("rune_in" & seq(i).ToString())

    but no change. Also tried changing background color, that only changed what it changes to, and tried building the application to see if maybe it would display different. I double checked file names, also checked to see if maybe it doesn't like underscore, nothing changed. What's wrong?

    I am pretty sure I can then apply what you have shown me above to make it a real matching game. There are quite a few things it will need after the core of it is working. But these methods make quite a difference.

    Monday, May 04, 2009 2:38 AM
  • In that case you must be using wrong resource names. Make sure that you pass in the GetObject function the **resource names** of your resources, i.e. the text you see in the Resources window below your images, not the file names.
    Monday, May 04, 2009 3:10 AM
  • oh, I see now.. THANKS! It works now!


    Now I just need to write how the program will behave in a number of circumstance... but what I'd expect doesn't work;

    If sender Is bb(i) And bb(i).Image <> rune_in(i) Then

    That line produces an error when I try it:

    Error 1 Operator '<>' is not defined for types 'System.Drawing.Image'

    And it says something similar whatever I try. I also tried putting pieces of the program into functions so I could refer to them optionally but then pieces of them don't want to work.

    In the old method, I would say something like, if .visible = false, or something. What would be the correct way of phrasing if a given image is used, so I can have the program do something when there is a match or mismatch.

    EDIT: Also, this really weird thing happened. Although the program builds ok, and runs ok, I can no longer directly edit images. "Value does not fall within expected range." Which is very weird. Why would it prevent me from making any changes to where they would be an error? Anyway, it's a good thing I back up the entire project folder every few changes. I was able to open a previous version, and just copy and paste the code and the images, and it worked fine without that error. It's just really weird it happened at all.


    Monday, May 04, 2009 3:28 AM
  • So comparing the images, you have to use the Is operator:

    If bb(i).Image Is rune_in(i) Then ...

    If Not bb(i).Image Is rune_in(i) Then ...

    Also if you're using I think .net 2.0 or newer, you can use IsNot instead:

    If bb(i).Image IsNot rune_in(i) Then ...

    I'm not sure what you mean by directly editing the images. The images you added to your project should be copied somewhere to your project folder, you must edit them there. The "Value does not fall within expected range" sounds like some runtime exception, it would be useful to know on which line this happens.
    Monday, May 04, 2009 8:53 AM
  • Thanks so much for the info!  Is and IsNot. yeah. I am using 3.5 actually. I figured if I am going to learn a new language might as well start with that. I'm going to have allot of fun trying out the different uses of these new (to me) commands.

    oh, that, I meant the display. um... the GUI where you position objects on the frame, etc. The actual error did not occur at all within the built application, or when running it in debug. The error only occured when trying to view the form within Visual Basic 2008 Express Edition. Just a weird error in Microsoft's product I was remarking about that made no sense.

    Monday, May 04, 2009 8:53 PM
  • ok, I ran into just one more problem...

    Is there a way to have the program detect how many of a given image is displayed at a given time?

    I know I can now easily say rune_(i) to refer to all of the rune images 1 through 48. And I can also say

    If

    bb1.Image Is My.Resources.rune_in1 And bb2.Image Is My.Resources.rune_in1 Then


    But that still leaves me with quite a few combinations going at it one at a time. Do you know of any methods for simply saying, if this image is used twice, or if this image is used exactly so many times, etc? If that's possible that would make TONS of game ideas Soooo much easier.
    • Edited by Elliander Monday, May 04, 2009 9:47 PM
    Monday, May 04, 2009 9:44 PM

  • I had an idea on how I could do that...

    I could create an invisible text box for each of the 25 pictures (including back image) and have a value written to these each time a card is flipped, so that way, a match would equal "2" in a given text, and that way I could have the program behave differently depending on what image is visible.

    I can think of a way that method could be simpler, but is there a way to add a text file to my resources, give each line in the file a value, and have them all read and write to their own lines in the given file?

    And is this idea the simplest way to do what I want, or is there an easier way?

    I don't think that idea would work for a game like tic tac toe or 4 in a row, but certainly would work where the match can be in any position, and certainly better than declaring every possible combination.
    Tuesday, May 05, 2009 1:20 AM
  • Let me see if I understand this right, you want to check if there are two flipped images with the same picture on them? You could do it in a loop like this:

            For i As Integer = 1 To 48
                For j As Integer = 1 To 48
                    If i <> j And bb(i).Image Is bb(j).Image And bb(i).Image IsNot My.Resources.BackImage Then
                        ...
                    End If
                Next
            Next

    This compares every possible pair of picture boxes and checks that:

    1. they are actually two different picture boxes (i <> j)
    2. they have the same image (bb(i).Image Is bb(j).Image)
    3. this image is not the back of the card image (bb(i).Image IsNot My.Resources.BackImage)
    Tuesday, May 05, 2009 2:28 AM
  • oh, I see. So if I assign more than one variable to bb, it can then compare if it is the same or not?

    yeah, that's what I was looking for ^^ Again, much better than my idea for using several text files.

    While I don't think I will need it for the way I plan to put this program together, I would like to ask how I would if I ever needed to make the script wait for a few seconds before doing more things.
     

    Tuesday, May 05, 2009 3:40 AM
  • Well, what exactly do you mean by "wait"? ;) Stopping the whole application for a a few seconds is not a very good idea because the user won't be able to do anything -- move or resize the window, click anything, or even close the program. You can use a timer to set up an event that will fire after a defined amount of time. Whether this can be used for your purposes really depends on what you're trying to achieve ;).
    Tuesday, May 05, 2009 4:03 AM
  • err... I don't think the program likes that code. >.<

    When I tried that method, using it inside a loop the program didn't want to run at all. and stops responding.

    I tried it on load, as part of flip(), sometimes it would do nothing and other times it wouldn't load.

    Any idea why it makes the program just hang?

    Tuesday, May 05, 2009 4:22 AM
  • Hm ok I think I know what's the problem and also I believe that this in fact won't work the way I wanted... Well, actually, I think that if you remove the last condition, i.e. change your code to:

            For i As Integer = 1 To 48
                For j As Integer = 1 To 48
                    If i <> j And bb(i).Image Is bb(j).Image Then
                        ...
                    End If
                Next
            Next

    Then it should work. The problem is (I think) that loading the image from resources (when you call My.Resources.BackImage) is too slow so the loop takes too long to execute. This is because (apparently) whenever you ask for My.Resources.BackImage, the program will actually create a new image and not reuse the old one. For this reason also

    bb(i).Image Is My.Resources.BackImage

    will always return false so we can safely drop it. (Every call to My.Resources.BackImage returns new unique image.)
    Tuesday, May 05, 2009 4:40 AM
  • That makes sense. Well, I'd rather code written longer than code written slower, so I thought to try my idea of using 25 labels as a reference.

    I created 25 labels, r1 through r25

    Then I created this code:

        Sub record()
            For i As Integer = 1 To 48
                If bb(i) Is My.Resources.rune_in1 Then
                    Dim x As Integer
                    x = Integer.Parse(r1.Text)
                    r1.Text = x + 1
                End If
            Next
        End Sub
    and actually tried it a number of ways, making sure r1 is visible with a starting value of 0. But no change at all was made to it. Even though I referred to it like this:

        Private Sub PictureBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
            For i As Integer = 1 To 48
                If sender Is bb(i) Then
                    Flip(i)
                    record()
                End If
            Next
        End Sub
    and then I tried it like this:

    Private Sub PictureBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
            For i As Integer = 1 To 48
                If sender Is bb(i) Then
                    Flip(i)
                    record()
                End If
                If bb(i) Is My.Resources.rune_in1 Then
                    Dim x As Integer
                    x = Integer.Parse(r1.Text)
                    r1.Text = x + 1
                End If
            Next
        End Sub
    but it wouldn't write to the label. Which is very weird. Is there something about bb(i) that makes it not work the way I'd like it to?
    Tuesday, May 05, 2009 4:54 AM
  • Same problem, you can't use

    bb(i) Is My.Resources.rune_in1

    for the reasons I explained in my last post. To make it work change that condition to this:

    bb(i) Is rune_in(1)

    Also note that you don't need to put labels on your form, it would be much easier to declare an array and store the values there:

    Dim x(48) As Integer
    ...
    If bb(i) Is rune_in(1) Then
        x(1) = x(1) + 1
    End If


    EDIT: and actually you should use "bb(i).Image Is ...", not "bb(i) Is ..." ;)
    Tuesday, May 05, 2009 5:09 AM
  • Ahhh ok so now I realized that there's a problem with this beacuse rune_in(1) is not the same image as My.Resources.rune_in1. We should probably change this so it makes more sense... In Form_Load, instead of

    For i As Integer = 1 To 48
        rune_in(i) = My.Resources.ResourceManager.GetObject("rune_in" & seq(i).ToString())
    Next

    it would be better to do

    For i As Integer = 1 To 48
        rune_in(i) = My.Resources.ResourceManager.GetObject("rune_in" & i.ToString())
    Next

    and then for every picture box remember which rune it's supposed to show in an integer array:

    Dim myRune(48) As Integer
    ...
    Private Sub matching_runes_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ...
        Dim seq As Integer()
        seq = RandomSequence()
        For i As Integer = 1 To 48
            myRune(i) = seq(i)
        Next
    End Sub

    Well, actually, you could also do without the seq variable:

    Dim myRune As Integer()
    ...
    Private Sub matching_runes_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ...
        myRune = RandomSequence()
    End Sub

    But then you'd also have to change the Flip function:

    Private Sub Flip(ByVal i As Integer)
        If bb(i).Image Is rune_in(myRune(i)) Then
            bb(i).Image = My.Resources.bb
        Else
            bb(i).Image = rune_in(myRune(i))
        End If
    End Sub

    Yes I know it's quite a couple of changes, I hope it's not too confusing. This new setup has also another advantage -- in my previous post, this code:

    If bb(i).Image Is rune_in(1) Then
        x(1) = x(1) + 1
    End If

    would have to be repeated for every rune from 1 to 24. But now you know exactly which rune is supposed to be in picture bb(i) -- it's rune myRune(i). So all you have to do is

    If bb(i).Image Is rune_in(myRune(i)) Then
        x(myRune(i)) = x(myRune(i)) + 1
    End If
    Tuesday, May 05, 2009 5:19 AM
  • I see. that would explain why even typing it all out bb1 through bb48, leaving out the i, still didn't work. but this also didn't work:

            For i As Integer = 1 To 48
                If bb(i) Is rune_in(1) Then
                    Dim x As Integer
                    x = Integer.Parse(r1.Text)
                    r1.Text = x + 1
                End If
            Next
    

    Using your code example,

    For i As Integer = 1 To 48
                Dim x(48) As Integer
                If bb(i) Is rune_in(1) Then
                    x(1) = x(1) + 1
                End If
            Next
    How would I get the value of x to display so I can read those values for testing? When I tried to add this in:

    r1.Text = x

    it produced the error:

    Error 1 Value of type '1-dimensional array of Integer' cannot be converted to 'String'.

    I'm really getting confused as to why I can't seem to write to the label even no matter how I phrase it, because I have done that much many times before without trouble.

    Tuesday, May 05, 2009 5:22 AM
  • First of all you need to use

    bb(i).Image Is whatever

    and not

    bb(i) Is whatever

    if you want to compare the images. (I didn't notice this before, see my edit two posts ago :D.) The error you're getting is because x is an array, you can only assign individual values such as x(1), x(2) ... to your label:

    r1.Text = x(1)

    If you want to display the content of the whole array, you'd have to do it in loop:

    For i As Integer = 1 To 24
        r1.Text = r1.Text & " " & x(i).ToString()
    Next
    Tuesday, May 05, 2009 5:34 AM
  • um... those changes, I followed along mostly, I think, and I like how you give multiple ways of doing things, but as I have it written the images don't appear on click anymore.

    I see, about the .image, I'll go see how that works.

    Public Class matching_runes
    
        Dim bb(48) As PictureBox
        Dim rune_in(48) As Image
        Dim myRune As Integer()
    
        Private Sub matching_runes_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    
            For i As Integer = 1 To 48
                bb(i) = Me.Controls("bb" & i.ToString())
            Next
    
            myRune = RandomSequence()
    
            For i As Integer = 1 To 48
                AddHandler bb(i).Click, AddressOf PictureBox_Click
            Next
    
        End Sub
    
        Private Sub PictureBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
            For i As Integer = 1 To 48
                If sender Is bb(i) Then
                    Flip(i)
                End If
            Next
        End Sub
    
        Private Sub Flip(ByVal i As Integer)
            If bb(i).Image Is rune_in(myRune(i)) Then
                bb(i).Image = My.Resources.bb
            Else
                bb(i).Image = rune_in(myRune(i))
            End If
        End Sub
    
        Private Function RandomSequence() As Integer()
            Dim rand As New Random()
            Dim result(48) As Integer
            Dim used(24) As Integer
            For i As Integer = 1 To 24
                used(i) = 0
            Next
            For i As Integer = 1 To 48
                Dim r As Integer
                Do
                    r = 1 + rand.Next(24)
                Loop Until used(r) < 2
                result(i) = r
                used(r) += 1
            Next
            Return result
        End Function
    
    End Class
    

    Tuesday, May 05, 2009 5:40 AM
  • You forgot the part where you initialize rune_in(i) in Form.Load
    Tuesday, May 05, 2009 5:43 AM
  • Note that the meaning of array rune_in has changed. When we started with this, rune_in(i) was holding the image of rune you want to display in i-th picture box. So there were 48 elements, each contained one of the 24 images and every image was used twice. rune_in(1) could be any of the 24 images chosen at random.

    Now rune_in(i) only holds the i-th rune image -- there should be 24 elements and each rune image should be used exactly once. So rune_in(1) will hold rune_in1.jpg, etc.
    Tuesday, May 05, 2009 5:48 AM
  • When I try to add rune_in(i) I get the error "Expression is not a method."

    Also, what is weird about the text thing is that, in another program, the following example worked perfectly: (part of win conditions in tic tac toe, one of my first programs.)

    If collection.option2.Checked = True And o1.Visible = True And o2.Visible = True And o3.Visible = True And collection.option2.Checked = True Or o4.Visible = True And o5.Visible = True And o6.Visible = True And collection.option2.Checked = True Or o7.Visible = True And o8.Visible = True And o9.Visible = True And collection.option2.Checked = True Or o1.Visible = True And o4.Visible = True And o7.Visible = True And collection.option2.Checked = True Or o2.Visible = True And o5.Visible = True And o8.Visible = True And collection.option2.Checked = True Or o3.Visible = True And o6.Visible = True And o9.Visible = True And collection.option2.Checked = True Or o1.Visible = True And o5.Visible = True And o9.Visible = True And collection.option2.Checked = True Or o3.Visible = True And o5.Visible = True And o7.Visible = True And collection.option2.Checked = True Then
    
                collection.display.Text = "O Wins!"
                display.Text = "O Wins!"
    
                Dim o As Integer
                o = Integer.Parse(lbloscore.Text)
                lbloscore.Text = o + 1
    
                reset()
    
    Tuesday, May 05, 2009 5:57 AM
  • Yes, because here o is an integer. But x is not an integer, it's an integer array . x(1) would be an integer.

    Oh and what I meant about rune_in(i) initialization was that you didn't put this loop in the Form.Load event:

    For i As Integer = 1 To 24
        rune_in(i) = My.Resources.ResourceManager.GetObject("rune_in" & i.ToString())
    Next
    Tuesday, May 05, 2009 6:00 AM
  • I mean, even using the method I was using to write to a text file, and not using it in an array, it wouldn't work the same way and I don't know why.

    Tuesday, May 05, 2009 6:24 AM
  • Not sure what you mean... but there's always some logical explanation for every "weird" and "not working" thing ;).
    Tuesday, May 05, 2009 6:32 AM
  • ya. I agree. the trick is just figuring out what that thing is. lol. Maybe if you explained again about the difference? I'm sorry I'm a bit slow picking this up.

    Ok, well, now this works:

                If bb(i).Image Is rune_in(1) Then
                    display.Text = "say something."
                End If
    When I click just the image for rune_in(1) it reads in display.text "say something." but doesn't say it for the others.

    Oddly, when I tried slipping this in after:

                If bb(i).Image IsNot rune_in(1) Then
                    display.Text = ""
                End If

    Then it didn't work anymore. I also tried with Else, and with Elseif. But that much doesn't really matter because this isn't really how I would express it.

                If bb(1).Image Is rune_in(myRune(i)) Then
                    display.Text = "say something."
                End If
    
                If bb(2).Image Is rune_in(myRune(i)) Then
                    display.Text = "something else."
                End If

    That works perfectly. So now I just need to figure out what I am doing wrong with the labels.

    Tuesday, May 05, 2009 6:40 AM
  • And somehow, just as strange, writing to r1.text works now. But it seems to add 2 instead of one. But no big deal. Now I have a way to define them.

     

    Dim o As Integer

    o =

    Integer.Parse(r1.Text)

    r1.Text = o + 1

    Tuesday, May 05, 2009 7:16 AM
  • ok, this code here works, but it's slow. Is there a script that would do the same thing but be faster? Maybe somekind of preload?

     Still trying a few things and seeing what works best.

     Sub mismatch()
    
            For i As Integer = 1 To 48
    
                For j As Integer = 1 To 48
    
                    If i <> j And bb(i).Image Is bb(j).Image Then
    
                        display.Text = "match"
    
                    Else : display.Text = "mismatch"
    
                    End If
    
                Next
    
            Next
    
    
    
        End Sub
    Tuesday, May 05, 2009 8:26 AM
  • So how about the code you tried before --

    Dim x(24) As Integer
    For i As Integer = 1 to 24
        x(i) = 0
    Next
    For i As Integer = 1 to 48
        If bb(i).Image Is rune_in(myRune(i)) Then
            x(myRune(i)) += 1
        End If
    Next
    For i As Integer = 1 to 24
        If x(i) = 2 Then
            ' Rune number i was uncovered twice
        End If
    Next
    Tuesday, May 05, 2009 11:43 AM
  • Or we could speed it up even more if we remember the array x and don't calculate it every time from scratch:

    Dim x(24) As Integer

    Private Sub matching_runes_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ...
        For i As Integer = 1 to 24
            x(i) = 0
        Next
        ...
    End Sub

    Private Sub Flip(ByVal i As Integer)
        If bb(i).Image Is rune_in(myRune(i)) Then
            bb(i).Image = My.Resources.bb
            x(myRune(i)) -= 1
        Else
            bb(i).Image = rune_in(myRune(i))
            x(myRune(i)) += 1
        End If
        If x(myRune(i)) = 2 Then
            ' Two cards with same rune were uncovered
        End If
    End Sub

    If you use this code, you only modify the appropriate value in x when card gets flipped. And you also immediately know that only the card we're flipping right now could possibly become uncovered twice.
    Tuesday, May 05, 2009 11:58 AM
  • I'm trying the label method, and one thing I am wondering about...

    Sub mismatch()
            For i As Integer = 1 To 48
                For y As Integer = 1 To 24
                    If r25.Text = "46" And r(y).Text = "9" Or r(y).Text = "4" Then
                        bb(i).Image = My.Resources.bb
                    End If
                Next
            Next
        End Sub

    When I try to do that, instead of typing out the same thing for r1.text through r24.text is produces the error:

    Error 1 Name 'r' is not declared. 
    Error 2 Name 'r' is not declared. 

    Which is not really a big deal since it's not so hard to repeat it just 24 times, but for sake of understanding, what am I doing wrong?

    Tuesday, May 05, 2009 8:13 PM
  • You write

    r(y).Text = "9"

    which means "take array r, look at index y, take the Text property of item at this index and set it to 9." But there is no such array called r. There are only labels called r1, r2 ... but no array r. So you can only use r1, r2, ... but not r(i). What you could do though is

    Me.Controls("name of control").Text = "9"

    so in this case

    Me.Controls("r" & y.ToString()).Text = "9"

    .
    Tuesday, May 05, 2009 8:28 PM

  • I am trying your array method, with a few changes...

            For i As Integer = 1 To 24
                If v(i) = 2 Then
                    ' Rune number i was uncovered twice
                    display.Text = "match!"
                End If
                If v(i) <> 2 And r25.Text = "46" Then
                    ' Rune number i was uncovered twice
                    display.Text = "mismatch!"
                End If
            Next

    Changed x to v because x is being used there, and used it in combination with the label text that sees how many are not flipped. Works perfectly!

    But now I am running into a bit of a problem... when 2 match, I want it to make those two image boxes invisible and again use the my.resources.bb and if a mismath, I want it to make those two flip back over, using my.resources.bb

    Problem is, when I use

    bb(i).Image = My.Resources.bb

    it just turns them all over, and not simply the ones flipped. Then I tried:

    rune_in(myRune(i)) = My.Resources.bb
    Figuring that maybe it would just flip back over those that are visible, but then all of them, even on the other side, use the background.

    Using r25.text and various IFs I am able to keep the user from flipping over more than 2. Now I need to figure out a way to get the program to do something only to those that were flipped, without having to define each and every box.
    Tuesday, May 05, 2009 8:32 PM
  • um.. weird. that array doesn't work right afterall. It reads "mismatch" as "mismatch" but also reads a match as a "mismatch"
    Tuesday, May 05, 2009 8:45 PM
  • hmm. these don't seem to work right either...

                If bb(i) Is rune_in(myRune(i)) Then
                    bb(i).Image = My.Resources.bb

    That doesn't flip em back over at all.

                If bb(i) Is rune_in(myRune(i)) Then
                    rune_in(myRune(i)) = My.Resources.bb
                End If

    Neither does that.

                If bb(i) Is rune_in(i) Then
                    rune_in(myRune(i)) = My.Resources.bb

    Nor that.

                If bb(i) Is rune_in(i) Then
                    bb(i).Image = My.Resources.bb

    Not that either.

    I'm not really sure what I am missing.

    Tuesday, May 05, 2009 9:51 PM
  • First of all

    If bb(i) Is rune_in(myRune(i)) Then

    won't work, you have to do

    If bb(i).Image Is rune_in(myRune(i)) Then

    To hide / flip back the matching / mismatched pictures just do:

    For i As Integer = 1 To 48
        If v(myRune(i)) = 2 Then
            bb(i).Visible = False
        ElseIf bb(i).Image Is rune_in(myRune(i)) Then
            bb(i).Image = My.Resources.bb
            v(myRune(i)) -= 1
        End If
    Next
    Tuesday, May 05, 2009 10:24 PM
  • oh. lol. I can't believe I forgot the .image on the first line. :P Thanks.
    Tuesday, May 05, 2009 10:37 PM
  • Ok, well now I have most of it worked out. I am only using one label instead of 25, and that one label is used to hold the value of how many are not flipped so only 2 can be flipped at a time and to help make everything else work the way I want it to

    Now at first I had it set up so that the moment there is a mismatch, they flip over, etc. But being that this is a memory game that wouldn't work because the user has to be able to see the images to remember what image is where

    I reworked the order, using extra functions and elseif and managed to get it where it won't flip over the moment a match or mismatch occurs, but on the click after.

    Then I had a small problem where if the user clicks the image that is flipped, the other one will flip back over but it does not. So I changed the click to:

            For i As Integer = 1 To 48
                If sender Is bb(i) And r25.Text = "46" And bb(i).Image IsNot rune_in(myRune(i)) Then
                mismatch()
                    r25.Text = "48"
                End If
                If sender Is bb(i) And r25.Text <> "46" And bb(i).Image IsNot rune_in(myRune(i)) Then
                    Flip(i)
                End If
                Dim x(myRune(i)) As Integer
            Next
    



    And then if the user clicked the flipped image, it wouldn't do anything, only on clicking an image that isn't flipped.

    I tried putting this in at the end of the click:

                If r25.Text = "46" And bb(i).Image Is rune_in(myRune(i)) Then
    
    
    
    
    
    
    
                    bb(i).Image = My.Resources.bb<br/>            End If
    
    
    
    
    But then only one would be visible at a given time no matter what I click. When I put it at the beginning, the strange thing was, it only worked half the time, and sometimes only one image is visble at a time.

    I don't really need the user to be able to click the flipped up images, but it would be nice to know what I am doing wrong there.
    • Edited by Elliander Tuesday, May 05, 2009 11:46 PM
    Tuesday, May 05, 2009 11:42 PM
  • Well, the condition

    If ... And bb(i).Image IsNot rune_in(myRune(i)) Then

    will only hold if the clicked image is not flipped right now. So if you want to make this work with flipped images you will have to remove this condition. I hope I'm not mistaken...
    Wednesday, May 06, 2009 12:14 AM
  • Something else I thought I would do is to make a second rune image, inverted, to appear when the images are matched.

    One way I thought to do this was to create an extra 48 picture boxes that don't act as buttons, in the back, then then an image becomes invisible these images appear instead.

    Doing this would probably help me understand more the sequence.

    I have them named rune1 through rune48. rune1 would be the same as rune_in1 and I have them in the resources.

    I guess the simple question would be, is there a way to make the same number sequence affect more than one picture box and image?
    Wednesday, May 06, 2009 12:27 AM
  • Well, sure. Although I'm not sure why you have rune1 to rune48, didn't you have only 24 different images? All you have to do is look at myRune(i) for picture box number i and load it with image rune(myRune(i)).
    Wednesday, May 06, 2009 12:41 AM
  • err.. your right. lol. rune1 through rune24. typo.
    Wednesday, May 06, 2009 12:43 AM
  • hmm...

    "Object reference not set to an instance of an object."

    rr(i).Image = rune(myRune(i))
    It only produces an error on run, no errors appear in the syntax...
    Wednesday, May 06, 2009 1:04 AM
  • Umm and what exactly is "rune" ? :) Is that some new array? (I know that I said rune(myRune(i)) in my prev post, what I meant was that you could create an array similar to rune_in() which you would call for example rune(), fill it with the inverted images just like you did rune_in(), and use it the same way as rune_in().)
    Wednesday, May 06, 2009 1:08 AM
  • oh, I see what you mean. Well, the 48 new pictures boxes are named rr1 through rr48. And the images are rune1 through rune28

    At the top I added:

     

    Dim rune(48) As Image

     

    Dim rr(48) As PictureBox


    I have load inside a function, so I can call on it when I use the new game button in a different form.

        Sub loader()
            For i As Integer = 1 To 48
                bb(i) = Me.Controls("bb" & i.ToString())
                rr(i).Image = rune(myRune(i))
            Next
            For i As Integer = 1 To 24
                rune_in(i) = My.Resources.ResourceManager.GetObject("rune_in" & i.ToString())
                rune(i) = My.Resources.ResourceManager.GetObject("rune" & i.ToString())
            Next
            myRune = RandomSequence()
            For i As Integer = 1 To 48
                AddHandler bb(i).Click, AddressOf PictureBox_Click
            Next
    
        End Sub

    After all this is done I think I am going to have to read over this thread a few times to make sure I understand everything. I understand more, but still not everything.

    Wednesday, May 06, 2009 1:40 AM
  • First of all

    If bb(i) Is rune_in(myRune(i)) Then

    won't work, you have to do

    If bb(i).Image Is rune_in(myRune(i)) Then

    To hide / flip back the matching / mismatched pictures just do:

    For i As Integer = 1 To 48
        If v(myRune(i)) = 2 Then
            bb(i).Visible = False
        ElseIf bb(i).Image Is rune_in(myRune(i)) Then
            bb(i).Image = My.Resources.bb
            v(myRune(i)) -= 1
        End If
    Next









    Hi Mike,


    I was wondering if you could assist me with generating random "TEXT" (not numbers) into Textbox1.Text. I need it to generate 6-8 random characters. I am new to VB 2008 & was wondering if you could give me a source code example. Thanks!






    Saturday, May 09, 2009 10:53 PM
  • Perhaps it would be better if you created a new thread instead of asking in a completely unrelated one ;). But ok here's an example:

            Dim rand As New Random()
            Dim length As Integer
            Dim randomString As String
            length = rand.Next(6, 9)
            randomString = ""
            For i As Integer = 1 To length
                randomString &= Chr(32 + rand.Next(95))
            Next
            TextBox1.Text = randomString

    This will generate a random string of length 6 to 8 consisting of random printable ascii characters (hex codes 32 to 126). If you need different characters, you'll have to modify the

                randomString &= Chr(32 + rand.Next(95))

    line.
    Saturday, May 09, 2009 11:26 PM
  • Perhaps it would be better if you created a new thread instead of asking in a completely unrelated one ;). But ok here's an example:

            Dim rand As New Random()
            Dim length As Integer
            Dim randomString As String
            length = rand.Next(6, 9)
            randomString = ""
            For i As Integer = 1 To length
                randomString &= Chr(32 + rand.Next(95))
            Next
            TextBox1.Text = randomString

    This will generate a random string of length 6 to 8 consisting of random printable ascii characters (hex codes 32 to 126). If you need different characters, you'll have to modify the

                randomString &= Chr(32 + rand.Next(95))

    line.





    Well this was my best bet for getting the quickest response from people who have already discussed a similar topic. Starting a new thread would have taken more time since I would then have to hope that the right person would find it rather then post to a thread that already has this similar topic ("How to generate a random number sequence"). My version is pretty much the same aspect, except "How to generate a random text sequence" instead. It could be answered here.  ;o)
    Sunday, May 10, 2009 1:18 AM
  • Perhaps it would be better if you created a new thread instead of asking in a completely unrelated one ;). But ok here's an example:

            Dim rand As New Random()
            Dim length As Integer
            Dim randomString As String
            length = rand.Next(6, 9)
            randomString = ""
            For i As Integer = 1 To length
                randomString &= Chr(32 + rand.Next(95))
            Next
            TextBox1.Text = randomString

    This will generate a random string of length 6 to 8 consisting of random printable ascii characters (hex codes 32 to 126). If you need different characters, you'll have to modify the

                randomString &= Chr(32 + rand.Next(95))

    line.
    Oh, and what does the 32 & 95 represent? I dont see how this ties-in with modifying/specifying the alphabetical values. I need between 6-9 random letters to be generated into the TexBox1.Text. I see the various signs, but I need strictly letters of the alphabet or even a few numbers mixed with letters. I can't exceed 8 characters though.
    Sunday, May 10, 2009 1:26 AM
  • Perhaps it would be better if you created a new thread instead of asking in a completely unrelated one ;). But ok here's an example:

            Dim rand As New Random()
            Dim length As Integer
            Dim randomString As String
            length = rand.Next(6, 9)
            randomString = ""
            For i As Integer = 1 To length
                randomString &= Chr(32 + rand.Next(95))
            Next
            TextBox1.Text = randomString

    This will generate a random string of length 6 to 8 consisting of random printable ascii characters (hex codes 32 to 126). If you need different characters, you'll have to modify the

                randomString &= Chr(32 + rand.Next(95))

    line.






    I got this code figured out now. All you have to do is change the following like so:

    e.g.


    length = rand.Next(8, 8)   ***This is if you want generate 8 characters

    randomString &= Chr(65+ rand.Next(20))  *** If you only want to generate letters A-W or almost Z (not sure how far, but letters strictly.)


    Thanks for your help!



    Sunday, May 10, 2009 3:47 AM
  • Oh yes, I forgot all about the original topic of this question since we got a bit further in discussion :).

    rand.Next(a, b) generates numbers from {a, a+1, ... b-1}. (It's a bit non-intuitive.) So rand.Next(6, 9) will generate numbers 6, 7 or 8. If you want to generate lower case characters, upper case characters and numbers, or even any specified set of characters, you can put them in an array (or a list):

    Dim chars As Char() = {"a", "b", "c", "d", "e", "f"}
    For i As Integer = 1 to 8
        randomString &= chars(rand.Next(chars.Length))
    Next

    If you don't want to specify the characters manually you can put them in a list using a loop:

            Dim chars As New List(Of Char)
            For i As Integer = Asc("A") To Asc("Z")
                chars.Add(Chr(i))
            Next
            For i As Integer = Asc("a") To Asc("z")
                chars.Add(Chr(i))
            Next
            For i As Integer = Asc("0") To Asc("9")
                chars.Add(Chr(i))
            Next
            For i As Integer = 0 To 10
                MsgBox(chars(rand.Next(chars.Count)))
            Next
    Sunday, May 10, 2009 12:16 PM