My program is assigning a value to ALL objects in an array. What's happening and how do I prevent it ? (VB 2008) - vb.net

I have exhausted all of my options and am very desperate for help since I cannot figure out where the bug in my code is, or if there is something I don't understand.
I'm trying to create a "methinks it is a weasel!" mimmick from Richard Dawkins' late 80s documentary about evolution. The goal is to progress through a genetic algorithm until the algorithm guesses the correct answer through mutation and fitness tournaments.
Now, here's the problem:
Private Function fitnessTourney(ByVal editGuess() As Guess, ByVal popIndex As Integer, ByVal tourneySize As Integer, ByVal popNum As Integer)
Dim randInt(tourneySize - 1) As Integer
Dim loopCount1 As Integer = 0
Dim fitnessWinner As New Guess
fitnessWinner.setFitness(-50)
...
And, this loop is where I am experiencing the critical error
...
For i = 0 To tourneySize - 1
Randomize()
randInt(i) = Int(Rnd() * popNum)
While editGuess(randInt(i)).Used = True
If loopCount1 > tourneySize Then
loopCount1 = 0
For i2 = 0 To popNum - 1
editGuess(i2).setUsed(False)
Next
i = -1
Continue For
End If
loopCount1 += 1
randInt(i) = Int(Rnd() * popNum)
End While
editGuess(randInt(i)).determineFitness(correctPhrase)
editGuess(randInt(i)).setUsed(True)
Next
For i = 0 To popNum - 1
editGuess(i).setUsed(False)
Next
What this loop is trying to do is pick out four random instances of the editGuess array of objects. This loop tries to prevent one from being used multiple times, as the population is competing to one of the 10 members (highest fitness of the 4 chosen candidates is supposed to win).
The critical error is that I mysteriously get an endless loop where any instances of editGuess(randInt(i)).Used will always evaluate to true. I have tried to fix this by resetting all instances to False if it loops too many times.
The stumper is that I'll have all instances evaluate to False in the debugger. Then, when I reach "editGuess(randInt(i)).setUsed(True)" (the exact same thing as "editGuess(randInt(i)).Used = True"), it sets this value for EVERY member of the array.
Is there anyone who can see what is happening? I am so close to completing this!
Here's the Guess class:
Public Class Guess
Dim Fitness As Integer
Dim strLength As Integer
Dim strArray(30) As String
Dim guessStr As String
Dim Used As Boolean
Public Sub New()
Fitness = 0
guessStr = ""
strLength = 0
Used = 0
End Sub
Public Sub determineFitness(ByVal correctPhrase As String)
Dim lowerVal
If guessStr.Length <= correctPhrase.Length Then
lowerVal = guessStr.Length
Else
lowerVal = correctPhrase.Length
End If
strArray = guessStr.Split("")
Fitness = 0 - Math.Abs(correctPhrase.Length - guessStr.Length)
For i = 0 To lowerVal - 1
If correctPhrase(i) = guessStr(i) Then
Fitness = Fitness + 1
End If
Next
End Sub
Public Sub Mutate(ByVal mutatepercentage As Decimal, ByVal goodLetters As String)
If mutatepercentage > 100 Then
mutatepercentage = 100
End If
If mutatepercentage < 0 Then
mutatepercentage = 0
End If
mutatepercentage = mutatepercentage / 100
If Rnd() < mutatepercentage Then
strLength = Int(Rnd() * 25) + 5
If strLength < guessStr.Length Then
guessStr = guessStr.Remove(strLength - 1)
End If
End If
For i = 0 To strLength - 1
If Rnd() < mutatepercentage Then
If i < guessStr.Length Then
guessStr = guessStr.Remove(i, 1).Insert(i, goodLetters(Int(Rnd() * goodLetters.Length)))
Else
guessStr = guessStr & goodLetters(Int(Rnd() * goodLetters.Length))
End If
End If
Next
End Sub
Public Sub setFitness(ByVal num As Integer)
Fitness = num
End Sub
Public Sub setStrLength(ByVal num As Integer)
strLength = num
End Sub
Public Sub initializeText()
End Sub
Public Sub setUsed(ByVal bVal As Boolean)
Used = bVal
End Sub
End Class
And, finally, here's where and how the function is called
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
population1(counter) = fitnessTourney(population1, counter, 4, 10)
population2(counter) = fitnessTourney(population2, counter, 4, 10)
population1(counter).Mutate(2, goodLetters)
population2(counter).Mutate(20, goodLetters)
Label1.Text = population1(counter).guessStr
Label2.Text = population2(counter).guessStr
counter += 1
If counter > 9 Then
counter = 0
End If
End Sub
End Class
EDIT 1:
Thank you guys for your comments.
Here is the custom constructor I use to the form. This is used to populate the population arrays that are passed to the fitnessTourney function with editGuess.
Public Sub New()
InitializeComponent()
Randomize()
For i = 0 To 9
population1(i) = New Guess
population2(i) = New Guess
Next
counter = 0
correctPhrase = "Methinks it is a weasel!"
goodLetters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ !##$%^&*()_+-=?></.,;\|`'~"
goodLettersArr = goodLetters.Split("")
For i = 0 To 9
population1(i).setStrLength(Int(Rnd() * 25) + 5)
population2(i).setStrLength(Int(Rnd() * 25) + 5)
For i2 = 0 To population1(i).strLength
population1(i).guessStr = population1(i).guessStr & goodLetters(Int(Rnd() * goodLetters.Length))
Next
For i2 = 0 To population2(i).strLength
population2(i).guessStr = population2(i).guessStr & goodLetters(Int(Rnd() * goodLetters.Length))
Next
Label1.Text = population1(i).guessStr
Label2.Text = population2(i).guessStr
Next
population1(0).guessStr = correctPhrase
population1(0).determineFitness(correctPhrase)
End Sub

I haven't studied all of your code thoroughly, but one big problem is that you are calling Randomize from within the loop. Every time you call Randomize, it re-seeds the random numbers with the current time. Therefore, if you call it multiple times before the clock changes, you will keep getting the first "random" number in the sequence using that time which will always evaluate to the same number. When generating "random" numbers, you want to re-seed your random number generator as few times as possible. Preferably, you'd only seed it once when the application starts.
As a side note, you shouldn't be using the old VB6 style Randomize and Rnd methods. Those are only provided in VB.NET for backwards compatibility. You should instead be using the Random class. It's easier to use too. With the Random class, you don't even need to call a randomize-like method, since it automatically seeds itself at the point in time when you instantiate the object. So, in the case of the Random class, the thing to be careful is to make sure that you only instantiate the object once before entering any loop where you might be using it. If you create a new Random object inside a loop, it will similarly keep generating the same numbers.

Related

Same number is generated in RNG in VB.NET

I am attempting to make a random number generator in VB 16 in Visual Studio, but every time I run this I keep getting 71, I've tried making it public, sharing it and several other things, but it won't work. I am trying to make a program that has the user guess a randomly generated number, and continue guessing until they get it, but for some reason the exact same number is chosen each time. It won't work properly specifically in window app forms. How do I get a random number each time?
Public Shared Randomize()
Dim value As Integer = CInt(Int((100 * Rnd()) + 1))
Public Sub EnterBtn_Click(sender As Object, e As EventArgs) Handles EnterBtn.Click
Dim entervalue As String = EnterTxt.Text
Dim chances As Integer
Select Case entervalue
Case > value
ResTxt.Text = "Too big"
chances += 1
Case < value
ResTxt.Text = "Too small"
chances += 1
Case = value
ResTxt.Text = "Well done, you got it in " & chances & " tries"
End Select
End Sub
You were close! Here's a working example modifying your original logic:
Private random As Random = New Random()
Private value As Integer
Private chances As Integer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
value = random.Next(1, 100)
chances = 0
End Sub
Private Sub EnterBtn_Click(sender As Object, e As EventArgs) Handles EnterBtn.Click
Select Case EnterTxt.Text
Case > value
chances += 1
ResTxt.Text = "Too big"
Case < value
chances += 1
ResTxt.Text = "Too small"
Case = value
chances += 1
ResTxt.Text = "Well done, you got it in " & chances & " tries"
'and reset for next attempt
value = random.Next(1, 100)
chances = 0
End Select
End Sub
Since your code is not correct it is hard to pinpoint the problem. It is also not clear what the code is supposed to do.
Try this
Private Shared PRNG As New Random ' add this
value = PRNG.Next(1, 101)'this will set value to a random number between 1 and 100 inclusive
Here's some skeleton code for you:
Dim rnd As New Random()
For i as Integer = 0 to 10
Console.WriteLine("{0,15:N0}", rnd.Next())
Next
Notice the rnd.Next() thing. Hope it helps.

How do I assign a default property to a FunctionProcedureName() in VB.NET?

I have done extensive research on using recursion in VB.NET (I am using 2015) in order complete a homework assignment. In my desperation, I even asked my professor for help!
I am trying to write a program that will calculate 1! through 12!, and post the results in a list box. The method I am using is based on the following example (sent to me by my prof):
var integer n, result
n = 0
For 1 to 12 do
n = n + 1
write (n, ‘! equals ‘, Fact(n)
End For
Function Fact (ByVal n as Integer)
if (n = 0) then Fact = 1
else Fact = n * Fact (n-1)
End If
End Function
http://www.softwareandfinance.com/VB/Factorial_Recursion.html
My issue is with my call statement for the function (I named it Factorial). Here is the section of the code where I am getting the error message:
For intN = 1 To 12
intFact = Factorial()
lstFactorialsAnswers.Items.Add(intN & "! = " & intFact)
Next
Thank you for your insights.
In response, I removed the "Dim Factorial as Int64" declaration. I also added "intN" as a parameter in the function call. The new error message is "Argument not specified for parameter 'intFact' of 'Public Function Factorial(intN As Integer, intFact As Integer) As Long'.
Here is the revised code:
Public Class frmFactorialMath
Private Sub btnCalculate_Click(sender As Object, e As EventArgs) Handles btnCalculate.Click
Dim intN As Integer
Dim intFact As Integer
intN = Convert.ToInt32(txtN)
For intN = 1 To 12
If intN = 1 Then
lstFactorialsAnswers.Items.Add(intN & "! = " & 1)
Else
intFact = Factorial(intN)
lstFactorialsAnswers.Items.Add(intN & "! = " & intFact)
End If
Next
End Sub
Function Factorial(ByVal intN As Integer, intFact As Integer) As Long
If (intN = 0) Then
Return 1
Else
intFact = intN * Factorial(intN - 1)
Return intFact
End If
End Function
Here is the solution to the issue(s) I was having:
Option Strict On
Public Class frmFactorialMath
' This event handler calculates the factorials for numbers 1 through 12.
' A list showing the answers is compiled and displayed.
Private Sub btnCalculate_Click(sender As Object, e As EventArgs) Handles btnCalculate.Click
Dim intN, intFact As Integer
For intN = 1 To 12
intFact = Factorial(intN)
lstFactorialsAnswers.Items.Add(intN.ToString() & "! = " & intFact.ToString())
Next
End Sub
' This Function performs the calculations using recursion.
Function Factorial(ByVal intFact As Integer) As Integer
If intFact = 1 Then
Return 1
Else
' This "Do While" loop ends the recursion, eliminating an infinite loop.
Do While intFact > 1
Return intFact * Factorial(intFact - 1)
Loop
End If
End Function

VB.NET Compare each item in collection to every other item in collection - Threading

this is my first time posting so please accept my apologies if im not doing this right and please feel free to correct me for any formatting or posting guidelines. I am doing this in VB.Net with .NET Framework 4.5.2.
I have a large collection called gBoard in a class.
Private gBoard As Collection
It contains roughly 2000 instances of a class.
What i am trying to achieve is for each item in the class, i want to look at each other item in the class and then update the first item based on variables in the second.
Currently i have the following code:
In the main class:
Private gBoard As New Collection ' This is populated elsewhere in the code
Private Sub CheckSurroundings()
For i As Integer = 1 To (xBoxes)
For j As Integer = 1 To (yBoxes)
For x = 1 As Integer To (xBoxes)
For y = 1 As Integer To (yBoxes)
Tile(New Point(i, j)).CheckDistance(Tile(New Point(x, y)))
Next y
Next x
Next j
Next i
End Sub
Private Function Tile(ByVal aPoint As Point) As clsTile
Return gBoard.Item("r" & aPoint.Y & "c" & aPoint.X)
End Function
In clsTile i have the following (as well as other items):
Private Function SurroundingTerrain(ByVal aTer As String) As clsTerrain
Return mySurroundings.Item(aTer) ' a small collection (6 items of clsTerrain type)
End Function
Public Sub CheckDistance(ByRef aTile As clsTile)
SurroundingTerrain(aTile.Terrain).CheckDistance(CalcDistance(Location, aTile.Location))
End Sub
Private Function CalcDistance(ByVal myPoint As Point, ByVal aPoint As Point) As Double
Dim myReturn As Double = 0
Dim xDiff As Integer = 0
Dim yDiff As Integer = 0
Dim tDiff As Integer = 0
xDiff = Math.Abs(myPoint.X - aPoint.X)
yDiff = Math.Abs(myPoint.Y - aPoint.Y)
tDiff = xDiff + yDiff
myReturn = (MinInt(xDiff, yDiff) * 1.4) + (tDiff - MinInt(xDiff, yDiff))
Return myReturn
End Function
Private Function MinInt(ByVal a As Integer, ByVal b As Integer) As Integer
Dim myReturn As Integer = a
If b < myReturn Then
myReturn = b
End If
Return myReturn
End Function
in clsTerrain i have the following sub that is called:
Public Sub CheckDistance(ByVal aDist As Double)
If aDist < Distance Then
Distance = aDist
End If
End Sub
This runs and works file but as you can guess it runs so slow... I have been trying to work out how to make this run faster and i looked into threading/tasks but it doesnt seem to work. There are no errors but the objects don't appear to update correctly (or at all). The code i tried was:
In the main class:
Private Sub CheckSurroundings()
Dim tasks As New List(Of Task)
Dim pTile As clsTile
For Each pTile In gBoard
tasks.Add(Task.Run(Sub() TileToCheck(pTile)))
Next
Task.WaitAll(tasks.ToArray())
End Sub
Private Sub TileToCheck(ByRef aTile As clsTile)
For x As Integer = 1 To (xBoxes)
For y As Integer = 1 To (yBoxes)
aTile.CheckDistance(Tile(New Point(x, y)))
Next y
Next x
End Sub
Does anyone have any suggestions or ideas for how to get this to work?
Sorry for any headaches or facepalms caused...

Sorted List in Array Visual Basic [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 7 years ago.
I have a program that creates a list of 25 random numbers from 0 to 1,000. I have to buttons the first button will load a list box with the random numbers and the second button will sort the list of numbers from the smallest to largest which is where I implemented bubble sort code. Now the other list box that is supposed to hold the sorted numbers doesn't work properly it only shows one number instead of all of them.
Here is my code:
Option Strict On
Public Class Form1
Dim rn As Random = New Random
Dim Clicked As Long = 0
Dim numbers, sort As Long
Private Sub GenerateBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GenerateBtn.Click
Clicked += 1
For x = 0 To 25
numbers = rn.Next(0, 1000)
RandomBox.Items.Add(numbers)
If Clicked >= 2 Then
RandomBox.Items.Clear()
Clicked = 1
End If
Next
End Sub
Private Sub SortBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SortBtn.Click
Dim Sorted() As Long = {numbers}
Dim Swapped As Boolean
Dim endOfArray As Integer = Sorted.Length - 1
Dim Tmp As Byte
While (Swapped)
Swapped = False
For I = 0 To endOfArray - 1
If Sorted(I) > Sorted(I + 1) Then
Tmp = CByte(Sorted(I))
Sorted(I) = Sorted(I + 1)
Sorted(I + 1) = Tmp
Swapped = True
End If
endOfArray = endOfArray - 1
Next
End While
SortBox.Items.Clear()
For I = 0 To Sorted.Count - 1
SortBox.Items.Add(Sorted(I))
Next
End Sub
End Class
Change your:
Dim Sorted() As Long = {numbers}
to
Sorted(x) = numbers
edit: Since you changed your code. You need to put back in the line that loads the Sorted Array.
For x = 0 To 25
numbers = rn.Next(0, 1000)
RandomBox.Items.Add(numbers)
Sorted(x) = numbers
If Clicked >= 2 Then
RandomBox.Items.Clear()
Clicked = 1
End If
Next
and remove the:
Dim Sorted() As Long = {numbers}
from the second part and put this declaration back in the beginning like you had:
Dim Sorted(26) as Long
The way you have will only show the latest random number. It is not any array but a single entity. Therefore only the latest will be add into the array. You need to load each number into the array as you create each one. Thus the (x) which loads it into position x.
You didn't use any arrays at all in your project...you're using the ListBox as your storage medium and that's a really bad practice.
I recommend you set it up like this instead:
Public Class Form1
Private rn As New Random
Private numbers(24) As Integer ' 0 to 24 = 25 length
Private Sub GenerateBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GenerateBtn.Click
For x As Integer = 0 To numbers.Length - 1
numbers(x) = rn.Next(0, 1000)
Next
' reset the listbox datasource to view the random numbers
RandomBox.DataSource = Nothing
RandomBox.DataSource = numbers
' empty out the sorted listbox
SortBox.DataSource = Nothing
End Sub
Private Sub SortBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SortBtn.Click
' make a COPY of the original array that we will sort:
Dim sorted(numbers.Length - 1) As Integer
Array.Copy(numbers, sorted, numbers.Length)
Dim Swapped As Boolean = True
Dim endOfArray As Integer = Sorted.Length - 1
Dim Tmp As Integer
While (Swapped)
Swapped = False
For I As Integer = 0 To endOfArray - 1
If sorted(I) > sorted(I + 1) Then
Tmp = sorted(I)
sorted(I) = sorted(I + 1)
sorted(I + 1) = Tmp
Swapped = True
End If
Next
endOfArray = endOfArray - 1
End While
' reset the listbox datasource to view the sorted numbers
SortBox.DataSource = Nothing
SortBox.DataSource = sorted
End Sub
End Class
Also, note that you were decrementing endOfArray inside your for loop. You should only decrement it after each pass; so outside the for loop, but inside the while loop.
Additionally, you were using a Tmp variable of type Byte, but generating numbers between 0 and 999 (inclusive). The Byte type can only hold values between 0 and 255 (inclusive).
Your Bubble Sort implementation was very close to correct!

Visual Basic - Looping an array 100 times not working?

I'm trying to loop this array 100 times (gets a number from 0-99,999). I've tried it several different ways with no luck. I've got it set up so you click a button and it generates the array. The user will then enter a number and hit the other button to check how many times that number shows up in the group of a 100 numbers. Any thoughts how to fix it? I'm stuck.
Dim i As Integer
Dim rnd As New Random()
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEnter.Click
Dim num As Integer
If Int32.TryParse(txtEnter.Text, num) AndAlso num >= 0 AndAlso num < 10 Then
lblNumber.Text = i.ToString()
Dim counts = From c In i.ToString()
Group By c Into Group
Select DigitGroup = New With {.Count = Group.Count(), .Digit = c, .Group = Group}
Order By DigitGroup.Count Descending, DigitGroup.Digit
Dim numCount = counts.FirstOrDefault(Function(grp) grp.Digit.ToString() = num.ToString())
If numCount IsNot Nothing Then
Dim numMessage = String.Format("There are {0} number {1}'s found.", numCount.Count, num)
MessageBox.Show(numMessage)
Else
MessageBox.Show("Your number does not contain a " & num)
End If
Else
MessageBox.Show("Please enter a number between 0 and 9.")
End If
End Sub
Private Sub btnGetNumber_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGetNumber.Click
btnEnter.Enabled = True
i = 0
Do Until i > 100
Dim randomInt = rnd.Next(0, 100000)
i = i + 1
i = rnd.Next(0, 100000)
Loop
End Sub
End Class
First of all, it is common to use For Loops when you know the number of loops.
Secondly, you forgot to increment i in the until loop
Finally, you don't need to redeclare rnd in every loop. Something like that sounds better to me :
Dim rnd As New Random()
For i = 1 To 100
Dim randomInt = rnd.Next( 0 , 100000 )
Next
Move the New Random() out of that loop. Make it global and create the instance just one time.
(The Random generator starts using a seed obtained from the current time. Declaring a Random variable inside a loop result in the same seed used again and again. This gives back the same number defeating the randomness of the function) It is like: http://xkcd.com/221/
See another explanation here
Of course, if you don't increment the variable i used to control the loop, you will loop forever
Dim i As Integer
Dim rnd As New Random()
.....
i = 0
Do Until i > 100
Dim randomInt = rnd.Next(0, 100000)
i = i + 1
Loop
Said that, now I can't get what are you trying to do here. There is no array to check and you do not use the value obtained from the Random generator, simply, your i variable exits with value 101.
Now I am just guessing what are your intentions, but perhaps you want just this?
i = rnd.Next(0, 100000)
This will give to the variable i a random number and your subsequent code checks how many, of the required digit, are present in i
I see that you are not adding the variable "i" to increment:
i = 1
Do Until i > 100
Dim rnd As New Random()
Dim randomInt = rnd.Next(0, 100000)
i+=1 'Increment value of i by 1, same as i = i + 1
Loop
The variable of "i" will always be 1 if not incremented. Check that and let me know if that solves the problem!