Quiz Master -- Randomizing Questions

Background: Making a Flash Card Quiz Game based on QuizMaster but lots customized. My first mostly unsupervised-by-a-tutorial project.

I have some functionality for selecting how many questions you want. Increments of 5 up to 25 max. I’m trying to randomize the questions that actually show in the final quiz. After selecting the categories the user wants to test and the number of questions, they hit “submit” and that will give me a random value which I’ll use at the index to the cleanedQuestionPool to add that question to the finalQuestionPool [the pool you will actually see in the quiz]. There’s 3 special scenarios: The cleanedQuestionPool < numberOfQuestions, cleanedQuestionPool = numberOfQuestions, and cleanQuestionPool > numberOfQuestions.

If <, I want it to just do it’s thing. Duplicates are fine.
If =, I want it to randomize the order, but include all of them.
if >, I want it to only give me whatever number = numberOfQuestions, but no duplicates.

This is my Code:


public void RandomizeQuestionPool()
    {

        Debug.Log("Randomizing...");
        Debug.Log("Number of Questions Selcted is " + MainMenu.numberOfQuestions);
        if (cleanedQuestionPool.Count < MainMenu.numberOfQuestions)
        {
            Debug.Log("Cleaned Question Pool Count is < numberOfQuestions");
            for (int i = 0; i < MainMenu.numberOfQuestions; i++)
            {
                int randomValue = Random.Range(1, cleanedQuestionPool.Count);
                finalQuestionPool.Add(cleanedQuestionPool[randomValue]);
            }
        }
        if (cleanedQuestionPool.Count == MainMenu.numberOfQuestions)
        {
            Debug.Log("Cleaned QPool Count is = numberOfQuestions");
            for(int i = 0; i < cleanedQuestionPool.Count; i++)
            {
                int randomValue = Random.Range(1, cleanedQuestionPool.Count);
                QuestionSO questionItem = cleanedQuestionPool[randomValue];
                if (finalQuestionPool.Contains(questionItem))
                {
                    Debug.Log("Question Item exists in finalQuestionPool. Continue.");
                    continue;
                }
                else
                {
                    Debug.Log("Question Item does not exist in finalQuestionPool. Adding.");
                    finalQuestionPool.Add(cleanedQuestionPool[randomValue]);
                }
            }
        }
        if (cleanedQuestionPool.Count > MainMenu.numberOfQuestions)
        {
            Debug.Log("Cleaned QPool Count is > numberOfQuestions");
            for (int i = 0; i < MainMenu.numberOfQuestions; i++)
            {
                int randomValue = Random.Range(1, cleanedQuestionPool.Count);
                QuestionSO questionItem = cleanedQuestionPool[randomValue];
                if (finalQuestionPool.Contains(questionItem))
                {
                    Debug.Log("Question Item exists in finalQuestionPool. Continue.");
                    continue;
                }
                else
                {
                    Debug.Log("Question Item does not exist in finalQuestionPool. Adding");
                    finalQuestionPool.Add(cleanedQuestionPool[randomValue]);
                }
            }
        }
    }

I am not quite sure I got it right.

The first one works just fine.
The second one I tested on a list that had 5 questions in the cleanedQuestionPool to start with and after randomizing and adding the results to finalQuestionPool, FQP only had 3 questions. Which I don’t want.
The third option is the same as the second, obviously. Its practically the same code. At first it was working fine with a large number of options, but I tested it with something that was smaller and it’s not.

What am I doing wrong with number 2 & 3?

I’m starting to think it has to do with that “continue”? I want the If statement to continue to loop after checking if the question already exists in our final question pool, until it finds something that doesn’t exist.

1 Like

Hi,

Please note, it’s better to copy/paste your code and apply the code fencing characters, rather than using screenshots. Screenshots are ideal for displaying specific details from within a game engine editor or even error messages, but for code, they tend to be less readable, especially on mobile devices which can require extensive zooming and scrolling.

You also prevent those that may offer to help you the ability to copy/paste part of your code back to you with suggestions and/or corrections, meaning that they would need to type a potentially lengthy response. You will often find that people are more likely to respond to your questions if you make it as easy as possible for them to do so.

The “continue” part does not do anything that the code would not do anyway without it. If the if-condition gets evaluated to true, the else gets skipped. Since there is no code after the else within the for-loop code block, the loop continues.

One thing I noticed: In the method, you have two ifs. If there any special reson why the second if is not an else if? This could be a potential problem because if the second if-condition gets evaluated to true, the else-block gets executed.

Hope this helps :slight_smile:


See also;

Ok! Thanks for that lil tidbit about the code formatting. With a little weirdness I got it figured out [its the tilde-postrophe not the Apostrophe, so… Yay!]

One thing I noticed: In the method, you have two ifs. If there any special reson why the second if is not an else if? This could be a potential problem because if the second if-condition gets evaluated to true, the else-block gets executed.

I have literally no reason for doing this other than I forgot about “else if.” lol

If the if-condition gets evaluated to true, the else gets skipped. Since there is no code after the else within the for-loop code block, the loop continues.

So then how do I tell it to just redo the loop within the same increment? I don’t want i to increment and pass over that increment with nothing in it. I want it to check if the question is already in the pool, and if it is, just re-roll until you get one that isn’t. Do I just have to nest a bunch of if’s in there. That doesn’t seem efficient.

You don’t have to do anything. Your code is the same as this:

    for (int i = 0; i < MainMenu.numberOfQuestions; i++)
            {
                int randomValue = Random.Range(1, cleanedQuestionPool.Count);
                QuestionSO questionItem = cleanedQuestionPool[randomValue];

                if (!finalQuestionPool.Contains(questionItem))
                {
                    Debug.Log("Question Item does not exist in finalQuestionPool. Adding");
                    finalQuestionPool.Add(cleanedQuestionPool[randomValue]);
                }
            }

The loop executes the code block, then it continues automatically. If the if-condition was evaluated to false, the if-block does not get executed. The loop continues anyway.

At the moment, I’m not quite sure why you have three different scenarios. In all three cases, you have finalQuestionPool.Add(cleanedQuestionPool[randomValue]);. I feel like the current state of the code could be refactored a bit to make your actual idea more obvious.

RandomizeQuestionPool sounds as if there is a pool with questions. And all you want to do is to randomise the elements in the pool. What I would expect when reading the code is a local variable which holds all available indices. And a variable which holds the elements in the new order. And the last line would be that you either return the list/array with the elements or assign it to the variable which you use elsewhere in your code.

I would opt for the method which returns the randomised pool. The technique I’m using is called “method overloading”. Don’t worry if you do not know it yet. As aforementioned, assigning the result to the instance variable works too.

Here’s a template in case my answer was a bit confusing. I also added a bunch of lengthy comments which should be removed in an actual solution.

// What you call in another method:
// originalPool = RandomizeQuestionPool(originalPool);

public QuestionSO[] RandomizeQuestionPool (QuestionSO[] pool)
{
    // For reasons of performance, always initialise a collection with a capacity
    // here: the number of elements in the original pool 'pool'
    // the "slots" in the array are empty/null
    QuestionSO[] randomizedPool = new QuestionSO[pool.Length];

    // The list with all available indices
    List<int>() indices = new List<int>();
    for (int i = 0; i < pool.Length; i++) { indices.Add(i); }

    // your code for randomising the order of the elements
    // populate the 'randomizedPool', do not manipulate the 'pool'

   return randomizedPool;
}

I used an array in this case but a List would also be fine. Since I do not know your entire code, I do not know if I used the correct type. However, I’m sure that you will be able to understand the code anyway.

Alternatively, you could create a temporary list with all QuestionSO elements instead of integers.

There are probably more solutions.

Think about your actual goal first, not about the implementation. Then try to form some kind of a module/unit. If you gave that module to another person, maybe a non-programmer, they should ideally be able to understand your idea just by skimming the code. If the code is so cryptic that it hides the actual idea (“I want to randomise the questions”), there is usually a flaw in the code.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms