3

I am making a quiz for my computer science class and the basic concept is that you have 15 keywords and 15 definitions. All need to be randomly displayed and the correct answer has to appear. The user has to match the correct definition to the keyword twice and then that keyword and definition are not displayed again. When all have been answered twice the quiz is over.

I have stored both my keywords and my definitions in the same file so they don't get out of sync. The text file looks like so:

Keyword1 = Definition1
Keyword2 = Definition2
Keyword3 = Definition3

etc (Total of 15)

My main form looks like this:

Public Class quiz
Private Sub quiz_load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles myBase.Load

Dim MyList As List(Of KeyValuePair(Of String, String)) = New List(Of String, String))
For Each line As String In System.IO.File.ReadAllLines("my-file-path")
    Dim Pair() As String = line.split("=")
    mylist.add(New KeyValuePair(Of String, String)(Pair(0), Pair(1)))
Next

I am displaying the random keyword in a label and the definitions in radiobuttons. Two need to be random definitions and one has to be the correct definition to the keyword shown, which also needs to be displayed randomly.

What I am asking is:

  1. How do I finish this list off as it is overwriting the other 15 lines only using the last one?
  2. How can I randomize the list of keywords and definitions for when they are displayed?
  3. How can I remove the items when each keyword has been matched to its definition twice? E.G: Keyword 1 and definition 1 have been answered correctly twice so remove from list so it won't be displayed again.
Dan Puzey
  • 33,626
  • 4
  • 73
  • 96
Matt Kent
  • 1,145
  • 1
  • 11
  • 26
  • Please [edit] your question title to explain something about the problem. "What am I doing wrong with this list?" will not be very meaningful to future readers as a search result. Thanks. – Ken White Mar 03 '14 at 15:43
  • 1
    Are you the instructor for this class, or a student in it? Also, this line strikes me as odd `Dim MyList As List(Of KeyValuePair(Of String, String)) = New List(Of String, String))`. – Bradley Uffner Mar 03 '14 at 15:44
  • Student, and I don't know exactly what is wrong with it or I wouldn't of set the title so vague. – Matt Kent Mar 03 '14 at 15:45
  • 2
    you can shorten the decl to `Dim MyList As New List(Of KeyValuePair(Of String, String))`. What makes you think only the last is retained? How many elements are in the list at the end of the loop? If you later want to tag them as matched/used, consider a List(Of myClass) where class is `ItemName (string key), Definition (string), Used (bool)`. – Ňɏssa Pøngjǣrdenlarp Mar 03 '14 at 15:45
  • There are 15 lines in the text file. What I mean by the last one is if I said: `LabelKeyword.text = pair(0)` it would display the last keyword in the file. I don't understand how I can retrieve all the lines from the file and pick one. E.G: If I did an array like `Keywords()` then I could call `Keywords(0)` for the first line. That worked when they were in seperate files but since I was recommended by others on DaniWeb to put them into one file I don't fully understand how to randomize or output the keywords and definitions randomly. – Matt Kent Mar 03 '14 at 15:53
  • 1
    but you no longer want to assign from `pair()`, assign from the List(of T). Check the List count at the end of the loop. You also are not likely to really want to randomize it or your next question will be how to prevent dupes and other issues. What you probably want is to shuffle it [see this - uses an array](http://stackoverflow.com/a/1222514/1070452) and [also this](http://stackoverflow.com/a/7513502/1070452). load the text, then shuffle, THEN setup for displaying. Should also accept Stevens answer to your last question since you implemented it. – Ňɏssa Pøngjǣrdenlarp Mar 03 '14 at 15:58
  • If you are setting the label text inside the loop then you will always be overwriting the text of the label with the current value. It will look like it only ever displays the last item even though it loaded all of them; it only displays the last one. – Bradley Uffner Mar 03 '14 at 16:00
  • So how can I change that so that I can retrieve all of them, not just the last one? – Matt Kent Mar 03 '14 at 16:02
  • You need to store the data in a field that has higher `scope` than the load method. You also need to keep track of the current question index in a variable. Once you load the questions set the current question index to 0 and display THAT question. Once the user answers the question you can increment the current question index variable. – Bradley Uffner Mar 03 '14 at 16:03
  • **debug your code!** Set a breakpoint and examine how many elements are in the List - it is likely 15 and you are chasing a ghost! Once the List is filled, you are done with the old pair stuff and need to work with the List. `List(0)` will be the first item, `List(2)` the second etc (and VS will show you the contents of the list when debugging). – Ňɏssa Pøngjǣrdenlarp Mar 03 '14 at 16:03
  • Okay, so would I still use a list or a dictionary? I originally used a dictioary and I was suggested on DaniWeb to use a list like the one in my question, so I am not sure what to use. Could you show me a quick example of what you mean by higher scope? @Plutonix if I call say `pair(2)` (as I have split the `=` I get and error saying it's not there). – Matt Kent Mar 03 '14 at 16:06
  • if you are going to shuffle or randomize it, a List seems a better choice than a Dictionary. just move the List decl up one line (outside the Sub) for form level scope. Again: **when the List is filled, you are done with `pair(n)`**; why did you fill the List if you are not going to use it to populate the labels/radios? – Ňɏssa Pøngjǣrdenlarp Mar 03 '14 at 16:08
  • I would stick to a list for your problem. There is no need to look anything up, so a dictionary would be overkill. You can move `MyList` to a higher scope by declaring it at the form level instead of as a local variable (inside the function). – Bradley Uffner Mar 03 '14 at 16:10
  • @Plutonix Are you sure it's declared at the form scope? In his example it looks to be declared inside the load event (the end sub is missing though). – Bradley Uffner Mar 03 '14 at 16:11
  • @BradleyUffner changed that, it is local in the code. – Ňɏssa Pøngjǣrdenlarp Mar 03 '14 at 16:12
  • @Matt Kent, I don't want do discourage you, but you may want to take a step back from this problem. If you are trying to do this as an independent project you may want to wait a bit longer in your studies before attempting it. There are some important concepts you seem to be missing right now that I'm sure will be covered soon. If this is homework or a class assigned project you may want to consult with your instructor about reviewing some things that you may have missed. A stronger understanding of `scoping` and the forms event system will be needed to pull off this project. – Bradley Uffner Mar 03 '14 at 16:18
  • We have 10 hours left and our instructors don't really seem to know much to be honest. All the things we are required to do we haven't been tought. All a shambles really. So that is why I came here to ask for some very much needed assistance. – Matt Kent Mar 03 '14 at 16:20

2 Answers2

1

This should give you an idea:

Const NUMBER_OF_ANSWERS As Integer = 3

Dim kv As New Dictionary(Of String, String)
kv.Add("Keyword1", "Definition1")
kv.Add("Keyword2", "Definition2")
kv.Add("Keyword3", "Definition3")

Dim r As New Random
Dim kvRandom As List(Of KeyValuePair(Of String, String)) =
  kv.OrderBy(Function() r.Next).ToList

'questions will appear in random order
For Each line As KeyValuePair(Of String, String) In kvRandom
  Dim keyword As String = line.Key
  Dim correctDefinition As String = line.Value

  Dim keywords As New List(Of String)
  keywords.Add(keyword)
  keywords.AddRange(kv.Keys.Except({keyword}).
    OrderBy(Function() r.Next).Take(NUMBER_OF_ANSWERS - 1))

  Dim definitionsRandom As List(Of String) =
    keywords.Select(Function(x) kv(x)).OrderBy(Function() r.Next).ToList

  'TODO: need to write some code here
  'display keyword and three possible definitions to the user
  '(out of which one is correct)
  'answers will also appear in random order
  'Check answer against value stored in "correctDefinition"
Next

The code is pretty much self-explanatory, if you have any questions, please let me know in comments.

EDIT: Here is how you can populate your dictionary from a file.

'assuming file structure is like this:
'keyword1,definition1
'keyword2,definition2
'keyword3,definition3
'...
For Each line As String In IO.File.ReadAllLines("keywords_and_definitions.txt")
  Dim parts() As String = line.Split(",")
  kv.Add(parts(0), parts(1))
Next
Victor Zakharov
  • 25,801
  • 18
  • 85
  • 151
  • 1
    Would I need the keywords and definitions in two files for this? E.G `Public Keywords As String() = IO.File.ReadAllLines("my-file-path")` and `Public Definitions As String() = IO.File.ReadAllLines("my-file-path-to-defs")`. Then when I go to add would I use: `kv.add(Keywords(0), Definitions(0))` etc? – Matt Kent Mar 03 '14 at 16:18
  • @MattKent: You can have one file, comma, space or tab separated, key + value. No, you should iterate through all lines and create a list or dictionary dynamically, not hard coding `0,0`; `1,1` etc. BTW, there is an issue with above code - it will display as many answers as there are questions - I am working to fix it. – Victor Zakharov Mar 03 '14 at 16:22
  • @MattKent: The code is fixed now, you can control the number of answers via a constant value defined at the start of code. You can make it dynamic parameter specified before the test, or in application config (`app.config` file). – Victor Zakharov Mar 03 '14 at 16:25
  • 1
    Would I need to control the number of answers even if I am using radiobuttons? (3 to be exact) – Matt Kent Mar 03 '14 at 16:36
  • @MattKent: You can leave it AS IS, i.e. always 3 for now. Set the Tag property to contain the answer, then create a generic handler for all 3 radio buttons. If you plan to evaluate the answer when user clicks a button (Next, Submit, OK etc.), put a code that enumerates through your radio buttons and retrieves a `Tag` property value for the checked one, in that button's click handler code. This is your user's selected answer. Don't forget to store correct answer somewhere as well. Check against that - and you are done. – Victor Zakharov Mar 03 '14 at 16:39
  • 2
    This is what I have got where you left the comments for me to code I added this so far: `LabelKeyword.Text = keyword` `RadioButtonDef1.Text = correctDefinition` `RadioButtonDef2.Text = definitionsRandom` `RadioButtonDef3.Text = definitionsRandom` I have got the top radiobutton working it's only the bottom two that I can't seem to get working. What am I doing wrong? Also really quickly is there a way to randomize which radio button the correct answer gets assigned to or will I just have to do it like what I've done? Thanks for your help :) – Matt Kent Mar 03 '14 at 16:54
  • @MattKent: You should be using `definitionsRandom`, so `RadioButtonDef1.Text = definitionsRandom(0)`, then `RadioButtonDef2.Text = definitionsRandom(1)` and finally `RadioButtonDef2.Text = definitionsRandom(2)`. This way your answers are random. What is it exactly that you `can't seem to get working`? Please update your question with relevant information. – Victor Zakharov Mar 03 '14 at 17:06
  • Ah I see, got it working. By the way how can I make it read from a file as at the moment it reads from the `kv.Add("Keyword", "Definition")`? Other than that it works like a charm. Thanks again. – Matt Kent Mar 03 '14 at 18:26
  • @MattKent: My pleasure. See the edit above (too big to be included in a comment). Don't forget to upvote & accept. Thanks. – Victor Zakharov Mar 03 '14 at 18:43
  • Thank you. Accepted just one last question. Do I enclose all code within this loop? In other words, where exactly do I place this code? – Matt Kent Mar 03 '14 at 18:55
  • @MattKent: Depends, I think the best option is to save your quiz sequence in a list variable (list of custom class - you decide), set your progress variable to 0 of 14 (number of questions minus 1, indexes are zero based in VB.NET). At first question, display question #0, when user presses `Next`, increment progress variable. Don't forget to count valid/invalid answers. You may want to store full answer history for a user. If you need mode detail on this one, I think it's worth asking a separate question - provide the functionality you need there. – Victor Zakharov Mar 03 '14 at 19:01
0

After you figure out your loading issues, you can follow this basic algorithm to do the rest of your tasks. I don't want to give you exact code for a class assignment. Figuring that kind of thing out is half the fun of learning programming.

  • Loop through each of the questions, keeping an index to the current question.
  • Declare a new list of answers and place the correct answer from the current question in to the list.
  • Pick 3 more answers randomly that are NOT the correct answer, and answers than have been picked for this question and add them to the answer list.
  • Randomize the answer list.
  • Display the questions and answers
  • Check the chosen answer against the answer for the current question.
  • Repeat for every question.
Bradley Uffner
  • 16,641
  • 3
  • 39
  • 76