cs193p – Assignment #4 Task #4

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

As in a real game of Set, the user should start with 12 cards and then have the option of requesting 3 more cards to be dealt at any time if he or she is unable to locate a Set. Do something sensible when there are no more cards in the deck.

Add a new button in storyboard and set its tag to 3 (the number of cards that should be added):

cs193p – assignment #4 task #4 – add cards button
cs193p – assignment #4 task #4 – add cards button


Create an outlet and an action for the new button. When the button is touched add the number of new cards to the game indicated by the tag of the button. If the deck is empty afterwards, disable the button.

@property (weak, nonatomic) IBOutlet UIButton *addCardsButton;
...
- (IBAction)touchAddCardsButton:(UIButton *)sender {
    for (int i = 0; i < sender.tag; i++) {
        [self.game drawNewCard];
    }
    if (self.game.deckIsEmpty) {
        sender.enabled = NO;
        sender.alpha = 0.5;
    }
    [self updateUI];
}

When dealing a new deck, make sure there is no old card view on screen, reset the card array, the grid and the add-cards button:

- (IBAction)touchDealButton:(UIButton *)sender {
    ...
    for (UIView *subView in self.cardViews) {
        [subView removeFromSuperview];
    }
    self.cardViews = nil;
    self.grid = nil;
    self.addCardsButton.enabled = YES;
    self.addCardsButton.alpha = 1.0;
    ...
}

When updating the user interface move the grid calculations to a loop of its own after you reset the grid to a possible new card count:

- (void)updateUI
{
    ...
    self.grid.minimumNumberOfCells = [self.cardViews count];
    for (NSUInteger viewIndex = 0; viewIndex < [self.cardViews count]; viewIndex++) {
        CGRect frame = [self.grid frameOfCellAtRow:viewIndex / self.grid.columnCount
                                          inColumn:viewIndex % self.grid.columnCount];
        frame = CGRectInset(frame, frame.size.width * CARDSPACINGINPERCENT, frame.size.height * CARDSPACINGINPERCENT);
        ((UIView *)self.cardViews[viewIndex]).frame = frame;
    }
    ...
}

The code above introduced two new public methods for the card-matching-game model:

- (void)drawNewCard;
@property (nonatomic, readonly) BOOL deckIsEmpty;

… and it also needs to store the deck (which was discarded previously after the initialization):

@property (strong, nonatomic) Deck *deck;
...
- (instancetype)initWithCardCount:(NSUInteger)count
                        usingDeck:(Deck *)deck
{
    ...
        _deck = deck;
        ...
}

The two new methods just add a new card from the deck to the game, and check if the deck is empty by trying to draw another card:

- (void)drawNewCard
{
    Card *card = [self.deck drawRandomCard];
    if (card) {
        [self.cards addObject:card];
    }
}

- (BOOL)deckIsEmpty
{
    Card *card = [self.deck drawRandomCard];
    if (card) {
        [self.deck addCard:card];
        return NO;
    }
    return YES;
}

When adding lots of new cards, the cards get smaller and smaller which uncovered a problem with a fixed border radios of the set cards, which can easily be adjusted to be relative to the actual card size:

#define CORNER_RADIUS 0.1
- (void)drawRect:(CGRect)rect
{
    UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:self.bounds
                                                           cornerRadius:self.bounds.size.width * CORNER_RADIUS];
    ...
}

The complete code is available on github.

Facebooktwittergoogle_plusredditpinterestlinkedintumblrmailFacebooktwittergoogle_plusredditpinterestlinkedintumblrmail

Flattr this!

6 thoughts on “cs193p – Assignment #4 Task #4”

  1. I wonder : why must the updateUI be changed? What was wrong with

    CGRect frame = [self.grid frameOfCellAtRow:viewIndex / self.grid.columnCount
    inColumn:viewIndex % self.grid.columnCount];
    frame = CGRectInset(frame, frame.size.width * CARDSPACINGINPERCENT, frame.size.height * CARDSPACINGINPERCENT);
    cardView.frame = frame;
    Because upon calling the updateUI the code iterates through all the card indices and consequently the frames of the views are being changed. Tx for your answer once more.

    1. … you really ask tough questions 😉 which show that I could have been more detailed with my explanations …

      Up to now (before the assignment #4 task #4) we only removed cards, and had just more empty space on screen. Now we add new cards! If there is not enough empty space for those new cards, they would be placed outside the screen and not accessible. Thus I added after adding and removing the cards an additional loop to resize the grid for the current count of cards …

      1. How about replace self.grid.minimumNumberOfCells = [self.cardViews count];
        by
        self.grid.minimumNumberOfCells = self.game.numberOfDealtGame;
        then move this to the first line of updateUI.

        We can afterwards remove the additional loop.

Leave a Reply

Your email address will not be published. Required fields are marked *