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:.

[objc highlight=»1,2,6,15,16,18,20,23,25″]

//  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
[/objc]

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

[objc highlight=»1,2,7,15,16,20,21,24,26,34,43,48″]
// 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
[/objc]

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

[objc highlight=»1,2,6,15,16,20,23,29,31,36,43,48,53,58,65″]
// 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
[/objc]

Пункт 3

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

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

[objc highlight=»5,11″]
//  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
[/objc]

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

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

Пункт 4

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

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

[objc highlight=»4,16″]
// 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;
}
…..
[/objc]

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

Пункт 5

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

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

Начало игры

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

[objc highlight=»4,9″]
. . . . . . . .
— (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++;
}
. . . . . . . .
[/objc]

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

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

Пункт 6

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

… выполнено.

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

  1. В этом тексте не приведена реализация PlayingCardDeck. На GitHub она есть.

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

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

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

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

Обсуждение закрыто.