How to loop a sub w/ a variable value? - vb.net

I'm creating a program that outputs a randomly generated password in which the user has jurasdiction over how many characters they want their password to be. How would i go about taking the input from the user and then looping the subroutine (which generates a single random character)? Any help would be much appreciated, thank you!
' this is the code for my input where n = the no. of characters the user wants in their pasword
Public Sub inp()
Console.WriteLine("How many characters do you want in your password?
(up to 20 allowed)")
n = Console.ReadLine()
End Sub
'this is the simple sub that generates a random number from which a character from my database is chosen
Public Sub randi()
Randomize()
i = Rnd() * 92
End Sub

Let us think about the steps envolved.
Ask user for a number
Place the response in a variable.
Create a random string with n characters
I looks like you have taken care of 1 and 2.
Now let's break down number 3 into tasks.
What characters do you want to include.
Uppercase letters? Lowercase letters? Numbers? Punctuation?
An ASCII table will tell you that there are readable characters from ASCII 33 to 126.
If you were to ask for a random number in the range 33 to 126 then change it to the character it represnts, it would be a start.
Luckily .net provides a Random class.
First we will need to get an instance of that class to use its methods.
Private rnd As New Random
Now we have a variable holding an instance of the random class so we can use any of the classed properties and methods.
There is a method, .Next(Int32, Int32), that will produce a random number in a range.
The first parameter is the bottom number in the range and the second is one more than the top number.
Going back to ASCII table we need a random number like this
rnd.Next(33, 125)
Next we need to change the result to a character.
Convert.ToChar(Ascii number)
The next problem is to get the number of characters the user asked for. Remember n.
We can do this is a For...Next loop.
Inside the loop we want to build a string with these random characters. This could be done with an ampersand equals but every time you changes a string the compiler has to throw away the old string and create an entirely new one because strings are immutable. To solve this .net has provided a StringBuilder class. We create an instance of this class and then use the .Append method to add the characters to the sb.
After the loop we change the StringBuilder to a regular string and return it to the calling code.
Public Sub Main()
Console.WriteLine("How many characters do you want in your password?
(up to 20 allowed)")
Dim n As Integer = CInt(Console.ReadLine())
Dim Pword As String = GetRandomString(n)
Console.WriteLine(Pword)
End Sub
Private rnd As New Random
Private Function GetRandomString(stringLength As Integer) As String
Dim sb As New StringBuilder
For c = 1 To stringLength
Dim newAscii = rnd.Next(33, 127)
Dim newChr = Convert.ToChar(newAscii)
sb.Append(newChr)
Next
Return sb.ToString
End Function
In a real application you would have to check if the user entered a valid number. An Integer.TryParse and a check for 0 would probably do it.

Related

Using loops to allow me to enter several numbers into a console application program, and then do calculations

This is basically the details of my assignment
Using loops, write a visual basic console application will allow you to enter an arbitrary amount of numbers. The application should then calculate the average of all the numbers entered by the user. Input should be validated using loops to ensure the data entered is a valid number. If an entered value is not valid, the user should be prompted until they enter a valid number.
Output the following to the Console
Sum of the Numbers Entered:
Total Number of Numbers Entered:
Average of Numbers Entered:
The way I am thinking about doing it is:
Have the user prompt for a number
check to see if it's numeric or not
if its not numeric it prompts again
if it is numeric, it prompts for more numbers
have an option where a user can type a key or keyword, and the program stops collecting numbers, and does the calculations, outputting the following three things that were required.
My issue is that I can't really visualize on how to do it, or really how to write it in such a way. My initial issue with learning so far is not being able to see how it works, or an example of the code, but when I do - I can understand it and use it efficiently. I just have trouble visualizing how to do/write it.
Dim str_num As String
Dim int_num As Integer
Dim int_counter As Integer
Sub Main()
Console.BackgroundColor = ConsoleColor.DarkBlue
Console.ForegroundColor = ConsoleColor.White
Console.Clear()
Do
Console.WriteLine("enter a number") 'prompts user to enter a number, followed by it storing in a variable.
str_num = Console.ReadLine
If Not IsNumeric(str_num) Then
int_num = str_num
Console.ReadKey()
End If
Loop
End Sub
Comments and explanations in-line.
Sub Main()
Dim num As Integer
'With a list, unlike an array, we don't have to know beforehand how many items we are going to add.
Dim lst As New List(Of Integer)
Do
Console.WriteLine("Enter a whole number.")
'A nested Do will test for a proper number
Do
'Integer.TryParse will return true if a valid number was entered and fill in the
'num variable with that number
If Integer.TryParse(Console.ReadLine, num) Then
'Since we have a valid number we add it to the lst
lst.Add(num)
'and exit the inner Do
Exit Do
Else
'If it isn't valid, we ask again for an input, the inner loop continues until we get a valid input
Console.WriteLine("Sorry, not a valid number. Please try again")
End If
Loop
Console.WriteLine("Is that enough numbers? Yes, No")
'Call .ToUpper on the input so we don't have to depend on what case the user typed in
If Console.ReadLine.ToUpper = "YES" Then
Exit Do
End If
Loop
'We can use the .Sum method and .Average method of the list to do the calculaion
Dim sumNum = lst.Sum()
'I have used Interpolated strings indicated by the $. These allow you to insert a variable
'surrounded by braces {} directly into a string.
'In Visual Studio prior to version 2015
'use Console.WriteLine(String.Format("The sum of the numbers you entered is {0}", sumNum))
Console.WriteLine($"The sum of the numbers you entered is {sumNum}")
Dim avgNum = lst.Average()
Console.WriteLine($"The average of the numbers your entered is {avgNum}")
Console.ReadLine()
End Sub
You look like you want to code it yourself, but miss a couple resources. I'll give you resources. You'll implement the logic. Deal?
Here's a way to get an input from the user and verify if it's really a number:
Dim userInput As String
Console.WriteLine("Enter a number:")
userInput = Console.ReadLine()
Dim inputAsInteger As Integer = 0
If Not Int32.TryParse(userInput, inputAsInteger) Then 'TryParse returns a Boolean which reads True if it worked
Console.WriteLine("Hey, that wasn't a number!")
End If
Here's a loop logic which you can use to gather your inputs:
Dim stopLooping As Boolean = False
Dim i As Integer = 0 'I'll count the loops with this integer, but the exit condition can be anything
Do
'Some code which do your stuff
'At some point, a condifion can flip stopLooping to True when you want the loop to stop
Console.WriteLine("Loop number " & i.ToString)
If i > 10 Then
stopLooping = True
End If
Loop Until stopLooping
Now you should be able to figure out something out of these tools, right? I'm sure you will. Have fun!

Vb Guessing Game Random generator broken

In my code,I have now realised I have to use the New Random function, my code was working before with the Randomize and then the numbers but now it comes up with loads of errors and wont even let me run the program. I think it is only a small error but I just need some help to get the final bit going
Heres the code and thanks for any help :)
I cannot get the code to work with the randomly generated number and I have to use the New Random function I cannot use randomize() Does anybody know how to help here is the code.
Dim timestook As Int32 = 1
Dim usersguess As Integer
Dim value = New Random(0 - 19)
Console.WriteLine("You have to guess this number. It is between 1 and 20. Good Luck !")
usersguess = Console.ReadLine()
'keep looping until they get the right value
While usersguess <> value
'now check how it compares to the random value
If usersguess < value Then
timestook = timestook + 1
Console.WriteLine("You're too low. Go higher ")
ElseIf usersguess > value Then
Console.WriteLine("You're too high. Go Lower.")
timestook = timestook + 1
End If
'If they are wrong the code will run again,after telling the user if they are too high or too low.
usersguess = Console.ReadLine()
End While
' Console.WriteLine("You're correct. Well Done")
If usersguess = value Then
Console.WriteLine("You took,{0}", timestook)
End If
Console.ReadLine()
End Sub
You'll want to do some googling on how to use random numbers. Your problem is that you aren't creating a Random object to handle the random number generation.
Here's how you can fix your code:
Dim randNumGen As New Random() 'Create Random object
Dim value As Integer = randNumGen.Next(0, 20) 'set value equal to a new random number between 0-19
Please note that this code could be further refactored for readability and simplicity (like changing timestook = timestook + 1 to timestook += 1 and selecting better variable names like numberOfGuesses as opposed to timestook, etc.
The expression New Random(0-19) does not do at all what you think it does, name it does NOT return an integer. Instead, it creates an instance of a Random object, which is a type that knows how to create new random values. The 0-19 part of the expression is the seed for the Random object's constructor, and is the same as just passing the value -19.
This looks like it's either homework or personal practice, so I feel like you will be better served in this case with a separate example using the Random type for reference than you would if I fixed the code sample in the question for you:
Dim rnd As New Random()
For i As Integer = 0 To 10
Console.WriteLine(rnd.Next(0, 20))
Next i
It's also worth mentioning here that you typically only want one Random object for your entire program, or at least only one Random object for each logical part of your program. Creating new Random objects resets the seeds, and for best results you want to follow the same seed on subsequent calls to the same instance for a while.

Replacing Characters Simultaneously

Hey guys I'm trying to make a program that helps people encrypt messages and decrypt messages using the Caesar shift cipher, I know it's probably already been done, I want to have a go myself though.
The problem I've been having is when it comes to encrypting the text. The user selects a number (between 1-25) and then the application will change the letters corresponding to the number chosen, e.g. if the user inputs "HI" and selects 2, both characters are moved two places down the alphabet outputting "JK". My main problem is the replacing characters though, mostly because I've set up the program to be able to encrypt large blocks of text, because my code is:
If cmbxKey.Text = "1" Then
If txtOutput.Text.Contains("a") Then
sOutput = txtOutput.Text.Replace("a", "b")
txtOutput.Text = sOutput
End If
If txtOutput.Text.Contains("b") Then
sOutput = txtOutput.Text.Replace("b", "c")
txtOutput.Text = sOutput
End If
End If
This means if the user inputs "HAY" it will change it to "HBY" and then because of the second if statement it will change it to "HCY" but I only want it to be changed once. Any suggestions to avoid this???? Thanks guys
Since you want to shift all characters, start out by looping though the characters using something like ToArray:
For each s as string in txtOutput.Text.ToArray
'This will be here for each character in the string, even spaces
Next
Then, rather than having cases for every letter, look at it's ascii number:
ACS(s)
...and shift it by the number you want to. Keep in mind that if the number is greater than (I don't know if you want upper/lower case) 122, you want to subtract 65 to get you back to "A".
Then you can convert it back into a character using:
CHR(?)
So this might look something like this:
Dim sb as new text.StringBuilder()
For each s as string in txtOutput.Text.ToArray
If asc(s) > 122 Then
sb.append(CHR(ASC(s) + ?YourShift? - 65)
Else
sb.append(CHR(ASC(s) + ?YourShift?)
END IF
Next
txtOutput.Text = sb.ToString
A very simple method of changing your application while keeping your strategy is to replace the lower case characters with upper case characters. Then they won't be recognized by the Replace method anymore.
Obviously, the problem is that you want to implement an algorithm. In general, an algorithm should be smart in the sense that you don't have to do the grunt work. That's why a method such as the one presented by Steve is smarter; it doesn't require you to map each character separately, which is tedious, and - as most tedious tasks - error prone.
One big issue arise when you're facing a String that the basic Alphanumeric table can't handle. A String that contains words like :
"Déja vu" -> The "é" is going to be what ?
And also, how about encoding the string "I'm Aaron Mbilébé" if you use .ToUpper().
.ToUpper returns "I'M AARON MBILÉBÉ".
You've lost the casing, and how do you handle the shifting of "É" ?
Of course, a code should be smart as pointed above, and I was used to deal with strings just by using the System.Text.ASCIIEncoding to make things easier. But from the moment I started to use large amount of textual datas, sources from the web, files (...) I was forced to dig deeper, and seriously consider string encoding (and System Endianness by the way, when coding and decoding string to/from array of bytes)
Re-think of what do you really want in the end. If you're the only one to use your code, and you're certain that you'll only use A..Z, 0..9, a..z, space and a fixed amount of allowed characters (like puntuation) then, just build a Table containing each of those chars.
Private _AllowedChars As Char() = { "A"c, "B"c, ... "0"c, "1"c, .. "."c, ","c ... }
or
Private _AllowedChars As Char() = "ABCDEF....012...abcd..xyz.;,?:/".ToCharArray()
Then use
Private Function ShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
Dim AllChars As Char() = CurrentString.ToCharArray()
Dim FinalChars As Char()
Dim i As Integer
FinalChars = New Char(AllChars.Length - 1) {} ' It's VB : UpperBound is n+1 item.
' so n items is UpperBound - 1
For i = 0 To AllChars.Length - 1
FinalChars(i) = _AllowedChars((Array.IndexOf(_AllowedChars, AllChars(i)) + ShiftValue) Mod _AllowedChars.Length)
Next
Return New String(FinalChars)
End Function
And
Private Function UnShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
' ... the same code until :
FinalChars(i) = _AllowedChars((Array.IndexOf(_AllowedChars, AllChars(i)) - ShiftValue + _AllowedChars.Length) Mod _AllowedChars.Length)
' ...
End Function
^^ Assuming ShiftValue is always positive (defined once)
But again, this only works when you have a predefined set of allowed characters. If you want a more flexible tool, you ought to start dealing with encodings, array of byte, BitConverter and have a look at system endianness. That's why I asked if someone else is goind to use your application : let's try this string :
"Xin chào thế giới" ' which is Hello World in vietnamese (Google Trad)
In that case, you may give up..? No ! You ALWAYS have a trick in your cards !
Just create your allowed chars on the fly
Private _AllowedChars As New SortedList(Of Char, Char)
-> get the string to encode (shift)
Private Function ShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
Dim AllChars As Char() = CurrentString.ToCharArray()
Dim FinalChars As Char()
Dim i As Integer
' Build your list of allowed chars...
_AllowedChars.Clear()
For i = 0 To AllChars.Length - 1
If Not _AllowedChars.ContainsKey(AllChars(i)) Then
_AllowedChars.Add(AllChars(i), AllChars(i))
End If
Next
' Then, encode...
FinalChars = New Char(AllChars.Length - 1) {}
For i = 0 To AllChars.Length - 1
FinalChars(i) = _AllowedChars.Keys.Item((_AllowedChars.IndexOfKey(AllChars(i)) + ShiftValue) Mod _AllowedChars.Count)
Next
Return New String(FinalChars)
End Function
The same for Unshift/decode.
Note : in foreing languages, the resulting string is pure garbage and totally unreadable, unless you (un)shift the chars again.
However, the main limitation of this workaround is the same as the fixed chars array above : Once you encode your string, and add a char in your encoded string that doesn't exists in the initial generated allowed chars, then you've nuked your data and you won't be able to decode your string. All you'll have is pure garbage.
So one day... one day maybe, you'll have to dig deeper at the byte level of the thing, in a defined extended encoding (Unicode/UTF8/16) to secure the integrity of your data.

Random from listbox

there has been many posts about this but nobody seems to have my problem.
I have a list box with letters a-z (line for each letter)
using
Dim rancon As New Random
Dim rc As Integer = ListBox1.Items.Count
Label5.Text = lcon.Items.Item(rancon.Next(rc)).ToString
all it is doing is chosing randomly, 1 of the first 7 or 8 characters.
can anyone advise?
I assume that you expect that the random always chooses a different item in the ListBox. Therefore you have to reuse the same Random instance since the default constructor derives the seed(which is used to initialize the pseudorandom number generator) from the current system time.
If you call this code very fast(for example in a loop), the seed will always be the same. Hence you get repeating numbers/items.
To avoid this you could make the Random a field in the class:
Private rancon As New Random
Public Sub YourMethod()
Dim rc As Integer = ListBox1.Items.Count
Label5.Text = ListBox1.Items(rancon.Next(rc)).ToString()
End Sub
MSDN:
The default seed value is derived from the system clock and has finite
resolution. As a result, different Random objects that are created in
close succession by a call to the default constructor will have
identical default seed values and, therefore, will produce identical
sets of random numbers. This problem can be avoided by using a single
Random object to generate all random numbers. You can also work around
it by modifying the seed value returned by the system clock and then
explicitly providing this new seed value to the Random(Int32)
constructor.
Note that i've also used ListBox1.Items instead of lcon.Items in case that was a typo.

Getting Duplicate Random Numbers

I'm doing basic random number generation using this Shared Function:
Public Shared Function RandomNumber(ByVal MaxNumber As Integer, Optional ByVal MinNumber As Integer = 0) As Integer
'initialize random number generator
Dim r As New Random(Date.Now.Ticks And &HFFFF)
If MinNumber > MaxNumber Then
Dim t As Integer = MinNumber
MinNumber = MaxNumber
MaxNumber = t
End If
Return r.Next(MinNumber, MaxNumber)
End Function
Called like this: dim x as integer = Random(2100000000)
Very simple, and the seed value comes straight from a MS example.
HERE'S THE PROBLEM: I'm getting duplicate numbers on occasion, but always created at times that are usually at least 5 or 10 minutes apart. I can see if I was calling the function multiple times per second or millisecond, because that'd kind of "breaks" the seed. But these are showing up at extended time spans. What else could be causing this?
Duplicate seed issue?
It might be better defining r as static so that it is initialised once when first invoked. Refer to this answer Random integer in VB.NET
The Random constructor takes an Integer as its parameter which is 32-bits. As spencer7593 said, with only 16 bits, you're repeating the sequence every 6.5ms. Try:
Dim r As New Random(Date.Now.Ticks And &HFFFFFFFF)
However, this will do the same thing:
Dim r As New Random()
Better yet, don't create a new Random object each time:
Private Static r As New Random()
Public Shared Function RandomNumber(MaxNumber As Integer, Optional MinNumber As Integer = 0) As Integer
...
Return r.Next(MinNumber, MaxNumber)
End Function
Q: What else could be causing this?
A: It could be happening purely by random. Random numbers are just that: random. At any point in time, whether its seconds or hours away from another point in time, its just as likely for a number to appear as any other number. There is no guarantee that a number won't be repeated.
On the other hand, it looks like your seed value is only on the order of 16 bits. And that's like a total of 65,536 possibilities. There's 10,000 ticks in a millisecond, so ever 6.5 milliseconds you have the possibility of reusing the same seed.
It's not at all clear whether the VB Random is using some other kind of entropy beyond that seed or not. (But gathering entropy for inclusion would slow down the initialization, so it may not be, as a performance consideration.)
According the docs, creating two Random objects using the same seed value results in Random objects that create duplicate sequences of unique numbers.
http://msdn.microsoft.com/en-us/library/ctssatww.aspx
I think that answers the question why it is happening.
I guess the next question is why do you need to instantiate a new Random object? If you need multiple objects, then instantiating several of them, but making sure you are using a different seed value for each one would be one approach.
But before you go there, I recommend you consider using just one Random. Calls to get random numbers can be serviced from an existing Random, rather than creating a new one every time you need a random number.
Try it another way:
Public Function RandomNumber2(ByVal MaxNumber As Integer, Optional ByVal MinNumber As Integer = 0) As Integer
' Initialize the random-number generator.
Randomize()
' Generate random value between MaxNumber and MinNumber.
Return CInt(Int((MaxNumber * Rnd()) + MinNumber))
End Function
See Randomize Function (Visual Basic) for more details. Hope this helps.