Please note, this blog entry is from a previous course. You might want to check out the current one.
The Set game only needs to allow users to pick sets and get points for doing so (e.g. it does not redeal new cards when sets are found). In other words, it works just like your other card game (except that it is a 3 card (instead of 2 card) match with different kinds of cards).
In this task we create the actual cards and its deck … and hope that everything is ok because we will not able to test it till later (we could add some unit test, but …)
Similar to PlayingCard, add properties to hold the color, symbol, shading and number of symbols, and provide class methods to access its valid values:
@property (strong, nonatomic) NSString *color; @property (strong, nonatomic) NSString *symbol; @property (strong, nonatomic) NSString *shading; @property (nonatomic) NSUInteger number; + (NSArray *)validColors; + (NSArray *)validSymbols; + (NSArray *)validShadings; + (NSUInteger)maxNumber;
In their setters and getters check for valid values:
@synthesize color = _color, symbol = _symbol, shading = _shading; - (NSString *)color { return _color ? _color : @"?"; } - (void)setColor:(NSString *)color { if ([[SetCard validColors] containsObject:color]) _color = color; } - (NSString *)symbol { return _symbol ? _symbol : @"?"; } - (void)setSymbol:(NSString *)symbol { if ([[SetCard validSymbols] containsObject:symbol]) _symbol = symbol; } - (NSString *)shading { return _shading ? _shading : @"?"; } - (void)setShading:(NSString *)shading { if ([[SetCard validShadings] containsObject:shading]) _shading = shading; } - (void)setNumber:(NSUInteger)number { if (number <= [SetCard maxNumber]) _number = number; }
The contents getter provides the symbol string:
- (NSString *)contents { return self.symbol; }
The class methods provide the valid values:
+ (NSArray *)validColors { return @[@"red", @"green", @"purple"]; } + (NSArray *)validSymbols { return @[@"oval", @"squiggle", @"diamond"]; } + (NSArray *)validShadings { return @[@"solid", @"open", @"striped"]; } + (NSUInteger)maxNumber { return 3; }
In the matching method check first if there are the right number of cards. Then create an array for each property and fill them looping over all cards. A set matches if all of those arrays contain either a single value or the same amount of different values as matched cards:
#define NUMBER_OF_MATCHING_CARDS 3 - (int)match:(NSArray *)otherCards { int score = 0; if ([otherCards count] == NUMBER_OF_MATCHING_CARDS - 1) { NSMutableArray *colors = [[NSMutableArray alloc] init]; NSMutableArray *symbols = [[NSMutableArray alloc] init]; NSMutableArray *shadings = [[NSMutableArray alloc] init]; NSMutableArray *numbers = [[NSMutableArray alloc] init]; [colors addObject:self.color]; [symbols addObject:self.symbol]; [shadings addObject:self.shading]; [numbers addObject:@(self.number)]; for (id otherCard in otherCards) { if ([otherCard isKindOfClass:[SetCard class]]) { SetCard *otherSetCard = (SetCard *)otherCard; if (![colors containsObject:otherSetCard.color]) [colors addObject:otherSetCard.color]; if (![symbols containsObject:otherSetCard.symbol]) [symbols addObject:otherSetCard.symbol]; if (![shadings containsObject:otherSetCard.shading]) [shadings addObject:otherSetCard.shading]; if (![numbers containsObject:@(otherSetCard.number)]) [numbers addObject:@(otherSetCard.number)]; if (([colors count] == 1 || [colors count] == NUMBER_OF_MATCHING_CARDS) && ([symbols count] == 1 || [symbols count] == NUMBER_OF_MATCHING_CARDS) && ([shadings count] == 1 || [shadings count] == NUMBER_OF_MATCHING_CARDS) && ([numbers count] == 1 || [numbers count] == NUMBER_OF_MATCHING_CARDS)) { score = 4; } } } } return score; }
The deck is created by looping over all possible variants:
- (id)init { self = [super init]; if (self) { for (NSString *color in [SetCard validColors]) { for (NSString *symbol in [SetCard validSymbols]) { for (NSString *shading in [SetCard validShadings]) { for (NSUInteger number = 1; number <= [SetCard maxNumber]; number++) { SetCard *card = [[SetCard alloc] init]; card.color = color; card.symbol = symbol; card.shading = shading; card.number = number; [self addCard:card atTop:YES]; } } } } } return self; }
The complete code is available on github.