0

I started to learn swift by trying to make a quiz game. But i'm stuck at trying to display random questions from an dictionary. The reason why i use a dictionary is because the quiz i'm working on is based on chemistry elements which has different symbols.

So Based on the selected difficulty i created four different dictionaries:

//Defining the dictionaries per difficulty
var easy: [String: String] = [
    "H": "Waterstof",
    "He": "Helium",
    "Li": "Lithium",
    "B": "Boor",
    "C": "Koolstof",
    "O": "Zuurstof",
    "Ne": "Neon",
    "Al": "Aliminium",
    "Si": "Silicium",
    "K": "Kalium",
    "Fe": "Ijzer",
    "Ni": "Nikkel",
    "Zn": "Zink",
    "Cd": "Cadmium",
    "I": "Jood"
]
var normal: [String: String] = [
    "N": "Stikstof",
    "F": "Fluor",
    "Na": "Natrium",
    "Mg": "Magnesium",
    "P": "Fosfor",
    "CI": "Chloor",
    "Ar": "Argon",
    "S": "Zwavel",
    "Ca": "Calcium",
    "Cu": "Koper",
    "Au": "Goud",
    "Br": "Broom",
    "Ag": "Zilver",
    "Pt": "Platina",
    "Ba": "Barium"
]

and so on.. (hard, extreme)

This is my viewDidLoad() method:

override func viewDidLoad() {

    switch (self.selectedDifficultyByUser){
        case "Makkelijk":// Means 'easy' in dutch
            //Assigning the easy dictionary to a temporary dictionary
            self.temp = self.easy
        case "Normaal":
            //Assigning the normal dictionary to a temporary dictionary
            self.temp = self.normal
        case "Moeilijk":
            //Assigning the hard dictionary to a temporary dictionary
            self.temp = self.moeilijk
        case "Extreem":
            //Assigning the extreme dictionary to a temporary dictionary
            self.temp = self.extreem
        default:
            println("Something went wrong")

    }

    super.viewDidLoad()
    self.answers = [self.btn1, self.btn2, self.btn3, self.btn4]

    pickRandomElement()
    randomizeAnswers()

    let updateTime : Selector = "updateTime"
    timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: updateTime, userInfo: nil, repeats: true)
    startTime = NSDate.timeIntervalSinceReferenceDate()

}

As you can see i'm assigning all my buttons to the array 'answers' which i will be looping through soon.

The function pickRandomElement will then be called, which looks like this:

func pickRandomElement() -> () {
    //First here we make sure that the 'answerOptions' Array is cleared,
    //Because it could cause that the correct answer won't be showed.
    answerOptions = [String]()


    if self.selectedDifficultyByUser == "Makkelijk" {
        let index: Int = Int(arc4random_uniform(UInt32(easy.count)))

        let symbol = Array(easy.keys)[index]
        let element = Array(easy.values)[index]

        self.currentQuestion = symbol
        self.correctAnswer = element

        //Assign the correctAnswer to the answerOptions array
        self.answerOptions.append(element)

        //Show the question to the user
        self.questionLabel.text = self.currentQuestion

        //remove the correctanswer from the dictionary, to
        //make sure that the answers won't be duplicated
        self.easy[symbol] = nil
        self.easy[element] = nil

        for (var i = 0; i < 3; i++) {
            let randomIndex: Int = Int(arc4random_uniform(UInt32(easy.count)))

            let optionSymbol = Array(easy.keys)[randomIndex]
            let optionElement = Array(easy.values)[randomIndex]

            self.answerOptions.append(optionElement)

            //Removing 'optionSymbol' and 'optionElement' from the array
            //to prevent duplicated answer
            self.easy[optionElement] = nil
            self.easy[optionSymbol] = nil
        }
        self.easy = self.temp //Filling the 'easy' array with the temporary array

    }
}

The problem i'm having is in the for loop shown above. I'm looping three times to pick random elements and show them to the user. And during this process i'm deleting the randomly chosen elements from the (easy) array to prevent duplicate answer. And because i'm removing those elements from the easy array i'm assigning the temporary array, which i created in the begin, back to the easy array.

If i don't do this, the whole easy array will be empty after three rounds or so. If i do the opposite i will get an infinte loop.

Can someone please put me in the right direction if i'm doing this the wrong way or help me out of this problem?

Thanks in advance!

UPDATED

Firo's solution worked perfectly for me. But i'm know faced with another bug. Namely, the pickRandomElement function returns sometimes the asked question more than once. I tried to make another dictionary and put the asked questions in there and check to see if that question has already been asked. However, as the number of questions answered increases, this would require a linearly increasing amount of lookups for each question asked, until all lookups is reached. Does someone know how i can take care of this problem? –

O.S.Kaya
  • 108
  • 2
  • 8
  • Where do you get the infinite loop? Obviously there would be a never ending number of rounds, but you would probably handle that with some independent counter or something. – Firo Jun 04 '15 at 13:06
  • A few tips: You don't need to write `self` all the time, it makes the code less readable; to return Void from a function is the default, so you don't need the `()` as the return type from your function – Kametrixom Jun 04 '15 at 13:07
  • @Firo That's the infinite loop what i was trying to tell, that i'm getting a never ending number of rounds. Could you please give me an example how i can implement a counter you're mentioning? – O.S.Kaya Jun 04 '15 at 13:12
  • 1
    @Kametrixom Thank you very much for your advice. I will adjust my code as soon as i fix this problem. – O.S.Kaya Jun 04 '15 at 13:13

2 Answers2

1

So without all of your code I can only give you an educated guess as to a good way of handing your infinite loop situation with your current code.

An easy way to prevent an infinite loop here is to verify you still have remaining questions you want to ask. Keep an instance value to check how many questions you still want to ask.

var remainingSteps = 5

Then when the user answers the correct question, check it before presenting the next question

func userAnsweredQuestionCorrectly() {
    if self.remainingSteps > 0 {
        // Present next question
        // Decrement remaining steps
        self.remainingSteps--
    else {
        // Done asking questions, back to menu (or whatever is next)
    }
}
Firo
  • 15,448
  • 3
  • 54
  • 74
  • Thank's for your answer. I'm actually doing all the logic in one VC which is called 'QuestionController.swift'. If the user answer's the question correctly or wrong the function pickRandomElement again. So the view will be updated only. – O.S.Kaya Jun 04 '15 at 13:34
  • Thank you man! You saved me a lot of time! As soon as i get 15 reputations i'll up vote your answer, trust me :) – O.S.Kaya Jun 04 '15 at 13:44
  • Ha, no problem, since you asked the question you should still be able to accept the answer. If you look under the voting buttons there should be a grayed out checkmark you can click on. – Firo Jun 04 '15 at 13:54
0

Firo's solution is awesome. This one might help you with getting a unique question each time to avoid repetition. Let me know here or on codementor if you have any questions.

Community
  • 1
  • 1
Iman Mk
  • 1
  • 1
  • Thanks for adding more info. However as you approve of Firo's answer this should probably be a comment rather than a new answer. Also, when quoting other answers it is best to place at least the core of the answer rather than just a link - links can become broken. – Ali Beadle Jun 10 '15 at 18:48
  • Thanks Ali! I'm new here so I didn't know exactly how I should post. + I can't comment because I need at least 50 reputation. Is there any alternative? – Iman Mk Jun 10 '15 at 19:57