斯坦福iOS 7公開課-Assignment 2

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

老樣子,將Lecture#3裏面的代碼都敲進去,可以實現第三節課白鬍子大叔實現的demo,即隨機匹配一張牌,並算分。github

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可以實現從新開始遊戲。
須要完成的工做:app

  1. 添加一個button來完成這一動做;less

  2. reset屬性值,包括card的chosen, matched以及game的score;iphone

  3. 更新UI.ide

實現也是比較簡單的,由於須要的大部分的函數咱們都有了。函數

  • (IBAction)startNewGame:(id)sender {ui

    self.game = nil;
    [self.game startNewGame];
    [self updateUI];

    }this

這裏須要注意兩個問題。

  1. 關於score的設置。
    因爲score定義的是readonly變量,因此無法在controller裏直接對其賦值,我採用的是新增長一個函數startNewGame在CardMatchingGame.m文件中,另外別忘了在CardMatchingGame.h中聲明哦。

-(void)startNewGame
    {
        self.score = 0;
    }
  1. 從新發牌。
    做爲新的遊戲,桌面上的牌固然是要更換的。這裏用的代碼是:

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-

相關文章
相關標籤/搜索