I'l trying to generate a unique random number generator with the snippet of code from below, but it's not working. The IF section is suppose to test if it's the first random number generated, if it is, it's suppose to add the first random number to the ArrayList, if it's not the first random number, it's supposed to check if the random number is already in the ArrayList and if it's in the ArrayList it's suppose to MsgBox and generate a new unique random number that is not already in the ArrayList and add it to the ArrayList, but it's not doing any of those. Any help would be greatly appreciated.
Public Class Form1
Dim r As New Random
Dim dLowestVal As Integer = 1
Dim dHighestVal As Integer = 26
Dim dItemAmount As Integer = 1
Dim RollCheck As New HashSet(Of Integer)
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnExit.Click
End
End Sub
Private Sub btnRollDice_Click(sender As Object, e As EventArgs) Handles btnRollDice.Click
lblRandomNo.Text = r.Next(dLowestVal, dHighestVal)
lblItemAmount.Text = dItemAmount
If dItemAmount = 1 Then
RollCheck.Add(Val(lblRandomNo.Text))
ElseIf (RollCheck.Contains(Val(lblRandomNo.Text))) Then
MsgBox("Already Exists")
lblRandomNo.Text = r.Next(dLowestVal, dHighestVal)
RollCheck.Add(Val(lblRandomNo.Text))
End If
dItemAmount = dItemAmount + 1
Thanks in advance.
You could replace your whole method with this simple one
' This is globally declared at the top of your form
Dim values As New List(Of Integer)
' This is called when you construct your form
' It will store consecutive integers from 1 to 25 (25 elements)
values = Enumerable.Range(1, 25).ToList()
This is the method that extract an integer from your values that is not already used
Private Sub Roll()
' Get an index in the values list
Dim v = r.Next(0, values.Count)
' insert the value at that index to your RollCheck HashSet
RollCheck.Add(values(v))
' Remove the found value from the values list, so the next call
' cannot retrieve it again.
values.Remove(values(v))
End Sub
And you can call it from the previous event handler in this way
Private Sub btnRollDice_Click(sender As Object, e As EventArgs) Handles btnRollDice.Click
if values.Count = 0 Then
MessageBox("No more roll available")
else
Roll()
End Sub
End Sub
The point of the HashSet is that since it doesn't allow duplicates you can just check the return value of Add() to determine whether the number was successfully inserted or if it already exists in the list.
If you want to keep trying until it succeeds all you have to do is wrap it in a loop:
If dHighestVal - dLowestVal >= RollCheck.Count Then
'If the above check passes all unique values are MOST LIKELY already in the list. Exit to avoid infinite loop.
MessageBox.Show("List is full!")
Return 'Do not continue.
End If
Dim Num As Integer = r.Next(dLowestVal, dHighestVal)
'Iterate until a unique number was generated.
While Not RollCheck.Add(Num)
MessageBox.Show("Already exists!")
Num = r.Next(dLowestVal, dHighestVal)
End While
lblRandomNo.Text = Num
An alternative way of writing the loop is: While RollCheck.Add(Num) = False.
Related
I am trying to take Items that I have already added to a list.
Dim lista As New List(Of String)
n = 2
i = 0
Do While i < n + 1
Randomize()
a = Int(Rnd() * 4) + 1
If a = 1 Then
lista.Add("1b")
ElseIf a = 2 Then
lista.Add("2b")
ElseIf a = 3 Then
lista.Add("3b")
ElseIf a = 4 Then
lista.Add("4b")
End If
i = i + 1
Loop
Lets imagine that the list i got was {2b,4b,1b}. Now i want to know how to get lets say just 2b from the list as a first Item and then delete it from the list.
This would be a good place to use a Queue(Of ).
Dim item As String
item = queuea.Dequeue
But List(Of ) has an indexer so you can just use it like an array:
Dim item As String
item = lista(0)
' Then remove the first item:
lista.RemoveAt(0)
Use the .net Random class instead of the old VB6 methods. Calling Random.Next(Interger1, Integer2) will return an Integer equal to or greater than Integer1 and less than Integer2. Note that since the values are so limited there is a good chance of having duplicates in the list.
Although multiple ElseIfs will work, a Select Case is easier to read and less typing.
I put the contents of the list in a ListBox so we could see what was going on. I used the Remove method of List(Of T) then rebind the ListBox.
Private lista As New BindingList(Of String)
Private rand As New Random
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BuildList()
ListBox1.DataSource = lista
End Sub
Private Sub BuildList()
Dim i = 0
Dim a As Integer
Do While i < 3 'since n never changes just use the literal value
a = rand.Next(1, 5)
Select Case a
Case 1
lista.Add("1b")
Case 2
lista.Add("2b")
Case 3
lista.Add("3b")
Case 4
lista.Add("4b")
End Select
i = i + 1
Loop
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'Remove from list
lista.Remove(ListBox1.SelectedItem.ToString)
End Sub
I have a form with a button and a label. I also have a text file with the following contents:
Bob:Available:None:0
Jack:Available:None:0
Harry:Available:None:0
Becky:Unavailable:Injured:8
Michael:Available:None:0
Steve:Available:None:0
Annie:Unavailable:Injured:12
Riley:Available:None:0
The values in the text file are:
person-name:available-or-unavailable:sick-or-injured:months-they-will-be-unavailable
What I would like to do is to have the user click the button and a random (available) person will be selected from the text file. The label's text will then say:
personname & " has gotten injured and will be unavailable for 10 months."
I would then like to overwrite the text file with the corresponding values for that particular person. For example that person's second value will now be "Unavailable", the third value will be "Injured" and the fourth value will be 10.
I hope this makes sense.
I don't have any code, as I literally have no idea how to do this. Any help would be much appreciated!
Explanations and code in line.
Private r As New Random
Private RandomIndex As Integer
Private dt As New DataTable
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddColumnsToDataTable()
FillDataTable()
End Sub
Private Sub AddColumnsToDataTable()
'Need to prepare the table to receive the data
dt.Columns.Add("Name")
dt.Columns.Add("Available")
dt.Columns.Add("Injury")
dt.Columns.Add("Months")
End Sub
Private Sub FillDataTable()
'ReadAllLines returns an array of lines in the text file
Dim lines = File.ReadAllLines("workers.txt")
'Loop through each line in the lines array
For Each line As String In lines
'.Split returns an array based on splitting the line
'by the colon. The c following ":" tells the compiler
'that this is a Char which the split function requires.
Dim items = line.Split(":"c)
'We can add a row to the data table all at once by
'passing in an array of objects.
'This consists of the elements of the items array
dt.Rows.Add(New Object() {items(0), items(1), items(2), items(3)})
Next
'Now we have an in memory DataTable that contains all the data from the text file.
End Sub
Private Function GetRandomWorker() As String
'A list of all the row indexes that are available
Dim AvailableList As New List(Of Integer)
For i As Integer = 0 To dt.Rows.Count - 1
'Loop through all the data in the date table row by row
If dt.Rows(i)("Available").ToString = "Available" Then
'Add only the row indexes that are Available
AvailableList.Add(i)
End If
Next
'Get a random index to use on the list of row indexes in IndexList
If AvailableList.Count = 0 Then
'No more workers left that are Available
Return ""
End If
'Get a random number to use as an index for the available list
Dim IndexForList = r.Next(AvailableList.Count)
'Selects a row index based on the random index in the list of Available
RandomIndex = AvailableList(IndexForList)
'Now use the index to get information from the row in the data table
Dim strName = dt.Rows(RandomIndex)("Name").ToString
Return strName
End Function
Private Sub SaveDataTable()
'Resave the whole file if this was a real app you would use a database
Dim sb As New StringBuilder
'A string builder keeps the code from creating lots of new strings
'Strings are immutable (can't be changed) so every time you think you are
'changing a string, you are actually creating a new one.
'The string builder is mutable (changable)
For Each row As DataRow In dt.Rows
'The ItemsArray returns an array of objects containing all the
'values in each column of the data table.
Dim rowValues = row.ItemArray
'This is a bit of Linq magic that turns the values into strings
Dim strRowValues = From s In rowValues
Select DirectCast(s, String)
'Now that we have strings we can use the String.Join with the colon
'to get the format of the text file
sb.AppendLine(String.Join(":", strRowValues))
Next
'Finally we change the StringBuilder to a real String
'The workers.txt is stored in the Bin\Debug directory so it is current directory
'no additional path required
File.WriteAllText("workers.txt", sb.ToString)
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
SaveDataTable()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim WorkerName As String = GetRandomWorker()
If WorkerName = "" Then
MessageBox.Show("There are no available workers")
Return
End If
Label1.Text = $"{WorkerName} has gotten injured and will be unavailable for 10 months."
dt.Rows(RandomIndex)("Available") = "Unavailable"
dt.Rows(RandomIndex)("Injury") = "Injured"
dt.Rows(RandomIndex)("Months") = "10"
End Sub
Public Class Form1
Private Sub btnCalculate_Click(sender As Object, e As EventArgs) Handles btnCalculate.Click
Dim EvenNum, EvenNumCount, EvenNumAverage, Number, Result As Integer
Calculations(EvenNum, EvenNumCount)
GetInput(Number)
Output(Result)
End Sub
Sub GetInput(ByRef Number)
Number = txtInput.Text
End Sub
Sub Calculations(ByRef EvenNum, ByRef EvenNumCount)
Dim ListedNumbers, lstOutputSize As Integer
GetInput(lstOutputSize)
For i As Integer = 0 To lstOutputSize - 1
ListedNumbers = InputBox("Enter Numbers", "Input")
lstOutput.Items.Add(ListedNumbers)
Next
For i As Integer = 0 To lstOutput.Items.Count - 1
If (CInt(lstOutput.Items(i)) Mod 2 = 0) Then
EvenNum += lstOutput.Items(i)
EvenNumCount += 1
End If
Next
End Sub
Function Average(ByRef EvenNumAverage As Integer) As Integer
Dim EvenNum, EvenNumCount As Integer
Calculations(EvenNum, EvenNumCount)
EvenNumAverage = EvenNum / EvenNumCount
Return EvenNumAverage
End Function
Sub Output(ByRef EvenNumAverage)
lstOutput.Items.Add(Average(EvenNumAverage))
End Sub
The program is supposed to get input from a textbox for a desired number of numbers to be entered into a listbox from inputboxes.
It is then supposed to get the average of only the even numbers and then display that average into the listbox.
In it's current state the program will do what it is intended to do, it just repeats the calculation code. This only happens when I add the Output call statement under the button procedure.
You're calling Calculations twice
From btnCalculate_Click
From Average which is called by Output
I have 2 buttons and a DataGridView with 2 Columns (0 & 1).
The 1st button transfers a randomized cell from the Column(1) to a TextBox. Then, it stores that Cell in variable (a), plus the cell that opposites it in variable (b).
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim rnd As New Random
Dim x As Integer = rnd.Next(0, Form1.DataGridView1.Rows.Count)
Dim y As Integer = 1
Dim a As String = Form1.DataGridView1.Rows(x).Cells(y).Value
Dim b As String = Form1.DataGridView1.Rows(x).Cells(y - 1).Value
TextBox3.Text = a
End Sub
The 2nd button, however, is supposed to compare if another TextBox's text has the same string variable (b) has as Strings. Now, if so, then it has to display a certain message and so on...
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If TextBox4.Text = b Then '<<< ISSUE HERE!
MsgBox("Correct! ^_^")
ElseIf TextBox4.Text = "" Then
MsgBox("You have to enter something first! O_o")
Else
MsgBox("Wrong! >,<")
End If
End Sub
The problem is that the variable (b) is surely not shared across the two "private" subs. And so, there is NOTHING to compare to in the 2nd button's sub! I presume that the solution here is to split the "randomization process" into a separate function, then execute it directly when the 1st button gets activated. Furthermore, that function's variables have to be SHARED somehow, and I certainly don't know how!
Thanks for Mr. Olivier, the code has been improved significantly! Yet, I still encounter a "wrong" comparison issue, somehow!
Dim RND As New Random
Dim x As Integer
Private Function GetCell(ByVal rowIndex As Integer, ByVal cellIndex As Integer) As String
Return Form1.DataGridView1.Rows(rowIndex).Cells(cellIndex).Value
End Function
Private Sub btnRoll_Click(sender As Object, e As EventArgs) Handles btnRoll.Click
x = RND.Next(0, Form1.DataGridView1.Rows.Count)
tbxRoll.Text = GetCell(x, 1)
End Sub
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
If tbxSubmit.Text = GetCell(x, 0) Then
MsgBox("Correct! ^_^")
ElseIf tbxSubmit.Text = "" Then
MsgBox("You have to enter something first! O_o")
Else
MsgBox("Wrong! >,<")
End If
End Sub</code>
Well, unbelievably, I read a guide about "comparison operations" in VB.net and tried out the first yet the most primal method to compare equality - which was to use .Equals() command - and worked like a charm! Thank God, everything works just fine now. ^_^
If tbxSubmit.Text.Equals(GetCell(x, 0)) Then
Alright now... This is going to sound weird! But, following Mr. Olivier's advise to investigate "debug" the code, I rapped the string I'm trying to compare with brackets and realized that it's been outputted after a break-line space! So, I used the following function to remove the "white-space" from both of the comparison strings! And it bloody worked! This time for sure, though. ^_^
Function RemoveWhitespace(fullString As String) As String
Return New String(fullString.Where(Function(x) Not Char.IsWhiteSpace(x)).ToArray())
End Function
If RemoveWhitespace(tbxSubmit.Text) = RemoveWhitespace(GetCell(x, 0)) Then
Turn the local variables into class fields.
Dim rnd As New Random
Dim x As Integer
Dim y As Integer
Dim a As String
Dim b As String
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
x = rnd.Next(0, Form1.DataGridView1.Rows.Count)
y = 1
a = Form1.DataGridView1.Rows(x).Cells(y).Value
b = Form1.DataGridView1.Rows(x).Cells(y - 1).Value
TextBox3.Text = a
End Sub
These fields can now be accessed from every Sub, Function and Property.
Of course Button3_Click must be called before Button2_Click because the fields are initialized in the first method. If this is not the case then you should consider another approach.
Create a function for the Cell access
Private Function GetCell(ByVal rowIndex As Integer, ByVal cellIndex As Integer) _
As String
Return Form1.DataGridView1.Rows(rowIndex).Cells(cellIndex).Value
End Function
And then compare
If TextBox4.Text = GetCell(x, y - 1) Then
...
And don't store the values in a and b anymore. If y is always 1 then use the numbers directly.
If TextBox4.Text = GetCell(x, 0) Then
...
One more thing: give speaking names to your buttons in the properties grid before creating the Click event handlers (like e.g. btnRandomize). Then you will get speaking names for those routines as well (e.g. btnRandomize_Click).
See:
- VB.NET Class Examples
- Visual Basic .NET/Classes: Fields
Hello i am trying to make a texas hold'em style game and im at the point where i filled an array with numbers 1-52 randomly assorted. I first pull the value from the first index of the array and have the corresponding card picture be set to a picturebox value. I have saved the 52 card .png files in my resources as well. These pictures are also saved with their names as 1.png, 2.png, 3.png.... etc depending the suit and value.
I am sorting the values as 1-13 spades (2-ace), 14-26 hearts, 27-39 diamonds, 40-52 clubs.
I also just saw i should probably use a global counter to keep track of the deck position.
Public Class Form1
Dim Deal As MsgBoxResult
Dim CardDeck As New Random
Dim Counter As Integer = 1
Dim CardCount(52) As Integer
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles btnStart.Click
Deal = MessageBox.Show("Would you like to start a Game?", "Texas Holde'em", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
ShuffleDeck()
CalculateFirst3Cards()
End Sub
Private Sub ShuffleDeck()
If Deal = MsgBoxResult.Yes Then
For num As Integer = 1 To CardCount.Length - 1
Dim DeckValue As Integer = CardDeck.Next(1, 52)
CardCount(Counter) = DeckValue
Counter += 1
Next
End If
End Sub
Private Sub CalculateFirst3Cards()
Dim counter As Integer = 1
For num As Integer = 1 To 3
Dim hold As Integer = CardCount(counter)
Dim hold1 As String = Convert.ToString(hold)
River1.Image = My.Resources.
counter += 1
Next
End Sub
End Class