Making a Quiz In visual basic that requires a highscore feature which isnt working correctly - vb.net

I'm making a quiz and it requires a highscore function as said before. When I run the program everything is fine except that the highest score won't always be the highest score.
If I retake it, a lower score will still replace the highest. scores aren't showing in the 2nd and 3rd highest brackets either which I assume is with the same problem..
Here's the code-- (With each question answered correctly there is score += 1, then calling score update at the end of last question. )
Public score As Integer = 0
'best score
Dim highest As Integer
'second best
Dim scoreuno As Integer
'third best
Dim scoredos As Integer
Private Sub Form7_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub scoreupdate()
highScore.Text = highest.ToString
score2.Text = scoreuno.ToString
score3.Text = scoredos.ToString
End Sub
Public Sub highscores()
If score > highest Then
scoredos = scoreuno
scoreuno = highest
highest = score
ElseIf score > scoreuno Then
scoredos = scoreuno
scoreuno = score
ElseIf score > scoredos Then
scoredos = score
End If
scoreupdate()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
score = 0
Form1.Show()
Me.Close()
End Sub

You could simplify your code a little by using a list of integers like this
Dim HighScores As New List(Of Integer)
Then, instead of using your current HighScores sub using the sub below, just add a score to the list which is sorted in to highest value first, and then the lowest value is removed from the end of the list.
This way, if you decide to have more than 3 high scores, it should work just fine. just change the maxHighscores variable instead of having to edit your If statement and add increasing amounts of code. Then just add code into your update sub to show the additional scores.
Private Sub AddScoreIfHighEnough(score As Integer, maxScoresCount As Integer)
'Adds score to list
HighScores.Add(score)
'Sorts list into ascending order and reverses the order
HighScores.Sort()
HighScores.Reverse()
'if the number of scores is greater than the maximum allowed
'number remove the extra scores
While maxScoresCount < HighScores.Count
HighScores.RemoveAt(HighScores.Count - 1)
End While
End Sub
To use it just add replace each time you use
highScores()
with
AddScoreIfHighEnough(score,3)
Finally to assign your scores, just use
Private Sub scoreupdate()
highScore.Text = HighScores(0).ToString
score2.Text = HighScores(1).ToString
score3.Text = HighScores(2).ToString
End Sub

Related

VB.Net guess user's number from 1 to 1000

I am trying to create a guessing game that guesses the user's number from 1 to 1000. The user inputs if the number is higher or lower than the computer's guess. Based on the user's input, the computer each time halves the amount of the guess (e.g. first guess is 500, second is 250, third 125, etc, etc)
However I have encountered a problem when I am running this program. After pressing 'higher' or 'lower' for a few times, I am unable to change the output any further. I suppose this is to do with amount = amount / 2 reaching a limit where it can barely be added or subtracted into intGuess. I have tried doing amount = (amount / 2) + 1, but that sometimes doesn't allow me to get to a number.
How would I counteract this problem?
Here is my code:
Dim intGuess As Integer = 500
Dim amount As Integer = 500
Dim count As Integer = 0
Private Sub btnLower_Click(sender As Object, e As EventArgs) Handles btnLower.Click
amount = amount / 2
intGuess = intGuess - amount
lblGuess.Text = $"Is your number {intGuess} ?"
count = count + 1
End Sub
Private Sub btnHigher_Click(sender As Object, e As EventArgs) Handles btnHigher.Click
amount = amount / 2
intGuess = intGuess + amount
lblGuess.Text = $"Is your number {intGuess} ?"
count = count + 1
End Sub
Just thought I should add this, but the first guess is 500.
I play this game verbally with my young son. I tell him to guess a number from 1 to 1000 and guarantee I can guess it in 10 or fewer guesses. It is a simple binary search. You can research binary search to come up with an algorithm. It's pretty simple and I've split it up into buttons like you have. Here is my form
The code to make it work is
Private guess As Integer
Private max As Integer
Private min As Integer
Private Sub StartButton_Click(sender As Object, e As EventArgs) Handles StartButton.Click
If Integer.TryParse(MaxTextBox.Text, max) AndAlso
Integer.TryParse(MinTextBox.Text, min) AndAlso
max > min Then
makeGuess()
Else
MessageBox.Show("Error in max or min, cannot continue! Fix max and min and try again.")
End If
End Sub
Private Sub HigherButton_Click(sender As Object, e As EventArgs) Handles HigherButton.Click
min = guess
makeGuess()
End Sub
Private Sub LowerButton_Click(sender As Object, e As EventArgs) Handles LowerButton.Click
max = guess
makeGuess()
End Sub
Private Sub JustRightButton_Click(sender As Object, e As EventArgs) Handles JustRightButton.Click
MessageBox.Show($"That's right, I found your number, it is {guess}!")
End Sub
Private Sub makeGuess()
guess = CInt((max - min) / 2 + min)
GuessLabel.Text = guess.ToString()
End Sub

VB.NET Random unique generator

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.

Need to know why code is repeating itself

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

Displaying winner's name in picture box

Below is code for a simple voting system I am coding.
Public Class Form1
Dim winner As String
Dim maxVotes As Integer
Dim votes() As String
Dim index As String
Dim candidates As String
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
If Not isValidInput(txtNewCandidate.Text) Then
Exit Sub
End If
lstCandidates.Items.Add(txtNewCandidate.Text)
txtNewCandidate.Clear()
txtNewCandidate.Focus()
ReDim Preserve votes(index)
index += 1
End Sub
Private Function isValidInput(ByRef firstName As String) As Boolean
If IsNumeric(txtNewCandidate.Text) Or txtNewCandidate.Text = "" Then
MsgBox("Please input a valid candidate name.")
txtNewCandidate.Focus()
Return False
Else
Return True
End If
End Function
Private Sub btnTally_Click(sender As Object, e As EventArgs) Handles btnTally.Click
lstTallies.Visible = True
lblTally.Visible = True
For i = 0 To lstCandidates.Items.Count - 1
lstTallies.Items.Add(lstCandidates.Items(i).ToString & " - " & votes(i))
Next
End Sub
Private Sub lstCandidates_DoubleClick(sender As Object, e As EventArgs) Handles lstCandidates.DoubleClick
If lstCandidates.SelectedIndex = -1 Then
MsgBox("Select a candidate by double-clicking")
End If
votes(lstCandidates.SelectedIndex) += 1
MsgBox("Vote Tallied")
End Sub
Private Sub pbxWinner_Click(sender As Object, e As EventArgs) Handles pbxWinner.Click
End Sub
End Class
The voter must double click on their choice of candidate in the first list box. The user then tallies the votes by clicking on a button and a second list box will appear with the votes per candidate.
Now I need to display the winner (or winners, if there is a tie) in a picture box, pbxWinner. I am not sure how to accomplish this. Any clues?
Here is what i am trying to do, though the code below doesn't work.
Private Function candidateWinner(ByRef winner As String) As Boolean
For i As Integer = 0 To lstCandidates.SelectedIndex - 1
If votes(i) > maxVotes Then
maxVotes += 1
End If
Next
g = pbxWinner.CreateGraphics
g.TranslateTransform(10.0F, 0.0F)
g.DrawString(winner, New Font("Arial", 7, FontStyle.Regular), Brushes.DarkBlue, New PointF(0, 0))
Return True
End Function
Your code is actually working fine for an initial paint, but when the picture box image doesn't have its own bitmap set, a number of events can repaint its graphics behind the scenes(even as simple as minimizing/mazimizing the form, and a whole bunch of other ones), so in effect your text seems to never appear at all or disappear almost instantly when in reality it's probable getting repainted. To fix this, use a bitmap for the graphics object's reference, paint the bitmap's graphics, and then assign the bitmap to the picturebox's image property. This will make the image persistent...give this code a try in your candidateWinner function after the for loop:
Dim bmp As New Bitmap(pbxWinner.Width, pbxWinner.Height)
Dim g As Graphics = Graphics.FromImage(bmp)
g.TranslateTransform(10.0F, 0.0F)
g.DrawString(winner, New Font("arial", 7, FontStyle.Regular), Brushes.DarkBlue, 0, 0)
pbxWinner.Image = bmp
...If you still aren't seeing text, make sure the winner string has the correct value set, I tested this code and it showed my test string correctly
Edit for Comment:
That's because of the logic you're using to calculate the winner...you are just checking to see if the currently selected candidate's vote count is higher than maxVotes and then incrementing the max by 1. If you wanted to stick with that sort of logic for picking the winner, you would want to iterate through ALL of the candidates(not just those from index 0 to the currently selected one), and if their vote count is higher than the max, then set the max EQUAL to their vote count. Then the next candidate in the loop will have their count checked against the previous max. However, tracking the winner could be done a lot easier if you just use a dictionary since you are allowing candidates to be added, and you must change your "winner" logic to actually check who has the most votes out of everyone entered. A bare bones example of that would look like this:
Dim dctTally As Dictionary(Of String, Integer)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
dctTally = New Dictionary(Of String, Integer)
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
dctTally.Add(txtNewCandidate.Text, 0)
lstCandidates.Items.Add(txtNewCandidate.Text)
End Sub
Private Sub lstCandidates_DoubleClick(sender As Object, e As EventArgs) Handles lstCandidates.DoubleClick
dctTally(lstCandidates.text) += 1
End Sub
Private Sub pbxWinner_Click(sender As Object, e As EventArgs) Handles pbxWinner.Click
Dim winner = dctTally.Aggregate(Function(l, r) If(l.Value > r.Value, l, r)).Key
Dim bmp As New Bitmap(pbxWinner.Width, pbxWinner.Height)
Dim g As Graphics = Graphics.FromImage(bmp)
g.TranslateTransform(10.0F, 0.0F)
g.DrawString(winner, New Font("arial", 7, FontStyle.Regular), Brushes.DarkBlue, 0, 0)
pbxWinner.Image = bmp
End Sub
This way, the program allows as many names as you want to be added to the candidates list, and will add a vote count to their name each time their name is double-clicked on. Then, when your winner pixturebox is clicked, it will find the dictionary with the highest vote count and display their name in the winner-box.
You can try this to draw the winners:
Private Sub candidateWinner()
Dim y As Single = 0
maxVotes = votes.Select(Function(x) Convert.ToInt32(x)).Max()
For i = 0 To UBound(votes)
If votes(i) = maxVotes.ToString() Then
g = pbxWinner.CreateGraphics
g.TranslateTransform(10.0F, 0.0F)
g.DrawString(lstCandidates.Items(i).ToString(), New Font("Arial", 7, FontStyle.Regular), Brushes.DarkBlue, New PointF(0, y))
y += 10
g.Dispose()
End If
Next
End Sub

Array as structure members Assignment Student Test Scores

ASSIGNMENT
A teacher has six students and wants you to create an application that stores their grade data in a file and prints a grade report. The application should have a structure that stores the following student data: Name (a string), Test Scores (an array of five Doubles), and Average (a Double). Because the teacher has six students, the application should use an array of six structure variables.
The application should allow the user to enter data for each student, and calculate the average test score.
The user should be abled to save the data to a file, read the data from the file, and print a report showing each student's test scores and average score. The form shows a meny system. You may you buttons instead if you prefer.
Input validation: Do not accept test scores less that zero or greater than 100.
]
my understanding of how it should be structured
For the Moment I don't understand that in the FOR EACH loop I can not accumulate total it saying that I am not allowed to use + . I am trying to get scores from txtScore1Std1 (For example) assign it to dblTestScoreArray and using for each loop to find sum of those 5 score and when find average and output it to lbl average for student number 1.
Code Module:
Module StudentTestScoresModule
Const intMAX_SUBSCRIPT_STUDENT As Integer = 6
Const intMAX_SUBSCRIPT_STUDENT_SCORES As Integer = 5
'create structure
Public Structure StudentData
Dim strName As String
Dim dblTestScoresArray() As Double
Dim dblAverage As Double
End Structure
Dim dblTotalStd1 As Double
Dim dblScore As Double
Dim StudentsArray(intMAX_SUBSCRIPT_STUDENT) As StudentData
Sub StudentNameDataInput()
StudentsArray(0).strName = MainForm.txtStdName1.Text
StudentsArray(1).strName = MainForm.txtStdName2.Text
StudentsArray(2).strName = MainForm.txtStdName3.Text
StudentsArray(3).strName = MainForm.txtStdName4.Text
StudentsArray(4).strName = MainForm.txtStdName5.Text
StudentsArray(5).strName = MainForm.txtStdName6.Text
End Sub
Sub StudentScoreDataInput()
For intIndex = 0 To intMAX_SUBSCRIPT_STUDENT
ReDim StudentsArray(intIndex).dblTestScoresArray(4)
Next
'test scores for first student
StudentsArray(0).dblTestScoresArray(0) = CDbl(MainForm.txtScore1Std1.Text)
StudentsArray(1).dblTestScoresArray(1) = CDbl(MainForm.txtScore2Std1.Text)
StudentsArray(2).dblTestScoresArray(2) = CDbl(MainForm.txtScore3Std1.Text)
StudentsArray(3).dblTestScoresArray(3) = CDbl(MainForm.txtScore4Std1.Text)
StudentsArray(4).dblTestScoresArray(4) = CDbl(MainForm.txtScore5Std1.Text)
For Each i As StudentData In StudentsArray
dblTotalStd1 += i
Next
dblAverage = dblTotalStd1 / intMAX_SUBSCRIPT_STUDENT_SCORES
MainForm.lblAvgStd1.Text = (dblAverage.ToString)
End Sub
Sub CalculateAverage()
End Sub
End Module
Code Main Form:
Public Class MainForm
Private Sub mnuHelpAbout_Click(sender As Object, e As EventArgs) Handles mnuHelpAbout.Click
'about program
MessageBox.Show("Student test score calculator version 0.1")
End Sub
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnExit.Click
' Close(program)
Me.Close()
End Sub
Private Sub btnCalculate_Click(sender As Object, e As EventArgs) Handles btnCalculate.Click
StudentScoreDataInput()
End Sub
End Class
just by looking, without testing, what you need to do is;
'untested code
For Each i As StudentData In StudentsArray
For Each S as Double in i.dblTestScoresArray
dblTotalStd1 += s
Next
Next
you cannot do += on a structure, you need to do it on the member and since its an array, you need to loop through it