首页 > 代码库 > 用swift重写stanford CS193P的纸牌游戏 (2)- PlayingCard,PlayingDeck和ViewCotronller

用swift重写stanford CS193P的纸牌游戏 (2)- PlayingCard,PlayingDeck和ViewCotronller

本系列编号基本对应stanford CS193P的课程编号,可能有一两节课的误差:比如我标(1)就对应Lecture 1,但有时我做得快了就变成(1)对应lecture 1的全部和lecture 2的一部分。

正文:

本文包括三个类,PlayingCard和PlayingDeck,两个单元测试类,和两个单元测试的用到的工具类。

写完之后的感觉:只要写完view controller这个程序就能在iphone上有个用户界面可以点了,刚做出来的时候还是挺有成就感的。在看swift的电子书只看了一两章的时候我就想把这个做出来,但是没写对。

后来在网上一搜发现别人也有在做这个,而且也在问这个怎么写。然后我借鉴他贴的代码把这个写出来了。再后面把书又读了几章,才决定再次重新开始继续看这个CS193P的课程,这次总算能跟上进度了。

 

具体值得记录的点有:

1.每个类都可以选TargetMembership,单元测试类应该只选CardGameTest,待测类应该同时选CardGame和CardGameTest。CardGame是我的应用名字。

 选对之后我就不用一直用默认提供的CardGameTests这个类了,我后面写了两个分开的单元测试类

2.PlayingCard类里,suit和rank做了合法值校验,比如rank不能大于13,suit不能取四种花色以外的值。

  这点在CS193P里,OC下用的做法是重载了这两个变量的setter,我的做法是用了swift的did set功能来校验。

3.PlayingCardTests是我写来测试PlayingCard的。原CS193P的老外是不写单元测试的。但我现在主职业是做测试的,所以我写了单元测试。

思路是一点一点校验PlayingCard类提供的所有东西,包括suit和rank的默认值或最大值,单个变量成功的设置、不成功的设置时的校验、

 4.PlayingCardDeckTests里我通过检查deck里所有元素来检查待测的init方法。

并且在其后做了2次抽牌,抽牌之前先记录当前卡组,做随机抽牌,抽牌之后先用正则表达式证明抽到的牌在抽牌之前在卡组中,再用正则表达式判断被抽出的牌在抽牌之后不再在卡组中。

5.DeckPrintHelper只是用来打印当前卡组中的所有牌和打印随机抽出的牌,为了减少重复代码用。Regex是我从网上找来的swift正则表达式封装好的工具类

代码:

 

首先是ViewController,里面只有一个IBOutlet和一个IBAction

 1 // 2 //  ViewController.swift 3 //  CardGame 4 // 5 //  Created by colin.zt on 14-6-6. 6 //  Copyright (c) 2014 Ting. All rights reserved. 7 // 8  9 import UIKit10 11 class ViewController: UIViewController {12                             13  14     @IBOutlet var flipCount : UILabel15 16     var flipCountNum: Int = 017     override func viewDidLoad() {18         super.viewDidLoad()19         // Do any additional setup after loading the view, typically from a nib.20     }21 22     override func didReceiveMemoryWarning() {23         super.didReceiveMemoryWarning()24         25         // Dispose of any resources that can be recreated.26     }27     28     @IBAction func touchButton(sender : UIButton) {29     //check the card title to know which side is upside30     //then flip it by change the background image and title31         if (sender.currentTitle.isEmpty){32             sender.setTitle("A♣?", forState: UIControlState.Normal)33             sender.setBackgroundImage(UIImage(named:"CardFront"),forState:  UIControlState.Normal)34         } else {35             sender.setTitle("", forState: UIControlState.Normal)36             sender.setBackgroundImage(UIImage(named:"CardBack"),forState:  UIControlState.Normal)37         }38     //increse the flip count number39         flipCountNum++40     //update the flip count on screen41         setFlipCount()42     }43     44     func setFlipCount(){45         // update the flip count by reset the text of the label46         flipCount.text = "Flips:" + String(flipCountNum)47 48     }49     50 51 }

 

然后是PlayingCard类,他是Card类的子类

 1 // 2 //  PlayingCard.swift 3 //  CardGame 4 // 5 //  Created by colin.zt on 14-10-18. 6 //  Copyright (c) 2014 Ting. All rights reserved. 7 // 8  9 import UIKit10 11 class PlayingCard: Card {12    //in did set, do a check and make it can‘t set to invalid value13     var suit: String = "?" {14         didSet {15             //#####could improve or not: is there a simpler way to check if an element is in an array?16             for it in PlayingCard.valid_suits{17                 if it == suit{18                 return19                 }20             }21             suit = oldValue22         }23     }24    //in did set, do a check and make it can‘t set to invalid value25     var rank: Int = 0 {26         didSet {27             if rank > PlayingCard.max_rank{28                 rank = oldValue29             }30 31         }32     }33     34     //all valid ranks in this game,"?" means unset35     class var rank_strings:String[] {36     return ["?","A","2","3","4","5","6","7","8","9","10","J","Q","K"]37     }38     39     //define the max rank40     class var max_rank:Int{41     return rank_strings.count - 142     }43     44     //all valid suits45     class var valid_suits:String[]{46     return ["♥?","♦?","♠?","♣?"]47     }48     49     init() {}50 51     //just pirnt the rank and suit, and rank 11 will be rank "J",and so n52     func contents() -> String{53         return PlayingCard.rank_strings[self.rank]+self.suit54     }55     56     57 }

接着是PlayingCardDeck类,他是Deck类的子类

 

 1 // 2 //  PlayingCardDeck.swift 3 //  CardGame 4 // 5 //  Created by colin.zt on 14-10-20. 6 //  Copyright (c) 2014 Ting. All rights reserved. 7 // 8  9 import UIKit10 11 class PlayingCardDeck: Deck {12     //just init the deck with 52 cards13     init(){14     super.init()15     for suit in PlayingCard.valid_suits{16         for (var rank = 1; rank <= PlayingCard.max_rank; rank++){17             var card: PlayingCard = PlayingCard()18             card.rank = rank19             card.suit = suit20             cards.append(card)21             }22         }23     24     }25     26 }

为了测试这两个类,写了两个单元测试:

PlayingCardTests

 1 // 2 //  PlayingCardTests.swift 3 //  CardGame 4 // 5 //  Created by colin.zt on 14-10-18. 6 //  Copyright (c) 2014 Ting. All rights reserved. 7 // 8  9 import XCTest10 11 class PlayingCardTests: XCTestCase {12 13     override func setUp() {14         super.setUp()15         // Put setup code here. This method is called before the invocation of each test method in the class.16     }17     18     override func tearDown() {19         // Put teardown code here. This method is called after the invocation of each test method in the class.20         super.tearDown()21     }22 23     func testExample() {24         // This is an example of a functional test case.25         var p_card: PlayingCard = PlayingCard()26         27         println(PlayingCard.valid_suits)28         println(PlayingCard.rank_strings)29         println(PlayingCard.max_rank)30         31         32         XCTAssert(PlayingCard.valid_suits == ["♥?","♦?","♠?","♣?"], "Pass")33         XCTAssert(PlayingCard.rank_strings == ["?","A","2","3","4","5","6","7","8","9","10","J","Q","K"], "Pass")34         XCTAssert(PlayingCard.max_rank == 13, "Pass")35         36         XCTAssert(p_card.contents == "??","Pass")37         println(p_card.contents)38        39         p_card.suit = "♥?"40         p_card.rank = 1141         println("this card:\(p_card.contents)")42         XCTAssert(p_card.contents == "J♥?","Pass")43     44         p_card.suit = "" // try to set an invalid value, it will be ignored  because I do the check in didSet of the suit45         println(p_card.contents)46         XCTAssert(p_card.contents == "J♥?","Pass")47        48        49         p_card.suit = "♣?"50         println(p_card.contents)51         XCTAssert(p_card.contents == "J♣?","Pass")52         53         p_card.rank = 3354         println(p_card.contents)55         XCTAssert(p_card.contents == "J♣?","Pass")56        57         p_card.rank = 1358         println(p_card.contents)59         XCTAssert(p_card.contents == "K♣?","Pass")60 61     }62 63     func testPerformanceExample() {64         // This is an example of a performance test case.65         self.measureBlock() {66             // Put the code you want to measure the time of here.67         }68     }69 70 }
View Code

PlayingCardDeckTests

 1 // 2 //  PlayingCardDeckTests.swift 3 //  CardGame 4 // 5 //  Created by colin.zt on 14-10-20. 6 //  Copyright (c) 2014 Ting. All rights reserved. 7 // 8  9 import XCTest10 11 class PlayingCardDeckTests: XCTestCase {12 13     override func setUp() {14         super.setUp()15         // Put setup code here. This method is called before the invocation of each test method in the class.16     }17     18     override func tearDown() {19         // Put teardown code here. This method is called after the invocation of each test method in the class.20         super.tearDown()21     }22 23     func testExample() {24         // This is an example of a functional test case.25       26         //to test the init method, I create a new deck27         var card_deck: PlayingCardDeck = PlayingCardDeck()28        29        // the expected result30         var expected_card_string: NSString = "A♥?,2♥?,3♥?,4♥?,5♥?,6♥?,7♥?,8♥?,9♥?,10♥?,J♥?,Q♥?,K♥?,A♦?,2♦?,3♦?,4♦?,5♦?,6♦?,7♦?,8♦?,9♦?,10♦?,J♦?,Q♦?,K♦?,A♠?,2♠?,3♠?,4♠?,5♠?,6♠?,7♠?,8♠?,9♠?,10♠?,J♠?,Q♠?,K♠?,A♣?,2♣?,3♣?,4♣?,5♣?,6♣?,7♣?,8♣?,9♣?,10♣?,J♣?,Q♣?,K♣?,"31         32         //print and check the deck, if it has all 52 cards33         XCTAssert( DeckPrintHelper.printDeck(card_deck) == expected_card_string,"Pass" )34         35         //check the card number in the deck36         XCTAssert(card_deck.cards.count == 52, "Pass")37         38         //draw a card and check again39         var draw_this_card = DeckPrintHelper.drawRandomCard(card_deck)40        // Regex("K♥?").is_found_in(DeckPrintHelper.printDeck(card_deck))41        // Regex(",").is_found_in(DeckPrintHelper.printDeck(card_deck))42         XCTAssert( Regex(draw_this_card.contents).is_found_in(expected_card_string),"Pass" )43         // use regex to check if still contain this card44        var after_draw_once = DeckPrintHelper.printDeck(card_deck)45         XCTAssert( Regex(draw_this_card.contents).is_found_in(after_draw_once) == false,"Pass" )46         XCTAssert(card_deck.cards.count == 51, "Pass")47     48         //do it again49         draw_this_card = DeckPrintHelper.drawRandomCard(card_deck)50     XCTAssert( Regex(draw_this_card.contents).is_found_in(after_draw_once),"Pass" )51         XCTAssert( Regex(draw_this_card.contents).is_found_in(DeckPrintHelper.printDeck(card_deck)) == false,"Pass" )52         XCTAssert(card_deck.cards.count == 50, "Pass")53 54 55     }56     57     58     59     func testPerformanceExample() {60         // This is an example of a performance test case.61         self.measureBlock() {62             // Put the code you want to measure the time of here.63         }64     }65 66 }
View Code

其中有写打印所有card的方法写在了工具类里

DeckPrintHelper

 1 // 2 //  DeckPrintHelper.swift 3 //  CardGame 4 // 5 //  Created by colin.zt on 14-10-20. 6 //  Copyright (c) 2014 Ting. All rights reserved. 7 // 8  9 //a help class which contains some test logic for reuse10 import UIKit11 class DeckPrintHelper: NSObject {12    13     // to print and return the deck string in my format14     class func printDeck(deck: Deck) -> String{15         var card_string: String = ""16         println("\n_______________________________________")17         println("Now these cards in deck")18         for card in deck.cards{19            card_string += ("\(card.contents),")20         }21         println(card_string)22         println("_______________________________________")23    24     return card_string25     }26     27     // to draw a random card while printing some comments28     class func drawRandomCard(deck: Deck) -> Card{29         var random_card = deck.drawRandomCard()30         println("Now draw this random card:")31         println(random_card.contents)32         return random_card33     }34 }
View Code

还有一个正则表达式的工具类

Regex

 1 // 2 //  Regec.swift 3 //  CardGame 4 // 5 //  Created by colin.zt on 14-10-20. 6 //  Copyright (c) 2014年 Ting. All rights reserved. 7 // 8  9 import Foundation10 11 class Regex: NSObject {12     let internalExpression: NSRegularExpression13     let pattern: String14     15     init(_ pattern: String) {16         self.pattern = pattern17         var error: NSError?18         self.internalExpression = NSRegularExpression(pattern: pattern, options: .CaseInsensitive, error: &error)19     }20     21     func is_found_in(input: String) -> Bool {22         let matches = self.internalExpression.matchesInString(input, options: nil, range:NSMakeRange(0, countElements(input)))23         var count = matches.count24         return matches.count > 025     }26 }27 28 //Usage Example: Regex("\\w{4}").is_found_in("abdddcd")
View Code

 

用swift重写stanford CS193P的纸牌游戏 (2)- PlayingCard,PlayingDeck和ViewCotronller