I am making a simple memory game app and I want to have it randomly place the pictures. so I was using a arc4random() %4
. I have 4 pictures I need 2 of each to show up for a total of 8. but when I do the arc4random() it gives me more than 2 of each.
here is my code this is my .M file
#import "GameViewController.h"
@interface GameViewController ()
@end
@implementation GameViewController
-(void)Card1SelectedType{
switch (card1Type) {
case 0:
{
UIImage *ButtonImage = [UIImage imageNamed:@"cell phone.jpeg"];
[card1 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card1];
}
break;
case 1:
{
UIImage *ButtonImage = [UIImage imageNamed:@"Dinasore.jpeg"];
[card1 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card1];
}
break;
case 2:
{
UIImage *ButtonImage = [UIImage imageNamed:@"jump Rope.jpeg"];
[card1 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card1];
}
break;
case 3:
{
UIImage *ButtonImage = [UIImage imageNamed:@"monkey.jpeg"];
[card1 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card1];
}
break;
default:
break;
}
}
-(void)Card2SelectedType{
switch (card2Type) {
case 0:
{
UIImage *ButtonImage = [UIImage imageNamed:@"cell phone.jpeg"];
[card2 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card2];
}
break;
case 1:
{
UIImage *ButtonImage = [UIImage imageNamed:@"Dinasore.jpeg"];
[card2 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card2];
}
break;
case 2:
{
UIImage *ButtonImage = [UIImage imageNamed:@"jump Rope.jpeg"];
[card2 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card2];
}
break;
case 3:
{
UIImage *ButtonImage = [UIImage imageNamed:@"monkey.jpeg"];
[card2 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card2];
}
break;
default:
break;
}
}
-(void)Card3SelectedType;{
switch (card3Type) {
case 0:
{
UIImage *ButtonImage = [UIImage imageNamed:@"cell phone.jpeg"];
[card3 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card3];
}
break;
case 1:
{
UIImage *ButtonImage = [UIImage imageNamed:@"Dinasore.jpeg"];
[card1 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card3];
}
break;
case 2:
{
UIImage *ButtonImage = [UIImage imageNamed:@"jump Rope.jpeg"];
[card3 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card3];
}
break;
case 3:
{
UIImage *ButtonImage = [UIImage imageNamed:@"monkey.jpeg"];
[card3 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card3];
}
break;
default:
break;
}
}
-(void)Card4SelectedType{
switch (card4Type) {
case 0:
{
UIImage *ButtonImage = [UIImage imageNamed:@"cell phone.jpeg"];
[card4 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card4];
}
break;
case 1:
{
UIImage *ButtonImage = [UIImage imageNamed:@"Dinasore.jpeg"];
[card4 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card4];
}
break;
case 2:
{
UIImage *ButtonImage = [UIImage imageNamed:@"jump Rope.jpeg"];
[card4 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card4];
}
break;
case 3:
{
UIImage *ButtonImage = [UIImage imageNamed:@"monkey.jpeg"];
[card4 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card4];
}
break;
default:
break;
}
}
-(void)Card5SelcetedType{
switch (card5Type) {
case 0:
{
UIImage *ButtonImage = [UIImage imageNamed:@"cell phone.jpeg"];
[card5 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card5];
}
break;
case 1:
{
UIImage *ButtonImage = [UIImage imageNamed:@"Dinasore.jpeg"];
[card5 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card5];
}
break;
case 2:
{
UIImage *ButtonImage = [UIImage imageNamed:@"jump Rope.jpeg"];
[card5 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card5];
}
break;
case 3:
{
UIImage *ButtonImage = [UIImage imageNamed:@"monkey.jpeg"];
[card5 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card5];
}
break;
default:
break;
}
}
-(void)Card6SelectedType{
switch (card6Type) {
case 0:
{
UIImage *ButtonImage = [UIImage imageNamed:@"cell phone.jpeg"];
[card6 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card6];
}
break;
case 1:
{
UIImage *ButtonImage = [UIImage imageNamed:@"Dinasore.jpeg"];
[card6 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card6];
}
break;
case 2:
{
UIImage *ButtonImage = [UIImage imageNamed:@"jump Rope.jpeg"];
[card6 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card6];
}
break;
case 3:
{
UIImage *ButtonImage = [UIImage imageNamed:@"monkey.jpeg"];
[card6 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card6];
}
break;
default:
break;
}
}
-(void)Card7SelectedType{
switch (card7Type) {
case 0:
{
UIImage *ButtonImage = [UIImage imageNamed:@"cell phone.jpeg"];
[card7 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card7];
}
break;
case 1:
{
UIImage *ButtonImage = [UIImage imageNamed:@"Dinasore.jpeg"];
[card7 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card7];
}
break;
case 2:
{
UIImage *ButtonImage = [UIImage imageNamed:@"jump Rope.jpeg"];
[card7 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card7];
}
break;
case 3:
{
UIImage *ButtonImage = [UIImage imageNamed:@"monkey.jpeg"];
[card7 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card7];
}
break;
default:
break;
}
}
-(void)Card8SelectedType{
switch (card8Type) {
case 0:
{
UIImage *ButtonImage = [UIImage imageNamed:@"cell phone.jpeg"];
[card8 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card8];
}
break;
case 1:
{
UIImage *ButtonImage = [UIImage imageNamed:@"Dinasore.jpeg"];
[card8 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card8];
}
break;
case 2:
{
UIImage *ButtonImage = [UIImage imageNamed:@"jump Rope.jpeg"];
[card8 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card8];
}
break;
case 3:
{
UIImage *ButtonImage = [UIImage imageNamed:@"monkey.jpeg"];
[card8 setImage:ButtonImage forState:UIControlStateNormal];
[self.view addSubview:card8];
}
break;
default:
break;
}
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
card1Selected.hidden = YES;
card2Selected.hidden = YES;
card3Selected.hidden = YES;
card4Selected.hidden = YES;
card5Selected.hidden = YES;
card6Selected.hidden = YES;
card7Selected.hidden = YES;
card8Selected.hidden = YES;
card1Type = arc4random() %4;
card2Type = arc4random() %4;
card3Type = arc4random() %4;
card4Type = arc4random() %4;
card6Type = arc4random() %4;
card7Type = arc4random() %4;
card8Type = arc4random() %4;
[self Card1SelectedType];
[self Card2SelectedType];
[self Card3SelectedType];
[self Card4SelectedType];
[self Card5SelcetedType];
[self Card6SelectedType];
[self Card7SelectedType];
[self Card8SelectedType];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
and this is in my .h file
![#import <UIKit/UIKit.h>
int card1Type;
int card2Type;
int card3Type;
int card4Type;
int card5Type;
int card6Type;
int card7Type;
int card8Type;
@interface GameViewController : UIViewController
{
IBOutlet UIButton *card1;
IBOutlet UIButton *card2;
IBOutlet UIButton *card3;
IBOutlet UIButton *card4;
IBOutlet UIButton *card5;
IBOutlet UIButton *card6;
IBOutlet UIButton *card7;
IBOutlet UIButton *card8;
IBOutlet UIImageView *card1Selected;
IBOutlet UIImageView *card2Selected;
IBOutlet UIImageView *card3Selected;
IBOutlet UIImageView *card4Selected;
IBOutlet UIImageView *card5Selected;
IBOutlet UIImageView *card6Selected;
IBOutlet UIImageView *card7Selected;
IBOutlet UIImageView *card8Selected;
}
-(void)Card1SelectedType;
-(void)Card2SelectedType;
-(void)Card3SelectedType;
-(void)Card4SelectedType;
-(void)Card5SelcetedType;
-(void)Card6SelectedType;
-(void)Card7SelectedType;
-(void)Card8SelectedType;
@end][1]
The solution to this problem is simple, to shuffle an array. In this case the array represents a fixed collection of values, much like a deck of playing/poker cards. Shuffling the deck does not add or remove any cards - e.g. there are 4 (and only 4) aces, one of each suit, even after a million shuffles.
The algorithm used is discussed in several other questions, such as Re-arrange NSArray/MSMutableArray in random order, with the (correct) presented solutions usually implementing a Fisher-Yates Shuffle. Wrapping the solution, yields a tidy function:
-(void)shuffleArray: (NSMutableArray*)arr {
NSUInteger count = [arr count];
for (NSUInteger i = 0; i < count; ++i) {
int nElements = count - i;
// See JeremyP's comment
int n = arc4random_uniform(nElements) + 1;
[ar exchangeObjectAtIndex:i withObjectAtIndex:n];
}
}
(Such a shuffle can also be written to work directly against normal C-arrays, which would be sufficient in this case.)
Now, consider the array that initially has the values [0, 0, 1, 1, 2, 2, 3, 3] where each number appears exactly twice and represents an image. It might be created like:
-(NSMutableArray*)createCardTypeDeck: {
NSMutableArray *cardTypes = [NSMutableArray arrayWithCapacity:8];
for (int i = 0; i < 4; i++) {
// Add the same number twice, for a total 8 objects added
[cardTypes addObject:[NSNumber numberWithInteger:i]];
[cardTypes addObject:[NSNumber numberWithInteger:i]];
}
return cardTypes;
}
And finally, let's put this together and remove some of the unnecessary copy'n'paste methods; this could further be refined, but I hope it shows a sufficient change without being "Too Complicated".
// Take in /a/ card and the type, so it will work for all cards;
// don't add the card to the view here. Note there is no hard-coding of
// card1..card8 and thus there is NO NEED to duplicate this method 8 times!
-(void)updateCard: (UIButton*) card, cardType: (int) cardType ({
// The imageName and UIImage creation could be further extracted but
// this should be sufficient to show how much common code (and copy'n'paste)
// can be eliminated - resulting in shorter and more readable code.
NSString *imageName;
switch (cardType) {
case 0:
imageName = @"cell phone.jpeg";
break;
case 1:
imageName = @"Dinasore.jpeg";
break;
case 2:
// .. etc
break;
default:
image = nil; // but really an error of some sort
break;
}
UIImage *image = [UIImage imageNamed:imageName];
[card setImage:image forState:UIControlStateNormal];
}
Now we can treat the cards generically as well, once we treat them as an array.
-(void)viewDidLoad
{
// Create card/cardType deck, values -> [0, 0, 1, 1, 2, 2, 3, 3]
NSMutableArray *cardTypes = [self createCardTypeDeck];
// Shuffle the card types, result e.g. -> [2, 1, 0, 3, 2, 3, 0, 1]
[self shuffleArray: cardTypes];
// At least we only use the names once now
NSArray *cards = [NSArray arrayWithObjects:
card1, card2, card3, card4,
card5, card6, card7, card8, nil];
// For each card, assign it an image and otherwise finish adding it
for (int i = 0; i < 8; i++) {
// Get now shuffled cardType and this index
// (We know that only values 0..3 will appear and each will appear
// exactly twice - as only those values, and that multiplicity,
// have been added to the original array before shuffling.)
int cardType = [[cardTypes objectAtIndex:i] intValue];
// Get the card to apply the changes to, and do so
UIButton* card = [cards objectAtIndex:i];
[self updateCard:card withType:cardType];
// Then add the card view
[self.view addSubview:card];
}
[super viewDidLoad];
}
YMMV. Bugs are free. Have fun.