# cs193p – Assignment #4 Extra Task #1

Please note, this blog entry is from a previous course. You might want to check out the current one.

You could add better score-keeping to the Set part of this application if you can figure out an algorithm for calculating whether a Set exists in the current cards in play. Then you can penalize the user not only for mismatches, but for clicking the “deal 3 more cards” button if he or she missed a Set. You’d also know when the game was “over” (because the user would click on “deal 3 more cards” and there would be no more cards in the deck and no more Sets to choose).

The game model needs a new public method returning matching cards. For this task a method returning a boolean result would be sufficient, but we will use the same method for the next task:

```- (NSArray *)findCombination;
```

First generate a potential combination, then check if that combination is valid (meaning it does not contain cards which are used already by previous matches) and check if the current combination is matching. Continue as long as there is no match or no further combination available:

```- (NSArray *)findCombination
{
Card *card = [self.cards firstObject];
NSMutableArray *combination = [NSMutableArray array];
for (NSUInteger i = 0; i < card.numberOfMatchingCards; i++) {
}
NSArray *foundCombination;
NSArray *nextCombination = combination;
do {
if (![self validCombination:nextCombination]) continue;
if ([self.cards[[nextCombination[0] intValue]] match:[self otherCardsFromCombination:nextCombination]]) {
foundCombination = [self cardsFromCombination:nextCombination];
break;
}
} while ((nextCombination = [self nextCombinationAfter:nextCombination
withNumberOfCards:card.numberOfMatchingCards]));
return foundCombination;
}
```

The method above used various helper methods to create more structured code. For a valid combination check if its cards have not been used for previous matches:

```- (BOOL)validCombination:(NSArray *)combination
{
for (NSNumber *index in combination) {
Card *card = self.cards[[index intValue]];
if (card.matched) return NO;
}
return YES;
}
```

Because the combinations are arrays of indexes (which makes the calculation of permutations much easier), its necessary to derive the actual cards from those indexes:

```- (NSArray *)cardsFromCombination:(NSArray *)combination startinWithIndex:(NSUInteger)start
{
NSMutableArray *cards = [[NSMutableArray alloc] init];
for (NSUInteger i = start; i < [combination count]; i++) {
}
return cards;
}
```

Once this method is needed for all cards, and once for all but the first card:

```- (NSArray *)cardsFromCombination:(NSArray *)combination
{
return [self cardsFromCombination:combination startinWithIndex:0];
}

- (NSArray *)otherCardsFromCombination:(NSArray *)combination
{
return [self cardsFromCombination:combination startinWithIndex:1];
}
```

Finally, the last helper method generates new combinations based on permutations of the previous combination:

```- (NSArray *)nextCombinationAfter:(NSArray *)combination withNumberOfCards:(NSUInteger)numberOfCards
{
NSUInteger n = [self.cards count];
NSUInteger k = numberOfCards;
NSUInteger i = k - 1;
NSMutableArray *next = [combination mutableCopy];
next[i] = @([next[i] intValue] + 1);
while ((i > 0) && ([next[i] intValue] > n - k + i)) {
i--;
next[i] = @([next[i] intValue] + 1);
}
if ([next[0] intValue] > n - k) return nil;
for (i = i + 1; i < k; ++i) {
next[i] = @([next[i - 1] intValue] + 1);
}
return next;
}
```

The score is calculated inside the game model. To adjust for the penalty, when adding new cards though there would have been valid matches, introduce a new property:

```@property (nonatomic) NSInteger scoreAdjustment;
```

Which is used when setting the score label:

```- (void)updateUI
{
...
self.scoreLabel.text = [NSString stringWithFormat:@"Score: %ld",
...
}
```

The penalty is added when there would have been valid combinations. In addition produce an alert message, when the deck is empty and no possible matches are left:

```- (IBAction)touchAddCardsButton:(UIButton *)sender {
if ([[self.game findCombination] count]) {
};
...
if (self.game.deckIsEmpty) {
//sender.enabled = NO;
//sender.alpha = 0.5;
if (![[self.game findCombination] count]) {
message:@"No matches left ..."
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:@"Game Over!", nil];