斯坦福iOS 7公开课-Assignment 2

来源:互联网 发布:少儿编程 教师培训 编辑:程序博客网 时间:2024/04/29 21:42

Task1

Follow the detailed instructions in the lecture slides (separate document) to reproduce the latest version of Matchismo we built in lecture (i.e. the one with multiple cards) and run it in the iPhone Simulator. Do not proceed to the next steps unless your card matching game functions as expected and builds without warnings or errors.

老样子,将Lecture#3里面的代码都敲进去,能够实现第三节课白胡子大叔实现的demo,即随机匹配一张牌,并算分。

Task2

Add a button which will re-deal all of the cards (i.e. start a new game). It should reset the score (and anything else in the UI that makes sense). In a real game, we’d probably want to ask the user if he or she is “sure” before aborting the game in process to re-deal, but for this assignment, you can assume the user always knows what he or she is doing when they hit this button.

任务描述:主要是添加一个button能够实现重新开始游戏。
需要完成的工作:

  1. 添加一个button来完成这一动作;
  2. reset属性值,包括card的chosen, matched以及game的score;
  3. 更新UI.

实现也是比较简单的,因为需要的大部分的函数我们都有了。

- (IBAction)startNewGame:(id)sender {     self.game = nil;    [self.game startNewGame];    [self updateUI];}

这里需要注意两个问题。

  1. 关于score的设置。
    由于score定义的是readonly变量,所以没法在controller里直接对其赋值,我采用的是新增加一个函数startNewGame在CardMatchingGame.m文件中,另外别忘了在CardMatchingGame.h中声明哦。

    -(void)startNewGame{    self.score = 0;}
  2. 重新发牌。
    作为新的游戏,桌面上的牌当然是要更换的。这里用的代码是:

    self.game = nil;

    这样每次再调用game的getter方法的时候就会进入到if语句里然后我们就顺利重开一局啦。

Task3

Drag out a switch (UISwitch) or a segmented control (UISegmentedControl) into your View somewhere which controls whether the game matches two cards at a time or three cards at a time (i.e. it sets “2-card-match mode” vs. “3-card-match mode”). Give the user appropriate points depending on how difficult the match is to accomplish. In 3-card-match mode, it should be possible to get some (although a significantly lesser amount of) points for picking 3 cards of which only 2 match in some way. In that case, all 3 cards should be taken out of the game (even though only 2 match). In 3-card-match mode, choosing only 2 cards is never a match.

任务描述:(要求越来越多了╮(╯▽╰)╭)
增加一个供用户选择游戏模式的开关,可以玩耍匹配三张牌。
需要完成的工作:

  1. 添加一个UISegmentedControl。考虑到后面的题目,可匹配的数目可扩展为n;将可匹配的牌的数目看作该控件的title属性。
  2. 选出可以进行匹配比较的牌,组成新的待比较数组CardsToMatch。显然,该数组的元素个数应该为最大可匹配的牌数-1(即假设现在是3张匹配的模式,那么我们应该在当前桌面上再选两张既被选中又没有被匹配的牌),选好之后就开始进行匹配,调用match方法。
  3. 修改match方法。注意除了当前选中的牌与CardsToMatch中的牌进行外,CardsToMatch中的牌之间也是要进行比较的。

实现步骤如下:

  1. 在CardMatchingGame里新增一个属性值maxMatchingCards,用来控制游戏的模式。

    @property (nonatomic) NSUInteger maxMatchingCards;
  2. 新增UISegmentController控件,通过Ctrl-拖动操作,新增一个outlet和一个action代理,主要为了将UISegmentController控件的title属性传给game作为maxMatchingCards。

    //ViewController.m……@property (weak, nonatomic) IBOutlet UILabel *scoreLabel;- (IBAction)changeModeSelector:(UISegmentedControl *)sender;-(CardMatchingGame *)game{    if (!_game){         _game = [[CardMatchingGame alloc]initWithCardCount:[self.cardButtons count] usingDeck:[self creatDeck]];        [self changeModeSelector:self.modeSelector];    }    return _game;}- (IBAction)changeModeSelector:(UISegmentedControl *)sender{    self.game.maxMatchingCards = [[sender titleForSegmentAtIndex:sender.selectedSegmentIndex]integerValue];}
  3. 修改chooseAtIndex函数。之前是桌面上有一张牌满足待匹配条件(被选中又没有被匹配)即调用match方法,现在将所有满足待匹配条件并且不超过最大可匹配数maxMatchingCards的牌搜集起来组成一个新的数组,再调用match方法。

    //-(void)chooseCardAtIndex:(NSUInteger)index……//match against other chosen card    NSMutableArray *cardsToMatch = [NSMutableArray array];    for (Card *otherCard in self.cards) {        if (otherCard.isChosen && !otherCard.isMatched)            [cardsToMatch addObject:otherCard];    }    if ([cardsToMatch count] +1 == self.maxMatchingCards) {        int matchScore = [card match:cardsToMatch];        if (matchScore) {            self.score += matchScore*MATCH_BONUS;            card.matched = YES;            for (Card *otherCard in cardsToMatch) {                otherCard.matched = YES;            }        }else{            self.score -= MISMATCH_PENALTY;            for (Card *otherCard in cardsToMatch) {                otherCard.chosen = NO;            }        }    }

4.修改match方法。

-(int)match:(NSArray *)otherCards{    int score = 0;    NSUInteger numOtherCards = [otherCards count];    if (numOtherCards) {        for (PlayingCard *otherCard in otherCards) {        if (otherCard.rank == self.rank) {            score += 4;        }else if ([otherCard.suit isEqualToString:self.suit]){            score += 1;        }        //If there are more than 3 cards to be matched, all cards in the otherCards should be matched in pairs.        if (numOtherCards > 1) {            score += [[otherCards firstObject] match:                      [otherCards subarrayWithRange:NSMakeRange(1, numOtherCards - 1)]];        }    }}    return score;}

这里用到了两个新的函数用来创建子数组:

(NSArray *)subarrayWithRange:(NSRange)range;//Returns a new array containing the receiving array’s elements that fall within the limits specified by a given range.NSRange NSMakeRange (NSUInteger loc,NSUInteger len);//Creates a new NSRange from the specified values.

Task4

Disable the game play mode control (i.e. the UISwitch or UISegmentedControl from Required Task 3) when a game starts (i.e. when the first flip of a game happens) and re-enable it when a re-deal happens (i.e. the Deal button is pressed).

任务描述:
开始游戏的时候选取游戏模式的控件不能使用(不然就是作弊嘛),按下Deal按钮的时候再让它可工作。
需要做的工作:

  1. 在touchCardButton里禁用selector;
  2. 新建Deal按钮,创建action,接收到消息的时候启用selector。

实现:

- (IBAction)touchDealButton:(id)sender;……- (IBAction)touchCardButton:(UIButton *)sender{       self.modeSelector.enabled = NO;    ……    }- (IBAction)touchDealButton:(id)sender{    self.modeSelector.enabled = YES;}

Task5

Add a text label somewhere which desribes the results of the last consideration by the CardMatchingGame of a card choice by the user. Examples: “Matched J? J? for 4 points.” or “6? J? don’t match! 2 point penalty!” or “8?” if only one card is chosen or even blank if no cards are chosen. Do not violate MVC by building UI in your Model. “UI” is anything you are going to present to the user. This must work properly in either mode of Required Task 3.

任务描述:
选中某张牌的时候,将选该牌的得分情况显示出来。
需要做的工作:
1. 新建Label控件,用来显示结果;
2. 涉及到两个变量,分数和匹配的牌的contents,注意不要违背MVC模式的精神,所以可在CardMatchingGame里添加两个属性;
3. 在chooseAtIndex里为上述属性赋值;
4. 在updateUI里更新。

实现的步骤:

-(void)chooseCardAtIndex:(NSUInteger)index{……        self.lastScore = 0;    self.lastChosenCards = [cardsToMatch arrayByAddingObject:card];    if ([cardsToMatch count] +1 == self.maxMatchingCards) {        int matchScore = [card match:cardsToMatch];        if (matchScore) {            self.lastScore = matchScore*MATCH_BONUS;            card.matched = YES;            for (Card *otherCard in cardsToMatch) {                 otherCard.matched = YES;            }            }else{                self.lastScore = -MISMATCH_PENALTY;                for (Card *otherCard in cardsToMatch) {                    otherCard.chosen = NO;                }            }        }                    self.score += self.lastScore - COST_TO_CHOOSE;        card.chosen = YES;        }    }}

用lastScore来记录这张牌的得分,lastChosenCards这次进行匹配的所有牌。

-(void)updateUI{……    if (self.game) {    NSString *description = @"";    if ([self.game.lastChosenCards count]) {        NSMutableArray *cardContents = [NSMutableArray array];        for (Card *card in self.game.lastChosenCards) {            [cardContents addObject:card.contents];        }        description = [cardContents componentsJoinedByString:@" "];//(1)    }    if (self.game.lastScore > 0) {        description = [NSString stringWithFormat:@"Matched %@ for %d points.", description, (int)self.game.lastScore];    } else if (self.game.lastScore < 0) {        description = [NSString stringWithFormat:@"%@ don’t match! %d point penalty!", description, (int)self.game.lastScore];    }    self.flipDescription.text = description;}}

UI更新部分,主要参考后面的参考链接,博主是用lastScore的正负来做判断的(好机智!)。
(1)用到一个数组函数,返回一个在数组元素中插入separator作为分隔符的NSString,这里就是在牌的内容之间插入一个空格。

- (NSString *)componentsJoinedByString:(NSString *)separator//Constructs and returns an NSString object that is the result of interposing a given separator between the elements of the array.

Task6

Change the UI of your game to have 30 cards instead of 12. See Hints about the size of cards.

任务描述:
将桌面上的牌扩大到30张……
需要做的工作:
耐心地调整大小吧,记得新建的button要绑定到cardsButton里,可能还要调整title的大小方便显示。

Task7

Do not change the method signature of any public method we went over in lecture. It is fine to change private method signatures (though you should not need to) or to add public and/or private methods.

虽然没有实质性要做的东西,但其实很重要,对写代码思路有指导作用哇。

Hints

3. Think carefully about where the code to generate the strings in Required Task 5 above should go. Is it part of your Model, your View, or your Controller? Some combination thereof? Sometimes the easiest solution is a violation of MVC, but do not violate MVC! Justify your decision in comments in your code. A lot of what this homework assignment is about is your understanding of MVC. This aspect of this assignment is very often missed by students, so give it special attention!
9. Economy is valuable in coding: the easiest way to ensure a bug-free line of code is not to write the line of code at all. But not at the expense of violating MVC! This assignment requires more lines of code than last week, but still not an excessive amount. So if you find yourself writing many dozens of lines of code, you are on the wrong track.

这是两条很重要的Hints,尤其是第三条。
在完成Task5时很容易想到的解决办法是在CardMatchingGame里面添加属性来描述信息,而这恰恰是违背了MVC的原则,所以只能考虑添加某个属性作为“开关”,控制显示我们想要的信息。

效果图


初学者理解不当之处,求指正,谢谢。
参考链接
-EOF-

0 0