# Random Picture Selection

• ### Question

• I would like to use a function to randomly select an image and apply it to a picturebox (MapPt[x]). This is for "procedural generation" in a game I have made. I know I can do each separately in a similar way, but it would be nice to know how to use functions properly.

Here is the current code I have:

```    'Map segments
Dim Numbr As Integer
Dim MapNum As Image
Dim Map0a As Image = My.Resources.Map0
Dim Map1a As Image = My.Resources.Map1
Dim Map2a As Image = My.Resources.Map2
Dim Map3a As Image = My.Resources.Map3
Dim Map4a As Image = My.Resources.Map4
Dim Map5a As Image = My.Resources.Map5
Dim Map6a As Image = My.Resources.Map6
Dim Map7a As Image = My.Resources.Map7
Dim Map8a As Image = My.Resources.Map8

'Random map gen
Public Function RNG(x) As Image
Numbr = (Int(Rnd() * 8) + 1)
If Numbr = 0 Then
x = Map0a
ElseIf Numbr = 1 Then
x = Map1a
ElseIf Numbr = 2 Then
x = Map2a
ElseIf Numbr = 3 Then
x = Map3a
ElseIf Numbr = 4 Then
x = Map4a
ElseIf Numbr = 5 Then
x = Map5a
ElseIf Numbr = 6 Then
x = Map6a
ElseIf Numbr = 7 Then
x = Map7a
ElseIf Numbr = 8 Then
x = Map8a
End If
Return x
End Function

'Procedural Generation
RNG(MapPt1.Image)
RNG(MapPt2.Image)
RNG(MapPt3.Image)

'~~~~~ OTHER GAME CODE STUFF ~~~~~

End Sub```

I used Dim MapNum as I tried using the function like this:

MapPt1.Image = RNG(MapNum)
MapPt2.Image = RNG(MapNum)
MapPt3.Image = RNG(MapNum)

• Edited by Saturday, March 31, 2018 9:45 PM
Saturday, March 31, 2018 9:43 PM

### All replies

• I would like to use a function to randomly select an image and apply it to a picturebox (MapPt[x]).

Use the Random object instead of the Rnd() function to get your random numbers.  As the numbers are being generated inside a function, make sure the Random object is either global or static.

Select Case is neater than a series of ElseIf.  See: https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/select-case-statement

Or you could put the map names in an array of Image, and select the one you want by using the random number as an index into that array.

Dim Maps As Image(8)
Maps(0) = My.Resources.Map0
...

x = Maps(RND.Next(8))

• Edited by Saturday, March 31, 2018 10:00 PM fmt
Saturday, March 31, 2018 9:58 PM
• Hi

Here is some code that will fill three PB on Form1 with Unique Random Images from those listed. The Button1 will reload with fresh images - for testing.

```' Form1 with MapPt1, MapPt2, MapPt3 (PictureBoxes)
' and Button1
Option Strict On
Option Explicit On
Public Class Form1
Dim im As New List(Of Image)
Dim rand As New Random
With im
End With
FillPB()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
FillPB()
End Sub
Sub FillPB()
Dim used As New List(Of Integer)
Dim UniqueItems As New List(Of Integer)
UniqueItems = GetRand(0, 7, 3)
MapPt1.Image = im(UniqueItems(0))
MapPt2.Image = im(UniqueItems(1))
MapPt3.Image = im(UniqueItems(2))
End Sub
Function GetRand(smallest As Integer, largest As Integer, tk As Integer) As List(Of Integer)
' returns list of tk unique randoms between smallest to (largest - smallest + 1) both inclusive
Return Enumerable.Range(smallest, largest - smallest + 1).OrderBy(Function(n) rand.Next).Take(tk).ToList
End Function
End Class```

Regards Les, Livingston, Scotland

• Proposed as answer by Saturday, March 31, 2018 11:55 PM
• Unproposed as answer by Sunday, April 1, 2018 11:22 PM
Saturday, March 31, 2018 10:10 PM
• This is the information needed in your other post to generate the list of maze images and wall data.  The only missing info at this point is how the maps "stitch" together and how you handle the player's start position on each map.  Some changes may need to be made to accommodate those unknowns, but with what we know from the two threads so far the example becomes something more like:

```Public Class Form1
Friend WithEvents MapPictureBox As New PictureBox With {.Dock = DockStyle.Fill}
Friend WithEvents PlayerPictureBox As New PictureBox With {.Size = New Size(12, 12)}

Private maps As New MapInfoCollection
Private mapIndex As Integer = -1
Private map As MapInfo
Private cellSize As Integer = 12
Private playerStart As New Point(1, 0) 'TODO: set to map start point (assumes all maps use the same point)
Private playerPosition As Point

'randomize mapinfo list
maps.Randomize()
'set current map
SetNextMap()

'TODO: Assign an image to the player picture box
'PlayerPictureBox.Image =

End Sub

Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
Dim moveDelta As Size
Select Case e.KeyCode
Case Keys.W
moveDelta = New Size(0, -1)
Case Keys.S
moveDelta = New Size(0, 1)
Case Keys.A
moveDelta = New Size(-1, 0)
Case Keys.D
moveDelta = New Size(1, 0)
End Select
Dim testCell = playerPosition + moveDelta
If map.IsPassable(testCell.X, testCell.Y) Then MovePlayerBy(moveDelta.Width, moveDelta.Height)
End Sub

Private Sub MovePlayerBy(cellsX As Integer, cellsY As Integer)
PlayerPictureBox.Top += cellsY * map.CellSize
PlayerPictureBox.Left += cellsX * map.CellSize
playerPosition += New Point(cellsX, cellsY)
End Sub

Private Sub MovePlayerTo(cellX As Integer, cellY As Integer)
PlayerPictureBox.Top = cellY * map.CellSize
PlayerPictureBox.Left = cellX * map.CellSize
playerPosition = New Point(cellX, cellY)
End Sub

'get the next random map
Private Sub SetNextMap()
mapIndex += 1
If mapIndex = maps.Count Then mapIndex = 0
map = maps(mapIndex)
MapPictureBox.Image = map.Image
MovePlayerTo(playerStart.X, playerStart.Y)
End Sub
End Class

'store the images along with the wall data
Public Class MapInfo
Public ReadOnly Property CellSize As Integer
Public ReadOnly Property Image As Image

Private passageData(,) As Boolean
Private cellCounts As Size

Public Sub New(mapImage As Bitmap, mapCellSize As Integer)
Image = mapImage
CellSize = mapCellSize
cellCounts = New Size(Math.Ceiling(mapImage.Width / mapCellSize), Math.Ceiling(mapImage.Height / mapCellSize))
ReDim passageData(cellCounts.Width - 1, cellCounts.Height - 1)
Dim halfcell = mapCellSize \ 2
For x = 0 To passageData.GetLength(0) - 1
For y = 0 To passageData.GetLength(1) - 1
'if the center pixel of the cell is not dark, this cell is passable
passageData(x, y) = Not (mapImage.GetPixel(x * mapCellSize + halfcell, y * mapCellSize + halfcell).GetBrightness < 0.1)
Next
Next
End Sub

Public Function IsPassable(x As Integer, y As Integer) As Boolean
If x > passageData.GetLength(0) - 1 Then Return False
If y > passageData.GetLength(1) - 1 Then Return False
If x < 0 OrElse y < 0 Then Return False
Return passageData(x, y)
End Function
End Class

Public Class MapInfoCollection
Inherits ObjectModel.Collection(Of MapInfo)
Private random As New Random

'randomize the order of items in the list
Public Sub Randomize()
Dim ids = Enumerable.Range(0, Count).ToList
While ids.Count > 0
Dim idx = random.Next(ids.Count)
Dim item = Items(idx)
RemoveAt(idx)
ids.RemoveAt(idx)
End While
End Sub
End Class```

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

Sunday, April 1, 2018 12:32 AM
• There are many ways to do most things. Here are some more ideas.

This example makes a list of bitmaps from the files read from a disc folder. Then it randomly picks an image from the list and draws the animation.

The example makes the controls, just cut and paste the code into an empty form.

```Public Class Form8
Private Cards As New List(Of Bitmap)
Private WithEvents Timer1 As New Timer With {.Interval = 30}
Private WithEvents Button1 As New Button With {.Parent = Me, .Text = "Go", .Location = New Point(150, 200)}
Private WithEvents picturebox1 As New PictureBox With {.Parent = Me, .Dock = DockStyle.Fill, .BackColor = Color.Black}
Private CardOffset, CardCount, CardSelection As Integer
Private cardSize As New Size(40, 60)

BackColor = Color.DimGray
Dim di As New IO.DirectoryInfo("c:\bitmaps\test\")
Dim aryFi As IO.FileInfo() = di.GetFiles("*.png")

For Each fi As IO.FileInfo In aryFi
Next

End Sub

Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles picturebox1.Paint

With e.Graphics
.Clear(Color.Black)
Dim destRect As Rectangle
Dim y2 As Integer
Dim ytotal As Integer = cardSize.Height * (Cards.Count - 1)

For y As Integer = 0 To Cards.Count - 1
y2 = (y * cardSize.Height) - CardOffset
If y2 < -cardSize.Height Then y2 += ytotal

destRect = New Rectangle(20, y2, cardSize.Width, cardSize.Height)
.DrawImage(Cards(y), destRect)
Next

If CardSelection > -1 Then
destRect = New Rectangle(150, 20, 2 * cardSize.Width, 2 * cardSize.Height)
.DrawImage(Cards(CardSelection), destRect)
End If

destRect = New Rectangle(10, 0, CInt(1.5 * cardSize.Width), cardSize.Height)
.DrawRectangle(Pens.Yellow, destRect)

End With
End Sub

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
CardCount -= 1
CardOffset += CInt(CardCount / 10)
If CardOffset > cardSize.Height * (Cards.Count - 1) Then
CardOffset = 0
End If
If CardCount < 0 Then
Timer1.Stop()
CardSelection = CInt(CardOffset / cardSize.Height)

End If

PictureBox1.Invalidate()
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
CardOffset = 0
CardSelection = -1
Dim rand As New Random
CardCount = 150 + rand.Next(0, 100)
Timer1.Start()

End Sub
End Class```

Sunday, April 1, 2018 9:18 AM
• This is the information needed in your other post to generate the list of maze images and wall data.  The only missing info at this point is how the maps "stitch" together and how you handle the player's start position on each map.  Some changes may need to be made to accommodate those unknowns, but with what we know from the two threads so far the example becomes something more like

Reed,

I figured the wall collisions out, I just made the grid bigger to expose more of the path when moving over it and then changed form size to 1080p. Also, they are "stitched" together but alligning the top of one with the bottom of the next in a tower type thing within the form.

Also, due to my newbieness of coding, I don't get what you have done in terms of random generation.

TheChapster

Sunday, April 1, 2018 3:11 PM
• There are many ways to do most things. Here are some more ideas.

This example makes a list of bitmaps from the files read from a disc folder. Then it randomly picks an image from the list and draws the animation.

The example makes the controls, just cut and paste the code into an empty form.

Thanks TommyTwoTrain,

With my code, there will be no animation, just when the form is loaded, 3 maps will be randomly selected out of a list (don't mind if same one is picked twice). I am getting confused as to which parts of your code are doing the random generation as a lot of it is code that I have never seen.
I assume the timed parts are to do with the animation, but what selects the random card out of the lot, and how would I change that to do the 3 map generations to different variables "simultaneously"?

TheChapster

Sunday, April 1, 2018 3:30 PM
• I would like to use a function to randomly select an image and apply it to a picturebox (MapPt[x]).

Use the Random object instead of the Rnd() function to get your random numbers.  As the numbers are being generated inside a function, make sure the Random object is either global or static.

Select Case is neater than a series of ElseIf.  See: https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/select-case-statement

Or you could put the map names in an array of Image, and select the one you want by using the random number as an index into that array.

Dim Maps As Image(8)
Maps(0) = My.Resources.Map0
...

x = Maps(RND.Next(8))

Thanks, Acamar.

The array way you stated will probably be the method I choose, although for some reason, even though the RNG that I used has been tested alone (slightly altered from first) my code still doesn't change the image from default.

Here is what I have now:

```    'Map generation variables
Dim Maps(8) As Image

'Random map gen function
Public Function RNG(x) As Integer
x = Int(Rnd() * (x + 1))
Return x
End Function

'Map array setup
Maps(0) = My.Resources.Map0
Maps(1) = My.Resources.Map1
Maps(2) = My.Resources.Map2
Maps(3) = My.Resources.Map3
Maps(4) = My.Resources.Map4
Maps(5) = My.Resources.Map5
Maps(6) = My.Resources.Map6
Maps(7) = My.Resources.Map7
Maps(8) = My.Resources.Map8

'Procedural Generation
MapPt1.Image = Maps(RNG(8))
MapPt2.Image = Maps(RNG(8))
MapPt3.Image = Maps(RNG(8))```
Am I missing something stupid or is it the same problem with "Rdn / random object"

TheChapster

Sunday, April 1, 2018 3:34 PM
• For anyone who may find it useful, here is a screenshot of the form in action:

And the Map segments look like this:

I know it would have been useful in the last post, but I didn't have access to posting pictures then.

Also, on the topic of this game, I also am having a slight issue with global variables working when they are set one way, but not another. It is for the character selection process, to change the sprite in use. Not important but good to get working:

```    'Character selection, what sprite will be used in game

'Doesn't work here
Public Sub GreenSelect_Click(sender As Object, e As EventArgs) Handles GreenSelect.Click
GlobalVariables.CubeSelect = 1
Me.Hide()
Game.Show()
End Sub

Public Sub BlueSelect_Click(sender As Object, e As EventArgs) Handles BlueSelect.Click
GlobalVariables.CubeSelect = 2
Me.Hide()
Game.Show()
End Sub

Public Sub PinkSelect_Click(sender As Object, e As EventArgs) Handles PinkSelect.Click
GlobalVariables.CubeSelect = 3
Me.Hide()
Game.Show()
End Sub

Public Sub YellowSelect_Click(sender As Object, e As EventArgs) Handles YellowSelect.Click
GlobalVariables.CubeSelect = 4
Me.Hide()
Game.Show()
End Sub

Public Sub RedSelect_Click(sender As Object, e As EventArgs) Handles RedSelect.Click
GlobalVariables.CubeSelect = 5
Me.Hide()
Game.Show()
End Sub

'Works Here
GlobalVariables.CubeSelect = 4
End Sub```

Many thanks to you all.

TheChapster

Sunday, April 1, 2018 3:41 PM
• Thanks TommyTwoTrain,

With my code, there will be no animation, just when the form is loaded, 3 maps will be randomly selected out of a list (don't mind if same one is picked twice). I am getting confused as to which parts of your code are doing the random generation as a lot of it is code that I have never seen.
I assume the timed parts are to do with the animation, but what selects the random card out of the lot, and how would I change that to do the 3 map generations to different variables "simultaneously"?

TheChapster

That example is just for fun. It does not work for you exactly or was not meant to at least.

The example also uses a list of bitmaps which you might find handy in the future.

We develop a tool box of ways and for each problem decide which is the best tool to acheive what we want. Sometimes have to try several things to see what seems best..

In the example the animation has counters that are set when you click the button. The result is the selection after the animation counts down.

This is what sets where the card countdown starts.

Dim rand As New Random
CardCount = 150 + rand.Next(0, 100)

where cardcount is the top of the card in pixels for drawing and is decreasing for the slow down effect. ie the wheel spins fast then slows to a stop on the selected card.

PS I meant to declare the rand private outside the sub routine so it repeats randomly with next correctly.

To determine the card to select and show the card this actually sets the card index in the bitmap list

If CardCount < 0 Then
Timer1.Stop()
CardSelection = CInt(CardOffset / cardSize.Height)

End If

because cardoffset is the top of the card at the top of the drawing in pixels. Divide by the cardheight to get the card index in the list of bitmaps.

Its tricky and hard to explain. Not saying do your project exactly this way. Just saying here are some tools one could use.

:)

Your grid looks good. Where do the original bitmaps that you "stitch" come from? Do you draw those and save them or are they something else?

PS You might like this thread:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/79c85729-592d-42a2-b639-7c2e3ac4503e/recursive-backtracker-maze?forum=vbgeneral

Sunday, April 1, 2018 5:39 PM
• TommyTwoTrain, Thanks for the reply.

The images I use were made on photoshop, in which I created a grid and filled in the gaps to create one route through (you can cheat by going diagonal, which is not a problem as it is really tricky to pull off and you gain extra points for touching a wall which happens 90% of the time (lowest score is best).
I also created the artwork and a Lore for the game as a kind of joke... even though it is just some crappy little school project. The problem is, I was so busy with getting the useless stuff done and ignoring the code and write up that I am pushing the deadline which is this tuesday now.

TheChapster

Sunday, April 1, 2018 8:02 PM
• my code still doesn't change the image from default.

You would need to use the debugger and step through one line at a time to find out whether the function is returning the same result each time, and what it is, or whether the problem is in assigning the images.  But if you use a Random object then there is no need for a separate function.  Just declare the object at the start of the code, and use it in-line as required.

Sunday, April 1, 2018 9:38 PM
• TommyTwoTrain, Thanks for the reply.

The images I use were made on photoshop, in which I created a grid and filled in the gaps to create one route through (you can cheat by going diagonal, which is not a problem as it is really tricky to pull off and you gain extra points for touching a wall which happens 90% of the time (lowest score is best).
I also created the artwork and a Lore for the game as a kind of joke... even though it is just some crappy little school project. The problem is, I was so busy with getting the useless stuff done and ignoring the code and write up that I am pushing the deadline which is this tuesday now.

TheChapster

Oh yeah very good! I see.

Yes the last 10 percent is the hardest to finish and say its done.

Did you get your question answered? Just use the random.next as Acamar and others mentioned. If you have 20 images then index = rand.next(0, 21) will assure they all are played.

A big part of a project, especially a game, is the "artwork" and other operations etc that is something not all programmers can do.

Sunday, April 1, 2018 9:57 PM
• Did you get your question answered? Just use the random.next as Acamar and others mentioned. If you have 20 images then index = rand.next(0, 21) will assure they all are played.

TommyTwoTrain and Acamar

I need to select only 3 maps out of the 9 that I have in total (0 - 8). I stated my exact code above, and for some reason it is still not working as inteneded.

Also, with the debugger tool, I've never used it, so will have to really quickly learn it -_- by putting code breaks in however, I did fix a different issue that I was looking for xD.

--- EDIT ---

It turns out that for some reason my code is always generating the numbers "6, 4, 5" no matter how many times I run it. This happens within RNG as I declared Pt1 Before, like this:

```        'Procedural Generation
Pt1 = 7
Pt1 = RNG(8)
MapPt1.Image = Maps(Pt1)
Pt2 = RNG(8)
MapPt2.Image = Maps(Pt2)
Pt3 = RNG(8)
MapPt3.Image = Maps(Pt3)```

And the 6, 4, 5 pattern still showed up instead of 7, 4, 5. removing the "Pt1 = RNG(8)" bit caused the pattern to become 7, 4, 5.

I also tested to see if it was the fact that it was in the function, putting the code inline. Same issue.

TheChapster

Sunday, April 1, 2018 10:49 PM
• Did you get your question answered? Just use the random.next as Acamar and others mentioned. If you have 20 images then index = rand.next(0, 21) will assure they all are played.

TommyTwoTrain and Acamar

I need to select only 3 maps out of the 9 that I have in total (0 - 8). I stated my exact code above, and for some reason it is still not working as inteneded.

Also, with the debugger tool, I've never used it, so will have to really quickly learn it -_- by putting code breaks in however, I did fix a different issue that I was looking for xD.

TheChapster

Do you mean GlobalVariables.CubeSelect is not passing from one sub routine or form to another?

Sorry. Its not clear to me what the question is now I guess.

Sunday, April 1, 2018 11:40 PM

• Do you mean GlobalVariables.CubeSelect is not passing from one sub routine or form to another?

Sorry. Its not clear to me what the question is now I guess.

Just figured it out. I wasn't using Randomize() before, to set the Rnd() seed using the clock. Just looked on the VB database online and it said you need to. Only problem is, I would prefer it didn't generate same one twice.

TheChapster

Sunday, April 1, 2018 11:49 PM
• Only problem is, I would prefer it didn't generate same one twice.

You will want the randomize sequence to be the same each time while testing so that you don't have to recalculate the expected test results each time.  It is only when testing is more advanced that you need the sequence to start at a new point each time.  The Random object can be provided with a seed value that ensures the sequence is the same.  Then you can remove that seed value and it will default to using the time, which will create a new sequence each time the application is run. See:
https://msdn.microsoft.com/en-us/library/system.random(v=vs.110).aspx

Monday, April 2, 2018 1:41 AM
• You will want the randomize sequence to be the same each time while testing so that you don't have to recalculate the expected test results each time.  It is only when testing is more advanced that you need the sequence to start at a new point each time.  The Random object can be provided with a seed value that ensures the sequence is the same.  Then you can remove that seed value and it will default to using the time, which will create a new sequence each time the application is run. See:

https://msdn.microsoft.com/en-us/library/system.random(v=vs.110).aspx

I mean, I have got it working, but I dont want multiple of the same segment to appear in the same maze.

TheChapster

Monday, April 2, 2018 11:39 AM
• I mean, I have got it working, but I dont want multiple of the same segment to appear in the same maze.

TheChapster

Make a list of the images when you start. Then pick the current random image to show from the list and remove the image from the list. Now you only have the images you have not used left in the list to choose from.

Monday, April 2, 2018 1:24 PM

• TheChapster

I like the artwork. That's too cool really!

La vida loca

Tuesday, April 3, 2018 2:28 AM
• I mean, I have got it working, but I dont want multiple of the same segment to appear in the same maze.

Then you need a process for unique random.  There are several ways to do this.  If you put the items into their own collection (eg, a list) you can remove them from that list as you select them so that they can't be selected again. Or, if you are putting them into a collection of some sort, then you can use the .Contains property of that collection so that you don't select anything already in the collection (use a loop to keep selecting until the destination collection doesn't contain the item just selected).  If you are selecting from an indexed collection (eg, a List or Array) you can create a list of integers representing the index of the list items, and select from that, either setting each item in the index list to -1 to mean 'already selected', or just removing it from the list.  Note that any process that removes items from the list requires an adjustment to the random number range to reflect the reduced number of item to select from.

Tuesday, April 3, 2018 3:32 AM