Stanford CS 193P iOS 7 — Домашнее задание 1

Начало игрыСодержание

Текст Домашнего задания на английском языке доступен на  iTunes в пункте “Developing iOS 7 app:Assignment 1″На русском языке

Задание 1 Matchismo fall 2013.pdf

Пункт 1

Следуйте пошаговой инструкции на слайдах лекции (отдельный документ) для построения и запуска Matchismo на симуляторе iPhone (iPhone Simulator) в Xcode 5. Не переходите к следующим шагам до тех пор, пока ваша карта не будет переворачиваться “лицевой” и обратной стороной без ошибок и предупреждений

 

Вторая часть слайдов Лекции 2 содержит пошаговую инструкцию для выполнения  демонстрационного проекта, в котором создаются классы для Домашнего задания. Следуя этой пошаговой инструкции, вы полностью выполните пункт 1 первого домашнего задания. Рекомендуется не пользоваться copy/paste, а набирать весь код вручную для лучшего освоения Xcode.

 

Если у вас все-таки возникнут проблемы с кодом, вы можете найти его на Github.

Пункт 2

Убедитесь, что вы включили в приложение весь приведенный на первых двух лекциях код всех четырех классов: Card, Deck, PlayingCard и PlayingCardDeck. Вы должны напечатать этот код (а не копировать и вставлять его откуда-то еще). Цель этого обязательного задания состоит в получении опыта редактирования кода в Xcode и освоении синтаксиса Objective-C.

 

Начните вводить в ваш проект классы, представленные в   пошаговой инструкции воспроизведения демонстрационного проекта.  Основная структура класса Card уже добавлена в течение Лекции №2. Сформируйте public interface этого класса и метод  match:.


//  Card.h
//  Matchismo

#import <Foundation/Foundation.h>

@interface Card : NSObject

@property (strong,nonatomic) NSString *contents;
@property (nonatomic,getter = isChosen) BOOL chosen;
@property (nonatomic,getter = isMatched) BOOL matched;

-(int)match:(NSArray *)otherCards;
@end

//  Card.m
//  Matchismo

#import "Card.h"

@interface Card ()

@end
@implementation Card

-(int)match:(NSArray *)otherCards
{
    int score =0;

    for (Card *card in otherCards) {
        if ([card.contents isEqualToString:self.contents]) {
            score = 1;
        }
    }
    return score;
}
@end
 

Добавьте новый класс Deck для колоды карт. Сформируйте его public interface (не забудьте включить класс  Card для доступа к нему внутри нового класса) и реализуйте его методы, а также getter для  lazy instantiation (отложенного получения экземпляра класса).

//  Deck.h
//  Matchismo

#import <Foundation/Foundation.h>
#import "Card.h"

@interface Deck : NSObject

- (void)addCard:(Card *)card atTop:(BOOL)atTop;
- (void)addCard:(Card *)card;
- (Card *)drawRandomCard;

@end

//  Deck.m
//  Matchismo

#import "Deck.h"

@interface Deck ()
@property (strong,nonatomic) NSMutableArray *cards; // of Card
@end

@implementation Deck

- (NSMutableArray *)cards
{
    if (!_cards) {
        _cards = [[NSMutableArray alloc] init];
    }
    return _cards;
}

- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
    if (atTop) {
        [self.cards insertObject:card atIndex:0];
    } else {
        [self.cards addObject:card];
    }
}

- (void)addCard:(Card *)card
{
    [self addCard:card atTop:NO];
}

 -(Card *)drawRandomCard
{
    Card *randomCard = nil;
    if ([self.cards count]) {
        unsigned index = arc4random() % [self.cards count];
        randomCard = self.cards[index];
        [self.cards removeObjectAtIndex:index];
    }
    return randomCard;
}
@end
 

Добавьте еще один новый класс PlayingCard как сабкласс (subclass) класса Card. Сформируйте его public interface и реализуйте его методы. Не забудьте сделать synthesize для suit свойства, так как используются оба его setter & getter.

//  PlayingCard.h
//  Matchismo

#import "Card.h"

@interface PlayingCard : Card

@property (strong, nonatomic) NSString *suit;
@property (nonatomic) NSUInteger rank;

+ (NSArray *)validSuits;
+ (NSUInteger)maxRank;

@end
//  PlayingCard.m
//  Matchismo

#import "PlayingCard.h"

@implementation PlayingCard

- (NSString *)contents
{
    NSArray *rankStrings = [PlayingCard rankStrings];
    return [rankStrings[self.rank] stringByAppendingString:self.suit];
}

@synthesize suit =_suit; // потому что мы реализовали setter И getter

 + (NSArray *)validSuits
{
    return @[@"♥️", @"♦️", @"♠️", @"♣️"];
}

 -(void)setSuit:(NSString *)suit
{
    if ([[PlayingCard validSuits] containsObject:suit]) {
        _suit =suit;
    }
}

- (NSString *)suit
{
    return _suit ? _suit : @"?";
}

+ (NSArray *)rankStrings
{
    return  @[@"?", @"A", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10", @"J", @"Q", @"K"];

}
+ (NSUInteger)maxRank
{
    return [[self rankStrings] count]-1;
}
 -(void)setRank:(NSUInteger)rank
{
    if (rank<= [PlayingCard maxRank]) {
        _rank = rank;
    }
}

-(int)match:(NSArray *)otherCards
{
    int score =0;
    int numMatches = 0;
    if ([otherCards count] > 0)
    {
        for (int i = 0; i < [otherCards count]; i++) {
            id otherCard1 = otherCards[i];
            if ([otherCard1 isKindOfClass:[PlayingCard class]])
            {
                PlayingCard *card1 = (PlayingCard *)otherCard1;

                for (int j = i+1; j < [otherCards count]; j++) {
                    id otherCard2 =otherCards[j];
                    if ([otherCard2 isKindOfClass:[PlayingCard class]])
                    {
                        PlayingCard *card2 =(PlayingCard *)otherCard2;
                         // check for the same suit
                        if ([card1.suit isEqualToString:card2.suit]) {
                            score += 1;
                            numMatches++;
                        }
                        // check for the same rank
                        if (card1.rank == card2.rank) {
                            score += 4;
                            numMatches++;
                        }
                    }
                }
            }
        }

        if (numMatches < [otherCards count] -1) score = 0;
    }
    return score;
}
@end
 

Пункт 3

Добавьте private свойство типа Deck * к CardGameViewController.

Импортируйте класс Deck и добавьте свойство Deck.

//  CardGameViewController.m
//  Matchismo

#import "CardGameViewController.h"
#import "Deck.h"

@interface CardGameViewController ()

@property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
@property (nonatomic) int flipCount;
@property (nonatomic, strong) Deck *deck;

@end
Больше ничего не нужно делать в этом пункте задания.

Код доступен на Github.

Пункт 4

Используйте паттерн “отложенное получение экземпляра класса” (lazy instantiation) для размещения (allocate) и инициализации (initialize) этого свойства ( в getter этого свойства) таким образом, чтобы оно стартовало с полной колодой игральных карт типа PlayingCardDeck.

Используем getter для проверки того, инициализировано ли свойство deck. Если нет, то выполним размещение в «куче» и инициализацию …

//  CardGameViewController.m

#import "CardGameViewController.h"
#import "PlayingCardDeck.h"

@interface CardGameViewController ()

@property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
@property (nonatomic) int flipCount;
@property (nonatomic, strong) Deck *deck;

@end

@implementation CardGameViewController

- (Deck *)deck
{
    if (!_deck) {
        _deck = [[PlayingCardDeck alloc] init];
    }
    return _deck;
}
.....

Код доступен на Github.

Пункт 5

До сих пор Matchismo показывало A♣ снова и снова. Усовершенствуйте Matchismo таким образом, чтобы при повороте “лицевой” стороной оно показывало другую случайную карту, вынимаемую  из колоды, описанной выше свойством Deck *. Другими словами, Matchismo должна перевернуть всю колоду игральных карт случайным образом, показывая в каждый момент времени только одну карту.

Так как мы «сдаем» случайные карты, мы не должны стартовать с A♣ (Туза пик), а должны стартовать с карты, перевернутой обратной стороной. Мы могли бы сделать это различными способами, но поскольку это всего лишь первое домашнее задание, то мы выберем самый простой: на storyboard уберем текст с кнопки и установим background image в «cardback«.

Начало игры

Когда карта переворачивается, то в зависимости от того, какую сторону карты («лицевую» или обратную) мы кликали будут производиться разные действия: если это была «лицевая» сторона, то мы просто должны показать теперь обратную сторону, ну, а если это была обратная сторона, то мы должны показать новую карту из колоды. Сторону карты мы будем определять по длине текста заголовка кнопки [sender.currentTitle length], а новую карту из колоды будем получать с помощью метода drawRandomCard :

.  .  .  .  .  .  .  .
- (IBAction)touchCardButton:(UIButton *)sender
{
    if ([sender.currentTitle length]) {
        [sender setBackgroundImage:[UIImage imageNamed:@"cardback"]
                          forState:UIControlStateNormal];
        [sender setTitle:@"" forState:UIControlStateNormal];
    } else {
        Card *card = [self.deck drawRandomCard];
        if (card){
            [sender setBackgroundImage:[UIImage imageNamed:@"cardfront"]
                              forState:UIControlStateNormal];
            [sender setTitle:card.contents forState:UIControlStateNormal];
        }
    }
    self.flipCount++;
}
.  .  .  .  .  .  .  .

Мы подсчитываем перевороты карты в обе стороны: и с обратной на «лицевую», и с «лицевой» на обратную, так что для колоды в 52 карты мы получим 104 переворота, а потом карта не будет переворачиваться на «лицевую» сторону.

Код можно получить на Github

Пункт 6

Не разрушайте существующей функциональности Matchismo (например, метка Flips для расчета переворотов должна продолжать их считать).

… выполнено.

Stanford CS 193P iOS 7 — Домашнее задание 1: 7 комментариев

    • У меня там вроде многоточие стоит и подразумевается, что в посте представлен код неполностью. Вы думаете надо добавить реализацию PlayingCardDeck в текст поста?

  1. Ну я думаю, стоит, по крайней мере, в пункте 2 в конце хотя бы упомянуть. А то я дошла до пункта 4, стала оттуда код переписывать — и смотрю, класса-то нет. Случился небольшой ступор.

    кстати, зачем тут приведена
    -(int)match:(NSArray *)otherCards для класса PlayingCard? Это профессор забежал вперед ?

    • Спасибо за замечание. Постараюсь исправить в ближайшее время.
      По поводу дополнительного кода: вообще-то Домашнее Задание выполняет студент, то есть я, а профессор читает Лекции и код, по-моему, лучше брать сначала с Лекций, а потом самому уже выполнять те пункты, которых нет в Лекции.
      Так что эта строка могла появиться и у меня на перепутье от Задания 1 к Заданию 2, например.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *