Question Get user-input variables to match set values (no matter the order they are entered)

Mynotoar

Active member
Joined
Sep 27, 2012
Messages
28
Location
England
Programming Experience
Beginner
Hey! I should start with a clarification: I'm new to the forums, and I'm also very new to VB. I started studying it at A-level Computing nearly three weeks ago. So please bear in mind that I'm nowhere near an expert and may need simple explanations :). Also, I'm working with a console application, and not a... whatever the other thing is called!

So what I'm trying to do is to create a quiz where you can answer the questions in any order. Say I have a five-question quiz. First I declare five values that are constants, they're the answers, and they're what'll be referred to. So I'll call them "red", "orange", "green", "blue" and "black". The program I want to write would then have you enter at least five variables, one at a time. If the variable matches the constant, so you type in "orange" (we'll assume everything is forced to lowercase so there are no case problems,) then it determines whether "orange" matches any of the five constants. If it does, it tallies up a value called "score" which would be totalled once you've filled all five answer slots or something like that.

If it was in order, I'd understand how to do it just fine. Here's what my code would look like - so you get an idea of the things I know, too.

VB.NET:
Module Module1

    Sub Main()
        Dim variable1, variable2, variable3, variable4, variable5 As String
        Dim constant1, constant2, constant3, constant4, constant5 As String
        Dim score As Integer
        constant1 = "red"
        constant2 = "orange"
        constant3 = "green"
        constant4 = "blue"
        constant5 = "black"
        score = 0
        'I'm just clearing it in case I want to include a Do loop later.


        Console.WriteLine("Enter a colour that starts with r.")
        variable1 = Console.ReadLine
        If LCase(variable1) = constant1 Then
            score = score + 1
            'The answer is correct, the score is now 1.
        Else
            score = score
        End If
        Console.WriteLine("Enter a colour that starts with o.")
        variable2 = Console.ReadLine
        If LCase(variable2) = constant2 Then
            score = score + 1
        Else
            score = score
        End If
        Console.WriteLine("Enter a colour that starts with g.")
        variable3 = Console.ReadLine
        If LCase(variable3) = constant3 Then
            score = score + 1
        Else
            score = score
        End If
        Console.WriteLine("Enter a colour that starts with b.")
        variable4 = Console.ReadLine
        If LCase(variable4) = constant4 Then
            score = score + 1
        Else
            score = score
        End If
        Console.WriteLine("Enter a colour that starts with b.")
        variable5 = Console.ReadLine
        If LCase(variable5) = constant5 Then
            score = score + 1
        Else
            score = score
        End If
        Console.WriteLine("You got " & score & "/5.")
        Console.ReadLine()
    End Sub


End Module

So this is just asking you to enter the variable, checking the variable (forced into lowercase) matches the constant, and if it does then the "score" value goes up, and is tallied at the end.

That's fine. But how on Earth could I do it out of order? In my head, I'm thinking that I'd have to say for every line that you input something like "Does it match constant1, does it match constant2, does it match constant3..." etc., and that would be tedious if I were doing 118 questions instead of 5. Is there a more efficient way? And if there isn't, how do I do it the first way, because I can't work that out either? I tried using "or" to say "If variable1 = constant1 OR constant2 OR constant3" but that doesn't work, it thinks I'm trying to convert the variable type or something silly.

Can anyone offer any help/advice? Input would be greatly and warmly appreciated :) <3.
 
Hi Leo,

On the basis that you are looking to make your code more optimal along with what you want to do next, the only thing that I would consider adding to Solitaire's comments is that it's probably about the right time to introduce you to Subroutines and Functions.

Subroutines and functions are blocks of code which you define outside of your Sub Main() loop which you then call from within your Sub Main() loop. The only difference between a subroutine and a function is that a function has the additional ability of being able to return a value / object to the calling procedure.

I have added both a function and a subroutine with plenty of comments to your code (see below) to demonstrate both techniques which then can be expanded on at your leisure.

Good luck and well done.

Cheers,

Ian

VB.NET:
Module Module1
 
  Sub Main()
    'Notice how your list of elements have now dissapeared making your Sub main a bit meore readable?
    'It has not really dissapeared - I have just moved the initialization routine to a function, see below
    'Notice how your Dim elements statement below has now changed to call the function to load the elements
    'Also notice that I have removed the New keyword here to. That's because the list is now actually created
    'in the function and the function then returns an existing object of list(of string) which contains
    'all the correct answers.
    Dim elements As List(Of String) = LoadCorrectAnswers()
    Dim wronganswerlist As New List(Of String)
    Dim answer, name, endgame As String
    Dim score, total, percentage As Integer
    Dim quit As Boolean = True
    Dim finish As Boolean = True
    Do
      score = 0 : total = 0 : percentage = 0
      Console.Clear()
      Console.ForegroundColor = ConsoleColor.Cyan
      Console.WriteLine("Periodic Table Quiz")
      Console.ForegroundColor = ConsoleColor.White
      Console.WriteLine("Hello! What is your name?")
      name = Console.ReadLine
      Console.WriteLine()
      Console.WriteLine("Welcome, " & name & "! This is a quiz about the Periodic Table of Elements.")
      Console.WriteLine()
      Console.ForegroundColor = ConsoleColor.Cyan
      Console.WriteLine("Instructions:")
      Console.ForegroundColor = ConsoleColor.White
      Console.WriteLine("  1. Type in as many elements from the Periodic Table as you can.")
      Console.WriteLine("  2. Press enter once after each element you guess.")
      Console.WriteLine("  3. Please spell accurately and according to British conventions.")
      Console.WriteLine("  Answers are not case-sensitive.")
      Console.WriteLine("  4. Use full names, not symbols or atomic numbers.")
      Console.WriteLine("  5. When you wish to stop, type 'f' and then press enter to display your score.")
      Console.WriteLine("  6. Don't lose the game.")
      Console.WriteLine()
      Console.WriteLine("Elements:")
 
      Do
        answer = Console.ReadLine
        If elements.Contains(answer.ToLower()) Then
          With elements
            .Remove(answer)
          End With
          score += 1
          total += 1
          finish = False
        ElseIf answer = "" Then
          finish = False
        ElseIf answer = "f" Then
          finish = True
        Else
          With wronganswerlist
            .Add(answer)
          End With
          total += 1
          finish = False
        End If
      Loop Until finish = True
 
      'somewhere whithin your code, presumably about here, you are going to want to display your list of wrongs answers as 
      'demonstrated by Solitaire. Again, rather that write the full code as demonstared directly in your sub main routine 
      'I have placed the code in a subroutine and than all you need to do is call the subroutine and pass the list of 
      'wrong answers as a parameter to the routine i.e.
 
      DisplayIncorrectAnswers(wronganswerlist)
      'notice the difference between the function and the subroutine. They are both performing actions in their
      'respective blocks of code but nothing is being returned from the DisplayIncorrectAnswers subroutine
      'whereas the LoadCorrectAnswers returned a newly created list containing the answers.
 
      percentage = (score / total) * 100
      Console.WriteLine()
      Console.WriteLine(name & ", your score is " & score & "/" & total & ", or " & percentage & "%")
      Console.WriteLine()
      Console.WriteLine("Would you like to play again?")
      Console.WriteLine("Type 'q' and press enter to quit.")
      Console.WriteLine("Press any key to restart.")
      endgame = Console.ReadLine
      If endgame = "q" Then
        quit = True
      Else
        quit = False
      End If
    Loop Until quit = True
 
  End Sub
 
  Private Sub DisplayIncorrectAnswers(WrongAnswers As List(Of String))
    Console.WriteLine("The following answers were wrong:")
    For Each itm As String In WrongAnswers
      Console.WriteLine(itm)
    Next itm
    Console.ReadLine()
  End Sub
 
  Private Function LoadCorrectAnswers() As List(Of String)
    Dim elements As New List(Of String)
    With elements
      .Add("actinium")
      .Add("aluminium")
      .Add("americium")
      .Add("antimony")
      .Add("argon")
      .Add("arsenic")
      .Add("astatine")
      .Add("barium")
      .Add("berkelium")
      .Add("beryllium")
      .Add("bismuth")
      .Add("bohrium")
      .Add("boron")
      .Add("bromine")
      .Add("cadmium")
      .Add("calcium")
      .Add("californium")
      .Add("carbon")
      .Add("cerium")
      .Add("caesium")
      .Add("chlorine")
      .Add("chromium")
      .Add("cobalt")
      .Add("copernicium")
      .Add("copper")
      .Add("curium")
      .Add("darmstadtium")
      .Add("dubnium")
      .Add("dysprosium")
      .Add("einsteinium")
      .Add("erbium")
      .Add("europium")
      .Add("fermium")
      .Add("flerovium")
      .Add("fluorine")
      .Add("francium")
      .Add("gadolinium")
      .Add("gallium")
      .Add("germanium")
      .Add("gold")
      .Add("hafnium")
      .Add("hassium")
      .Add("helium")
      .Add("holmium")
      .Add("hydrogen")
      .Add("indium")
      .Add("iodine")
      .Add("iridium")
      .Add("iron")
      .Add("krypton")
      .Add("lanthanum")
      .Add("lawrencium")
      .Add("lead")
      .Add("lithium")
      .Add("livermorium")
      .Add("lutetium")
      .Add("magnesium")
      .Add("manganese")
      .Add("meitnerium")
      .Add("mendelevium")
      .Add("mercury")
      .Add("molybdenum")
      .Add("neodymium")
      .Add("neon")
      .Add("neptunium")
      .Add("nickel")
      .Add("niobium")
      .Add("nitrogen")
      .Add("nobelium")
      .Add("osmium")
      .Add("oxygen")
      .Add("palladium")
      .Add("phosphorous")
      .Add("platinum")
      .Add("plutonium")
      .Add("polonium")
      .Add("potassium")
      .Add("praseodymium")
      .Add("promethium")
      .Add("protactinium")
      .Add("radium")
      .Add("radon")
      .Add("rhenium")
      .Add("rhodium")
      .Add("roentgenium")
      .Add("rubidium")
      .Add("ruthenium")
      .Add("rutherfordium")
      .Add("samarium")
      .Add("scandium")
      .Add("seaborgium")
      .Add("selenium")
      .Add("silicon")
      .Add("silver")
      .Add("sodium")
      .Add("strontium")
      .Add("sulfur")
      .Add("tantalum")
      .Add("technetium")
      .Add("tellurium")
      .Add("terbium")
      .Add("thallium")
      .Add("thorium")
      .Add("thulium")
      .Add("tin")
      .Add("titanium")
      .Add("tungsten")
      .Add("ununtrium")
      .Add("ununpentium")
      .Add("ununseptium")
      .Add("ununoctium")
      .Add("uranium")
      .Add("vanadium")
      .Add("xenon")
      .Add("ytterbium")
      .Add("yttrium")
      .Add("zinc")
      .Add("zirconium")
    End With
    Return elements
  End Function
 
End Module
 
Hey, thank you Solitaire - but what does "vbCrLf" do in the second function? I wrote out the code as you presented it with and without and it didn't make a difference, and the information that Visual Studio gives you as you type isn't much use to a beginner :p. And thank you very much for the error help!

Though I don't think I can incorporate this into the If function - everything else in the If is a string that has to be entered, whereas the keypress thing needs... well, a keypress :p. It'll work for closing the program at least :).
 
Last edited:
And Ian, again, many thanks! But I'm confused - you said that a function can return an object/value. Here, from what I can tell, the function is the creation of the list of elements, which can then be read by the program at any time, and the subroutine is the For loop that creates the wrong answer list - this sounds just like a function to me.
 
Hi Leo,

You will find that you are not alone when trying to get your head round the difference between functions and subroutines for the first time. The important thing here is that the function actually created the list and returned an object of type list to the calling dim elements statement that was defined in Sub Main(). You then used the elements list in the Sub Main() section of the code even though it was not actually created in the Sub Main() section of the code. The subroutine however just displayed some text on the screen from the list that you sent to it as a parameter - does that make better sense?

By the way the vbCRLF in Solitaires example is the Visual Studio Internal Constant for Carriage Return / Line Feed (New Line)

Additionally, I am going to send you a separate note on errors?

Cheers,

Ian
 
Hey, I've been having another problem. I've created the subroutine for wrong answers as you showed me:

VB.NET:
Private Sub Displaywronganswers(Wronganswerlist As List(Of String))


        Console.WriteLine("The following answers were incorrect:")
        For Each item As String In Wronganswerlist
            Console.WriteLine(item)
        Next
    End Sub

Then in the sub main I've called the function with "Displaywronganswers(wronganswerlist)". It all works fine, except for when you loop the program, the list remembers what you last put into it. I need to delete the contents of the list once the program has displayed its contents, and I've tried everything but I can't work out how to do it. The issue is that For loops don't want a declared variable, e.g. "item", so I can't use a variable which I'll then tell the list to delete all of using a With tag. And I don't know how to clear it any other way. Any suggestions?
 
Hi Leo,

Lets talk errors for a while. There are three main categories of errors to deal with in programming:-

1) Syntax Errors.
These are grammar errors / typo's in the code and are the easiest to find and fix since the compiler will not allow you to run the program until you have sorted them. I am sure will have come across a few of these already.

2) Runtime Errors.
These are errors where everything compiles fine and the program runs but than something happens in the program that forces the program to stop running unexpectedly. These occur for all sorts of reasons i.e. an index out of range in an array for example and are not as easy to find but there is a whole array of debugging and error checking tools to help you sort these out.

3) Then there is the dreaded Logic Error.
These are the worst kind of errors to find and fix since from your own mindset, logic and experience everything is working correctly in the program. This is where your own mindset is right now with the application that you have written. This is also where you need to have the patience of a saint sometimes to listen to your users experience of using your program to figure out what they have seen going wrong but what you have not seen going wrong.

I am now your student using your application and I have experienced the following:-

1) You state that answers are not case sensitive? But if I enter my answers in capitals I can get a point every time that I enter the same thing. i.e. if I enter SODIUM twice I get two points - wooohooo!, double points for nothing.

2) I play the quiz once, great quiz by the way, and I enter sodium, lead, iron, bread and butter and when I finish it gives me my results. It then asks me if I want to try the quiz again and I say yes and again I enter sodium, lead, iron, bread and butter but this time it says that I got all five wrong and the error list that is displayed shows not only valid elements but bread and butter twice? I am confused Mr programmer?

Now its time to debug your own logic. Here are two hints:-

Have a think about where things need to be declared and initialised and Lists have a .Clear method.

Good luck,

Ian.
 
vbCrLf -- vb constant for CarriageReturnLineFeed

The code I posted above using vbCrLf is the same as using an extra Console.WriteLine(), but it takes only one line of instruction instead of two. The following is the same and should produce exactly the same output as the one with vbCrLf:


VB.NET:
    Sub Main()
        Dim ans As String
        Do
            Console.WriteLine()
            Console.WriteLine("Program goes here")
            Console.WriteLine("Press any key to continue or Esc to quit:  ")
            ans = Console.ReadKey(True).KeyChar
        Loop Until ans = Chr(27)   'This is the Esc key
        Console.WriteLine()
        Console.WriteLine("Ending program now")
        Console.ReadLine()
    End Sub

I suggest substituting it for your code instead of:

VB.NET:
          Console.WriteLine("Would you like to play again?")
            Console.WriteLine("Type 'q' and press enter to quit.")
            Console.WriteLine("Press any key to restart.")
            endgame = Console.ReadLine
            If endgame = "q" Then
                quit = True
            Else
                quit = False
            End If
        Loop Until quit = True

Do this:

VB.NET:
          Console.WriteLine("Would you like to play again?")
            Console.WriteLine("Press any key to continue or Esc to quit.")
                ans = Console.ReadKey(True).KeyChar
        Loop Until ans = Chr(27)

or if you prefer:

VB.NET:
           Console.WriteLine("Type 'q' to quit.")
            Console.WriteLine("Press any key to restart.")
            endgame = Console.ReadKey(True).KeyChar
      Loop Until endgame.ToLower() = "q"

You can also eliminate the Dim quit As Boolean declaration.
 
Last edited:
Don't use the variable item since it's a reserved word. That's why I used itm in my sample code above. Make up a different variable name if you don't want to use itm.

Regarding what Ian said, you need to reinitialize your variables if you are going to repeat the quiz in a loop. This includes the list of wrong answers, which needs to be cleared.

wronganswerlist.Clear()

PS: In this program, you have only strings for user inputs. There are other things you need to be aware of when creating a program that gets user input of a number. You can post a new thread when you're ready for it, and I'll give you some more hints.
 
Last edited:
1) Syntax Errors.
These are grammar errors / typo's in the code and are the easiest to find and fix since the compiler will not allow you to run the program until you have sorted them. I am sure will have come across a few of these already.

Indeed so!

2) Runtime Errors.
These are errors where everything compiles fine and the program runs but than something happens in the program that forces the program to stop running unexpectedly. These occur for all sorts of reasons i.e. an index out of range in an array for example and are not as easy to find but there is a whole array of debugging and error checking tools to help you sort these out.

Yeah, I have these problems most frequently with things like pressing enter where you're supposed to type a value in first, or pressing the wrong key. I'm gradually learning to avoid these - if I ever have an If statement I try to make sure it accounts for the possibility that you press Enter.

3) Then there is the dreaded Logic Error.
These are the worst kind of errors to find and fix since from your own mindset, logic and experience everything is working correctly in the program. This is where your own mindset is right now with the application that you have written. This is also where you need to have the patience of a saint sometimes to listen to your users experience of using your program to figure out what they have seen going wrong but what you have not seen going wrong.

Yeah, in this stage at my programming experience, the problem is less logic errors than figuring how to write the darn logic :).

1) You state that answers are not case sensitive? But if I enter my answers in capitals I can get a point every time that I enter the same thing. i.e. if I enter SODIUM twice I get two points - wooohooo!, double points for nothing.

Thank you for bringing that to my attention, neither I nor anyone had noticed that! So... the code goes as follows:

VB.NET:
 If elements.Contains(answer.ToLower()) Then                    With elements
                        .Remove(answer)
                    End With
                    score += 1
                    total += 1

So every answer that is entered is put to lowercase, and then removed from the list. So if I type "Sodium", then it will process it to "sodium" and remove "sodium" from the list, then add 1 to score and total. If I type "SODIUM", it should still convert to "sodium" which will by then be removed from the list, and therefore shouldn't count. So I haven't a clue why that doesn't work.

2) I play the quiz once, great quiz by the way, and I enter sodium, lead, iron, bread and butter and when I finish it gives me my results. It then asks me if I want to try the quiz again and I say yes and again I enter sodium, lead, iron, bread and butter but this time it says that I got all five wrong and the error list that is displayed shows not only valid elements but bread and butter twice? I am confused Mr programmer?

Yeah, this was the problem that got me today - my friend showed me how to fix it, with Wronganswerlist.clear(). Works wonderfully :D.

Now its time to debug your own logic. Here are two hints:-

Have a think about where things need to be declared and initialised and Lists have a .Clear method.

Thankee :).
 
Don't use the variable item since it's a reserved word. That's why I used itm in my sample code above. Make up a different variable name if you don't want to use itm.

Regarding what Ian said, you need to reinitialize your variables if you are going to repeat the quiz in a loop. This includes the list of wrong answers, which needs to be cleared.

wronganswerlist.Clear()

PS: In this program, you have only strings for user inputs. There are other things you need to be aware of when creating a program that gets user input of a number. You can post a new thread when you're ready for it, and I'll give you some more hints.

Ooh, thank you!
 
Morning Leo,

I am working on day-old code now so I hope I am not commenting on things that you have now resolved. The two errors which I think you may still have is:-

1) The reason why things are not being removed from the list when dealing with case issues is that even though you check for answer correctness using answer.ToLower you then forget this point when trying to remove the correct answer from the list. Lists of string are also case sensitive and therefore you need to use elements.Remove(answer.ToLower)

You could also get round the many .ToLower methods being used by creating a new variable to hold the lowercase version of the string. This variable could then be used for testing throughout your code. i.e. Dim MyLowerCaseAnswer as String = answer.ToLower

2) You have sorted the WrongAnswerList now which is great but when you run the quiz more that once you need to reset your elements list because do not forget you have been removing correct answers from this list in the previous quiz. Your statement which creates the elements list being Dim elements As List(Of String) = LoadCorrectAnswers() needs to be moved to be the first statement after your top level Do keyword. This is where this function plays it's part now because you can call it as many times as you want to rest the elements list.

Hope that gets you about finished and good luck.

Cheers,

Ian
 
Morning Leo,

I am working on day-old code now so I hope I am not commenting on things that you have now resolved. The two errors which I think you may still have is:-

1) The reason why things are not being removed from the list when dealing with case issues is that even though you check for answer correctness using answer.ToLower you then forget this point when trying to remove the correct answer from the list. Lists of string are also case sensitive and therefore you need to use elements.Remove(answer.ToLower)

You could also get round the many .ToLower methods being used by creating a new variable to hold the lowercase version of the string. This variable could then be used for testing throughout your code. i.e. Dim MyLowerCaseAnswer as String = answer.ToLower

2) You have sorted the WrongAnswerList now which is great but when you run the quiz more that once you need to reset your elements list because do not forget you have been removing correct answers from this list in the previous quiz. Your statement which creates the elements list being Dim elements As List(Of String) = LoadCorrectAnswers() needs to be moved to be the first statement after your top level Do keyword. This is where this function plays it's part now because you can call it as many times as you want to rest the elements list.

Hope that gets you about finished and good luck.

Cheers,

Ian

Thank you, the .tolower() in the With tag worked beautifully! But the Dim elements is already at the start of the loop - it works just fine. The list is repopulated after the program loops :).
 
Back
Top