-2

I'm a total beginner and have almost no knowledge in programming, but i'm helping my 11 year-old son in creating a small quiz app, and we have a big problem as we can not make the questions in each category not to repeat before all the questions have appeared. Please find below our current situation (inspired from an example we found online) and I would greatly appreciate your help on this issue, if possible if there is a simple solution that does not require a great xcode experience to implement it.

Thanks in advance and of course please let me know if the question needs further clarification:

-(void)Category2{

switch (QuestionSelected) {
    case 0:
        QuestionText.text = [NSString stringWithFormat:@"question1?"];
        [Answer1 setTitle:@"A1" forState:UIControlStateNormal];
        [Answer2 setTitle:@"A2" forState:UIControlStateNormal];
        [Answer3 setTitle:@"A3" forState:UIControlStateNormal];
        Answer3Correct = YES;
        break;

    case 1:
        QuestionText.text = [NSString stringWithFormat:@"question2"];
        [Answer1 setTitle:@"A1" forState:UIControlStateNormal];
        [Answer2 setTitle:@"A2" forState:UIControlStateNormal];
        [Answer3 setTitle:@"A3" forState:UIControlStateNormal];
        Answer2Correct = YES;
        break;

    case 2:
        QuestionText.text = [NSString stringWithFormat:@"question3"];
        [Answer1 setTitle:@"A1" forState:UIControlStateNormal];
        [Answer2 setTitle:@"A2" forState:UIControlStateNormal];
        [Answer3 setTitle:@"A3" forState:UIControlStateNormal];
        Answer1Correct = YES;
        break;




    default:
        break;
}

}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
    // Custom initialization
}
    return self;
}

- (void)viewDidLoad
{

if (GameInProgress == NO) {
    LivesNumber = 3;
    ScoreNumber = 0;
    GameInProgress = YES;
}


Result.hidden = YES;
Exit.hidden = YES;
StartOver.hidden = YES;

Lives.text = [NSString stringWithFormat:@"%i", LivesNumber];
Score.text = [NSString stringWithFormat:@"%i", ScoreNumber];



Answer1Correct = NO;
Answer2Correct = NO;
Answer3Correct = NO;


LevelLoaded = [[NSUserDefaults standardUserDefaults] integerForKey:@"LevelSaved"];

QuestionSelected = arc4random_uniform(5);

Addendum (04.07.2014)

Please forgive me as we are new to the site and we have responded to PJS in the comments below his answer and I believe the format makes it unclear. Please find below, once again a copy of the current state of our project, hoping he can see what is going wrong (duplicates still appear):

   LevelLoaded = [[NSUserDefaults standardUserDefaults] integerForKey:@"LevelSaved"];

int length = 20;
int myArray [] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};


for (int i = 0; i < length - 1; ++i) {
    int j = arc4random_uniform(length - i) + i;
    int tmp = myArray[i];
    myArray[i] = myArray[j];
    myArray[j] = tmp;


}


for (int i = 0; i < length; ++i) {
    QuestionSelected = myArray[i];
}



switch (LevelLoaded) {
    case 1:
        LevelSelected.text = [NSString stringWithFormat:@"Beginner"];
        [self Category1];
        break;
    case 2:
        LevelSelected.text = [NSString stringWithFormat:@"Intermediate"];
        [self Category2];
        break;
    case 3:
        LevelSelected.text = [NSString stringWithFormat:@"Expert"];
        [self Category3];
        break;

    default:
        break;



[super viewDidLoad];

Adendum 2 (10.07.2014)

Please find below the entire m file: (the random here only works for category 1) Is this what you mean by the SWITCH BLOCK in the FOR loop? It still continues to repeat the questions. Am I positioning the text wrong?

#import "Game.h"

@interface Game ()


@end


@implementation Game



-(void)RightAnswer{

ScoreNumber = ScoreNumber + 1;
Score.text = [NSString stringWithFormat:@"%i", ScoreNumber];
StartOver.hidden = NO;
Answer1.hidden = YES;
Answer2.hidden = YES;
Answer3.hidden = YES;
QuestionText.hidden = YES;
Result.hidden = NO;
Result.image = [UIImage imageNamed:@"rightanswer.jpg"];


}

-(void)WrongAnswer{

LivesNumber = LivesNumber - 1;
Lives.text = [NSString stringWithFormat:@"%i", LivesNumber];
StartOver.hidden = NO;
Answer1.hidden = YES;
Answer2.hidden = YES;
Answer3.hidden = YES;
QuestionText.hidden = YES;
Result.hidden = NO;
Result.image = [UIImage imageNamed:@"wronganswer.jpg"];

if (LivesNumber == 0) {
    Result.image =  [UIImage imageNamed:@"gameover.jpg"];
    StartOver.hidden = YES;
    Exit.hidden = NO;
    GameInProgress = NO;

}

}


-(IBAction)Answer1:(id)sender{


if (Answer1Correct == YES) {
    [self RightAnswer];
}
else{
    [self WrongAnswer];
}

}

-(IBAction)Answer2:(id)sender{

if (Answer2Correct == YES) {
    [self RightAnswer];
}
else{
    [self WrongAnswer];
}

}
-(IBAction)Answer3:(id)sender{

if (Answer3Correct == YES) {
    [self RightAnswer];
}
else{
    [self WrongAnswer];
}

}


-(void)Category1{

int length = 3;
int myArray [] = {0,1,2};


for (int i = 0; i < length - 1; ++i) {
    int j = arc4random_uniform(length - i) + i;
    int tmp = myArray[i];
    myArray[i] = myArray[j];
    myArray[j] = tmp;

}

for (int i = 0; i < length; ++i) {
    QuestionSelected = myArray[i];
}

switch (QuestionSelected) {


    case 0:
        QuestionText.text = [NSString stringWithFormat:@"question1"];
        [Answer1 setTitle:@"answer1" forState:UIControlStateNormal];
        [Answer2 setTitle:@"answer2" forState:UIControlStateNormal];
        [Answer3 setTitle:@"answer3" forState:UIControlStateNormal];
        Answer3Correct = YES;
        break;

    case 1:
        QuestionText.text = [NSString stringWithFormat:@"question2"];
        [Answer1 setTitle:@"answer1" forState:UIControlStateNormal];
        [Answer2 setTitle:@"answer2" forState:UIControlStateNormal];
        [Answer3 setTitle:@"answer3" forState:UIControlStateNormal];
        Answer1Correct = YES;
        break;

    case 2:
        QuestionText.text = [NSString stringWithFormat:@"question3"];
        [Answer1 setTitle:@"answer1" forState:UIControlStateNormal];
        [Answer2 setTitle:@"answer2" forState:UIControlStateNormal];
        [Answer3 setTitle:@"answer3" forState:UIControlStateNormal];
        Answer1Correct = YES;
        break;


    default:
        break;


}


}
-(void)Category2{

switch (QuestionSelected) {
    case 0:
        QuestionText.text = [NSString stringWithFormat:@"question1"];
        [Answer1 setTitle:@"answer1" forState:UIControlStateNormal];
        [Answer2 setTitle:@"answer2" forState:UIControlStateNormal];
        [Answer3 setTitle:@"answer3" forState:UIControlStateNormal];
        Answer3Correct = YES;
        break;

    case 1:
        QuestionText.text = [NSString stringWithFormat:@"question2"];
        [Answer1 setTitle:@"T.Cubillas(PER)" forState:UIControlStateNormal];
        [Answer2 setTitle:@"R.Rensenbrink(NET)" forState:UIControlStateNormal];
        [Answer3 setTitle:@"J.Jordan(SCO)" forState:UIControlStateNormal];
        Answer2Correct = YES;
        break;

    case 2:
        QuestionText.text = [NSString stringWithFormat:@"question3"];
        [Answer1 setTitle:@"answer1" forState:UIControlStateNormal];
        [Answer2 setTitle:@"answer2" forState:UIControlStateNormal];
        [Answer3 setTitle:@"answer3" forState:UIControlStateNormal];
        Answer1Correct = YES;
        break;



    default:
        break;
}

}
-(void)Category3{

switch (QuestionSelected) {
    case 0:
        QuestionText.text = [NSString stringWithFormat:@"question1"];


        [Answer1 setTitle:@"answer1" forState:UIControlStateNormal];
        [Answer2 setTitle:@"answer2" forState:UIControlStateNormal];
        [Answer3 setTitle:@"answer3" forState:UIControlStateNormal];
        Answer2Correct = YES;
        break;

    case 1:
        QuestionText.text = [NSString stringWithFormat:@"question2"];
        [Answer1 setTitle:@"answer1" forState:UIControlStateNormal];
        [Answer2 setTitle:@"answer2" forState:UIControlStateNormal];
        [Answer3 setTitle:@"answer3" forState:UIControlStateNormal];
        Answer2Correct = YES;
        break;

    case 2:
        QuestionText.text = [NSString stringWithFormat:@"question3"];
        [Answer1 setTitle:@"answer1" forState:UIControlStateNormal];
        [Answer2 setTitle:@"answer2" forState:UIControlStateNormal];
        [Answer3 setTitle:@"answer3" forState:UIControlStateNormal];
        Answer3Correct = YES;
        break;

    default:
        break;
}



}



- (void)viewDidLoad

{

if (GameInProgress == NO) {
    LivesNumber = 3;
    ScoreNumber = 0;
    GameInProgress = YES;
}


Result.hidden = YES;
Exit.hidden = YES;
StartOver.hidden = YES;

Lives.text = [NSString stringWithFormat:@"%i", LivesNumber];
Score.text = [NSString stringWithFormat:@"%i", ScoreNumber];



Answer1Correct = NO;
Answer2Correct = NO;
Answer3Correct = NO;


LevelLoaded = [[NSUserDefaults standardUserDefaults] integerForKey:@"LevelSaved"];




switch (LevelLoaded) {


    case 1:
        LevelSelected.text = [NSString stringWithFormat:@"Beginner"];
        [self Category1];
        break;
    case 2:
        LevelSelected.text = [NSString stringWithFormat:@"Intermediate"];
        [self Category2];
        break;
    case 3:
        LevelSelected.text = [NSString stringWithFormat:@"Expert"];
        [self Category3];
        break;

    default:
        break;


[super viewDidLoad];

}

}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];

}

@end

1 Answers1

1

The statement QuestionSelected = arc4random_uniform(5); can (and usually will) generate duplicate values before you've enumerated all of the possibilities. That's the nature of randomness. What you should do instead is create an array of the question numbers {0,1,2,3,4} and shuffle the array. Then iterate through the shuffled array, which will enumerate all of the questions in a randomized order with no repeats. Wikipedia has a nice article on the Fisher-Yates shuffling algorithm. Shuffling n values is efficient, it only takes O(n) work.

ADDENDUM

You can construct and shuffle a C array, an NSArray would be overkill for a handful of integers:

/* Create an array of 5 indices */
int length = 5;
int myArray[] = {0,1,2,3,4};

/* This loop performs a Fisher-Yates shuffle.   */
/* Could be implemented as a function or method */
/* for clarity or encapsulation purposes.       */
for (int i = 0; i < length - 1; ++i) {
    int j = arc4random_uniform(length - i) + i;
    int tmp = myArray[i];
    myArray[i] = myArray[j];
    myArray[j] = tmp;
}

Now replace your statement QuestionSelected = arc4random_uniform(5);, which can deal out duplicates, with:

for (int i = 0; i < length; ++i) {
    QuestionSelected = myArray[i];
    // do whatever the heck you want with QuestionSelected
}

Each iteration through the loop will give you a different question, in random order, with no duplicates.

pjs
  • 18,696
  • 4
  • 27
  • 56
  • PJS: Thank you very much for your answer. This option is perfect for us but considering our poor programming knowledge, I would like to ask you if you could help us telling us more specifically how to create the array (where and which statement to implement in both the .n and the .h files), considering the way we have organised the app (Switch, NsString, cases, etc). Again, thank you very much for your help. – user3762849 Jun 26 '14 at 01:50
  • PJS: Again, thank you very much for your help and for taking time to give us a hand on this. – user3762849 Jul 04 '14 at 11:54
  • PJS: Unfortunately I have copied your proposal (20 questions instead of 5) and maybe I'm missing something but I still get the questions several times. Please find in the next comment (not enough space) a copy of the current state and with some chance you might see what is wrong: – user3762849 Jul 04 '14 at 12:20
  • LevelLoaded = [[NSUserDefaults standardUserDefaults] integerForKey:@"LevelSaved"]; int length = 20; int myArray [] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}; for (int i = 0; i < length - 1; ++i) { int j = arc4random_uniform(length - i) + i; int tmp = myArray[i]; myArray[i] = myArray[j]; myArray[j] = tmp; } for (int i = 0; i < length; ++i) { QuestionSelected = myArray[i]; } – user3762849 Jul 04 '14 at 12:24
  • The `switch` block needs to be in the `for` loop where a question is being chosen. – pjs Jul 04 '14 at 16:46
  • I still get repeated questions and i imagine I am not positioning the text correctly. I have attached the whole m file in order to give let you see what am I doing wrong. Thanks very much once again – user3762849 Jul 10 '14 at 11:41
  • Do you see in my answer where I placed the comment `// do whatever the heck you want with QuestionSelected`? That's where your switch block ought to be, so that you can work with the `QuestionSelected`. – pjs Jul 10 '14 at 13:30
  • pjs: I’m really sorry that I don’t understand what you are trying to say. In the copy of the m file above, I thought I did what you said (put the line: ‘QuestionSelected = myArray[i]; ‘ just before the line: ‘switch (QuestionSelected) { ‘ … and all this within (Void)Category 1{ The problem with this is that the randomness only happens in Category1 and the repetition continues. If I move the ‘}’ and put it after the list of questions, the repetition continues and additionally, the RightAnswer/WrongAnswer function does not work anymore. – user3762849 Jul 11 '14 at 09:14
  • On the other hand if I put the text you told me just before the line: ‘(Void)Category 1{‘ … I then get an error message in both “for” lines (Parse Issue – Expected indentifier or ‘(‘ Please let me know if you can identify where the problem is. Thanks again and sorry for not getting it more quickly. – user3762849 Jul 11 '14 at 09:14
  • We're getting to where you probably should have somebody local with eyes on and knowledge of programming show you how to use the shuffle. – pjs Jul 11 '14 at 14:44