├── CardGame ├── en.lproj │ ├── InfoPlist.strings │ └── MainStoryboard.storyboard ├── Default.png ├── set-tab.png ├── cardback.png ├── match-tab.png ├── score-tab.png ├── Default@2x.png ├── settings-tab.png ├── Default-568h@2x.png ├── Model │ ├── SetCardDeck.h │ ├── PlayingCardDeck.h │ ├── SetGame.h │ ├── Card.m │ ├── Deck.h │ ├── Card.h │ ├── SetGame.m │ ├── PlayingCard.h │ ├── FlipResult.h │ ├── PlayingCardDeck.m │ ├── FlipResult.m │ ├── SetCard.h │ ├── Deck.m │ ├── SetCardDeck.m │ ├── CardMatchingGame.h │ ├── GameResult.h │ ├── SetCard.m │ ├── PlayingCard.m │ ├── CardMatchingGame.m │ └── GameResult.m ├── SettingsViewController.h ├── GameResultViewController.h ├── SetGameViewController.h ├── CardGameAppDelegate.h ├── CardGame-Prefix.pch ├── main.m ├── CardGameViewController.h ├── SettingsViewController.m ├── CardGame-Info.plist ├── CardGameAppDelegate.m ├── GameResultViewController.m ├── SetGameViewController.m └── CardGameViewController.m ├── CardGameTests ├── en.lproj │ └── InfoPlist.strings ├── Model │ ├── MockDeck.h │ ├── SetCardTest.h │ ├── SetGameTest.h │ ├── FlipResultTest.h │ ├── GameResultTest.h │ ├── PlayingCardTest.h │ ├── SetCardDeckTest.h │ ├── CardMatchingGameTest.h │ ├── PlayingCardDeckTest.h │ ├── DeckTest.h │ ├── MockUserDefaults.h │ ├── SetGameTest.m │ ├── MockDeck.m │ ├── SetCardDeckTest.m │ ├── PlayingCardDeckTest.m │ ├── FlipResultTest.m │ ├── MockUserDefaults.m │ ├── DeckTest.m │ ├── SetCardTest.m │ ├── PlayingCardTest.m │ ├── GameResultTest.m │ └── CardMatchingGameTest.m └── CardGameTests-Info.plist ├── screenshot-match.png ├── screenshot-set.png ├── screenshot-scores.png ├── screenshot-settings.png ├── readme.md └── CardGame.xcodeproj └── project.pbxproj /CardGame/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /CardGameTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /CardGame/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/CardGame/Default.png -------------------------------------------------------------------------------- /CardGame/set-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/CardGame/set-tab.png -------------------------------------------------------------------------------- /screenshot-match.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/screenshot-match.png -------------------------------------------------------------------------------- /screenshot-set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/screenshot-set.png -------------------------------------------------------------------------------- /CardGame/cardback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/CardGame/cardback.png -------------------------------------------------------------------------------- /CardGame/match-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/CardGame/match-tab.png -------------------------------------------------------------------------------- /CardGame/score-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/CardGame/score-tab.png -------------------------------------------------------------------------------- /screenshot-scores.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/screenshot-scores.png -------------------------------------------------------------------------------- /CardGame/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/CardGame/Default@2x.png -------------------------------------------------------------------------------- /CardGame/settings-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/CardGame/settings-tab.png -------------------------------------------------------------------------------- /screenshot-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/screenshot-settings.png -------------------------------------------------------------------------------- /CardGame/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbj96347/Stanford-W2013-CardGame/HEAD/CardGame/Default-568h@2x.png -------------------------------------------------------------------------------- /CardGame/Model/SetCardDeck.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetCardDeck.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 08.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "Deck.h" 10 | 11 | @interface SetCardDeck : Deck 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGameTests/Model/MockDeck.h: -------------------------------------------------------------------------------- 1 | // 2 | // MockDeck.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 26.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "Deck.h" 10 | 11 | @interface MockDeck : Deck 12 | 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /CardGame/Model/PlayingCardDeck.h: -------------------------------------------------------------------------------- 1 | // 2 | // PlayingCardDeck.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 23.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "Deck.h" 10 | 11 | @interface PlayingCardDeck : Deck 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGameTests/Model/SetCardTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetCardTest.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 07.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SetCardTest : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGameTests/Model/SetGameTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetGameTest.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 08.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SetGameTest : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGame/SettingsViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 05.05.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SettingsViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGameTests/Model/FlipResultTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // FlipResultTest.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 12.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FlipResultTest : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGameTests/Model/GameResultTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // GameResultTest.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 06.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GameResultTest : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGameTests/Model/PlayingCardTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // PlayingCardTest.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 23.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PlayingCardTest : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGameTests/Model/SetCardDeckTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetCardDeckTest.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 08.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SetCardDeckTest : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGame/GameResultViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GameResultViewController.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 07.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GameResultViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGame/SetGameViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetGameViewController.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 08.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "CardGameViewController.h" 10 | 11 | @interface SetGameViewController : CardGameViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGameTests/Model/CardMatchingGameTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardMatchingGameTest.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 25.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CardMatchingGameTest : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGameTests/Model/PlayingCardDeckTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // PlayingCardDeckTest.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 23.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PlayingCardDeckTest : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CardGame/Model/SetGame.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetGame.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 08.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "CardMatchingGame.h" 10 | 11 | @interface SetGame : CardMatchingGame 12 | 13 | - (id) initWithCardCount:(NSUInteger)cardCount; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /CardGameTests/Model/DeckTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // DeckTest.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 21.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DeckTest : SenTestCase 12 | 13 | - (void)testGetCardsForEmptyDeck; 14 | - (void)testAddCard; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /CardGame/CardGameAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardGameAppDelegate.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 21.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CardGameAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /CardGame/CardGame-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'CardGame' target in the 'CardGame' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /CardGame/Model/Card.m: -------------------------------------------------------------------------------- 1 | // 2 | // Card.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 21.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "Card.h" 10 | 11 | @implementation Card 12 | 13 | - (NSString*) contents 14 | { 15 | return nil; // should be defined in subclasses. 16 | } 17 | 18 | - (int) match: (NSArray*) cards 19 | { 20 | return 0; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /CardGame/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 21.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "CardGameAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([CardGameAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CardGame/Model/Deck.h: -------------------------------------------------------------------------------- 1 | // 2 | // Deck.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 21.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "Card.h" 11 | 12 | @interface Deck : NSObject 13 | 14 | @property (nonatomic) NSMutableArray* cards; 15 | 16 | - (void) addCard: (Card*)card atTop:(BOOL)atTop; 17 | - (Card*) drawRandomCard; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /CardGame/Model/Card.h: -------------------------------------------------------------------------------- 1 | // 2 | // Card.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 21.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Card : NSObject 12 | 13 | @property (nonatomic, getter=isFaceUp) BOOL faceUp; 14 | @property (nonatomic, getter=isUnplayable) BOOL unplayable; 15 | - (NSString*) contents; 16 | - (int) match: (NSArray*) cards; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /CardGame/Model/SetGame.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetGame.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 08.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "SetGame.h" 10 | #import "SetCardDeck.h" 11 | 12 | @implementation SetGame 13 | 14 | - (id) initWithCardCount:(NSUInteger)cardCount 15 | { 16 | return self = [super initWithCardCount: cardCount maxCardsToOpen: 3 usingDeck: [[SetCardDeck alloc] init]]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /CardGameTests/Model/MockUserDefaults.h: -------------------------------------------------------------------------------- 1 | // 2 | // MockUserDefaults.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 07.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MockUserDefaults : NSObject 12 | 13 | @property (strong, nonatomic) NSMutableDictionary* storage; 14 | 15 | - (NSDictionary*) dictionaryForKey: (id) key; 16 | - (id) objectForKey: (id) key; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /CardGame/Model/PlayingCard.h: -------------------------------------------------------------------------------- 1 | // 2 | // PlayingCard.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 23.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "Card.h" 10 | 11 | @interface PlayingCard : Card 12 | 13 | + (NSUInteger) maxRank; 14 | + (NSArray*)validSuits; 15 | 16 | - (id) initWithContents: (NSString*)contents; 17 | 18 | @property (nonatomic) NSUInteger rank; 19 | @property (strong, nonatomic) NSString* suit; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /CardGameTests/Model/SetGameTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetGameTest.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 08.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "SetGameTest.h" 10 | #import "SetGame.h" 11 | #import "SetCardDeck.h" 12 | 13 | @implementation SetGameTest 14 | 15 | - (void) testCreation 16 | { 17 | SetGame* set = [[SetGame alloc] initWithCardCount: 24]; 18 | STAssertNotNil(set, @"Set game should be initialized successfully"); 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /CardGameTests/Model/MockDeck.m: -------------------------------------------------------------------------------- 1 | // 2 | // MockDeck.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 26.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "MockDeck.h" 10 | 11 | @implementation MockDeck 12 | 13 | // draws top card from the deck 14 | - (Card*) drawRandomCard 15 | { 16 | Card* card = nil; 17 | 18 | if ([self.cards count]) { 19 | card = [self.cards objectAtIndex: 0]; 20 | [self.cards removeObjectAtIndex: 0]; 21 | } 22 | 23 | return card; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /CardGameTests/Model/SetCardDeckTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetCardDeckTest.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 08.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "SetCardDeckTest.h" 10 | #import "SetCardDeck.h" 11 | 12 | @implementation SetCardDeckTest 13 | 14 | - (void) testDeckCardCount 15 | { 16 | SetCardDeck* deck = [[SetCardDeck alloc] init]; 17 | STAssertEquals([deck.cards count], (NSUInteger) 81, @"Should be 81 cards in the deck for Set"); 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /CardGame/CardGameViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardGameViewController.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 21.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CardMatchingGame.h" 11 | #import "Card.h" 12 | #import "GameResult.h" 13 | 14 | @interface CardGameViewController : UIViewController 15 | 16 | - (CardMatchingGame*) createNewGame; 17 | - (void)updateCardButton:(UIButton *)cardButton forCard:(Card *)card; 18 | - (GameResult*) createGameResult; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /CardGameTests/Model/PlayingCardDeckTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // PlayingCardDeckTest.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 23.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "PlayingCardDeckTest.h" 10 | #import "PlayingCardDeck.h" 11 | 12 | @implementation PlayingCardDeckTest 13 | 14 | - (void) testCardsCount 15 | { 16 | PlayingCardDeck* deck = [[PlayingCardDeck alloc] init]; 17 | STAssertEquals((NSUInteger)52, [deck.cards count], @"In playing card deck must be all 52 cards"); 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /CardGameTests/Model/FlipResultTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // FlipResultTest.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 12.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "FlipResultTest.h" 10 | #import "FlipResult.h" 11 | 12 | @implementation FlipResultTest 13 | 14 | - (void) testCreation 15 | { 16 | FlipResult* none = [FlipResult NoneFlipResult]; 17 | STAssertNotNil(none, @"None flip result should be not nit"); 18 | STAssertEquals(none.type, FLIPRESULT_NONE, @"None flip result type should be correct"); 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /CardGame/SettingsViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 05.05.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "SettingsViewController.h" 10 | #import "GameResult.h" 11 | 12 | @interface SettingsViewController () 13 | 14 | @end 15 | 16 | @implementation SettingsViewController 17 | 18 | - (IBAction)resetAllScores:(id)sender { 19 | [[NSUserDefaults standardUserDefaults] setObject: nil forKey:ALL_RESULTS_KEY]; 20 | [[NSUserDefaults standardUserDefaults] synchronize]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /CardGame/Model/FlipResult.h: -------------------------------------------------------------------------------- 1 | // 2 | // FlipResult.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 12.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define FLIPRESULT_NONE 0 12 | #define FLIPRESULT_FLIPPED_UP 1 13 | #define FLIPRESULT_MATCH 2 14 | #define FLIPRESULT_MISMATCH 3 15 | 16 | @interface FlipResult : NSObject 17 | 18 | @property (nonatomic) int type; 19 | @property (nonatomic) int points; 20 | @property (strong, nonatomic) NSArray* cards; 21 | 22 | + (FlipResult*) NoneFlipResult; 23 | 24 | - (id) initWithType: (int) type cards: (NSArray*) cards points: (int) points; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /CardGame/Model/PlayingCardDeck.m: -------------------------------------------------------------------------------- 1 | // 2 | // PlayingCardDeck.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 23.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "PlayingCardDeck.h" 10 | #import "PlayingCard.h" 11 | 12 | @implementation PlayingCardDeck 13 | 14 | - (id) init 15 | { 16 | if (self = [super init]) { 17 | for(NSString* suit in [PlayingCard validSuits]) { 18 | for(int rank = 1; rank <= [PlayingCard maxRank]; rank++) { 19 | PlayingCard* card = [[PlayingCard alloc] init]; 20 | card.suit = suit; 21 | card.rank = rank; 22 | 23 | [self addCard:card atTop:NO]; 24 | } 25 | } 26 | } 27 | 28 | return self; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /CardGameTests/CardGameTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | livin.edu.stanford.cs193p.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /CardGame/Model/FlipResult.m: -------------------------------------------------------------------------------- 1 | // 2 | // FlipResult.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 12.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "FlipResult.h" 10 | 11 | static FlipResult* NoneFlipResult; 12 | 13 | @implementation FlipResult 14 | 15 | + (FlipResult*) NoneFlipResult 16 | { 17 | if (!NoneFlipResult) 18 | NoneFlipResult = [[FlipResult alloc] initWithType: FLIPRESULT_NONE cards: @[] points:0]; 19 | return NoneFlipResult; 20 | } 21 | 22 | - (id) initWithType: (int) type cards: (NSArray*) cards points: (int) points 23 | { 24 | self = [super init]; 25 | if (self) { 26 | _type = type; 27 | _cards = cards; 28 | _points = points; 29 | } 30 | return self; 31 | } 32 | 33 | - (id) init { 34 | return nil; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /CardGameTests/Model/MockUserDefaults.m: -------------------------------------------------------------------------------- 1 | // 2 | // MockUserDefaults.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 07.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "MockUserDefaults.h" 10 | 11 | @implementation MockUserDefaults 12 | 13 | - (NSMutableDictionary*) storage 14 | { 15 | if (!_storage) 16 | _storage = [[NSMutableDictionary alloc] init]; 17 | return _storage; 18 | } 19 | 20 | - (NSDictionary*) dictionaryForKey: (id) key 21 | { 22 | return [self.storage objectForKey: key]; 23 | } 24 | 25 | - (id) objectForKey: (id) key 26 | { 27 | return [self.storage objectForKey: key]; 28 | } 29 | 30 | - (void) setObject: (id) obj forKey: (id) key 31 | { 32 | [self.storage setObject: obj forKey: key]; 33 | } 34 | 35 | - (void) synchronize 36 | { 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /CardGame/Model/SetCard.h: -------------------------------------------------------------------------------- 1 | // 2 | // SetCard.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 07.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "Card.h" 11 | 12 | #define SYMBOL_CIRLE 0 13 | #define SYMBOL_TRIANGLE 1 14 | #define SYMBOL_SQUARE 2 15 | 16 | #define SETCOLOR_RED 1 17 | #define SETCOLOR_GREEN 2 18 | #define SETCOLOR_BLUE 3 19 | 20 | #define SHADING_OPEN 0 21 | #define SHADING_SOLID 1 22 | #define SHADING_SHADE 2 23 | 24 | @interface SetCard : Card 25 | 26 | + (BOOL) allValuesSameOrDifferent: (NSArray*)threeItems; 27 | 28 | @property (nonatomic) int symbol; 29 | @property (nonatomic) int number; 30 | @property (nonatomic) int color; 31 | @property (nonatomic) int shading; 32 | 33 | - (id) initWithNumber: (int)number symbol: (int)symbol color: (int)color shading: (int)shading; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /CardGame/Model/Deck.m: -------------------------------------------------------------------------------- 1 | // 2 | // Deck.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 21.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "Deck.h" 10 | 11 | @implementation Deck 12 | 13 | - (NSMutableArray *)cards { 14 | if (!_cards) { 15 | _cards = [[NSMutableArray alloc] init]; 16 | } 17 | return _cards; 18 | } 19 | 20 | - (void) addCard: (Card*)card atTop:(BOOL)atTop { 21 | if (atTop) { 22 | [self.cards insertObject:card atIndex:0]; 23 | } else { 24 | [self.cards addObject:card]; 25 | } 26 | } 27 | 28 | - (Card*) drawRandomCard 29 | { 30 | Card* card = nil; 31 | 32 | if (self.cards.count) { 33 | unsigned index = arc4random() % [self.cards count]; 34 | card = self.cards[index]; 35 | [self.cards removeObjectAtIndex:index]; 36 | } 37 | 38 | return card; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /CardGame/Model/SetCardDeck.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetCardDeck.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 08.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "SetCardDeck.h" 10 | #import "SetCard.h" 11 | 12 | @implementation SetCardDeck 13 | 14 | - (id) init 15 | { 16 | self = [super init]; 17 | 18 | if (self) { 19 | for(int number = 1; number <= 3; number++) { 20 | for(int symbol = SYMBOL_CIRLE; symbol <= SYMBOL_SQUARE; symbol++) { 21 | for(int color = SETCOLOR_RED; color <= SETCOLOR_BLUE; color++) { 22 | for(int shading = SHADING_OPEN; shading <= SHADING_SHADE; shading++) { 23 | SetCard* card = [[SetCard alloc] initWithNumber: number symbol: symbol color: color shading: shading]; 24 | [self addCard: card atTop: NO]; 25 | } 26 | } 27 | } 28 | } 29 | } 30 | return self; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /CardGame/Model/CardMatchingGame.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardMatchingGame.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 26.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "Deck.h" 11 | #import "Card.h" 12 | #import "FlipResult.h" 13 | 14 | @interface CardMatchingGame : NSObject 15 | 16 | @property (nonatomic) int matchBonus; 17 | @property (nonatomic) int flipCost; 18 | @property (nonatomic) int mismatchPenalty; 19 | - (id) initWithCardCount:(NSUInteger)cardCount maxCardsToOpen: (NSUInteger)maxCardsToOpen usingDeck:(Deck*) deck; 20 | - (id) initWithCardCount:(NSUInteger)cardCount usingDeck:(Deck*) deck; 21 | - (void) flipCardAtIndex:(NSUInteger)index; 22 | - (Card*) cardAtIndex:(NSUInteger)index; 23 | @property (readonly, nonatomic) int score; 24 | @property (readonly, strong, nonatomic) FlipResult* lastFlipResult; 25 | @property (strong, nonatomic) NSMutableArray* flipHistory; 26 | @property (nonatomic) NSUInteger maxCardsToOpen; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /CardGame/Model/GameResult.h: -------------------------------------------------------------------------------- 1 | // 2 | // GameResult.h 3 | // CardGame 4 | // 5 | // Created by Vladimir on 06.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define ALL_RESULTS_KEY @"GameResult_All" 12 | #define START_KEY @"start" 13 | #define END_KEY @"end" 14 | #define SCORE_KEY @"score" 15 | #define GAMETYPE_KEY @"gametype" 16 | 17 | #define GAMETYPE_MATCH 0 18 | #define GAMETYPE_SET 1 19 | 20 | @interface GameResult : NSObject 21 | 22 | + (NSUserDefaults*) userDefaults; 23 | + (void) setUserDefaults: (NSUserDefaults*)aUserDefaults; 24 | + (NSArray*) allGameResults; 25 | + (NSArray*) gameTypeStrings; 26 | + (NSDateFormatter*) dateFormatter; 27 | 28 | - (id) initFromPropertyList: (id) plist; 29 | @property (strong, nonatomic) NSDate* startTime; 30 | @property (strong, nonatomic) NSDate* endTime; 31 | @property (nonatomic) int score; 32 | @property (nonatomic) int gameType; 33 | - (NSTimeInterval) duration; 34 | - (void) synchronize; 35 | - (NSString*) gameTypeString; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /CardGame/CardGame-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | livin.edu.stanford.cs193p.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | MainStoryboard 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /CardGameTests/Model/DeckTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // DeckTest.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 21.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "DeckTest.h" 10 | #import "Deck.h" 11 | #import "Card.h" 12 | 13 | @implementation DeckTest 14 | 15 | - (void)testGetCardsForEmptyDeck { 16 | Deck* deck = [[Deck alloc] init]; 17 | STAssertEquals((NSUInteger)0, [deck.cards count], @"There should be no cards in empty deck"); 18 | } 19 | 20 | - (void)testAddCard 21 | { 22 | Deck* deck = [[Deck alloc] init]; 23 | Card* card = [[Card alloc] init]; 24 | 25 | [deck addCard: card atTop: YES]; 26 | STAssertEquals((NSUInteger)1, [deck.cards count], @"One card should be here now"); 27 | } 28 | 29 | - (void)testAddCardAtTop 30 | { 31 | Deck* deck = [[Deck alloc] init]; 32 | 33 | Card* cardOne = [[Card alloc] init]; 34 | Card* cardTwo = [[Card alloc] init]; 35 | 36 | [deck addCard: cardOne atTop: NO]; 37 | [deck addCard: cardTwo atTop: YES]; 38 | 39 | STAssertTrue([deck.cards objectAtIndex:0] == cardTwo, @"At the top must be card No. 2"); 40 | } 41 | 42 | - (void) testDrawRandomCard 43 | { 44 | Deck* deck = [[Deck alloc] init]; 45 | 46 | Card* cardOne = [[Card alloc] init]; 47 | Card* cardTwo = [[Card alloc] init]; 48 | 49 | [deck addCard: cardOne atTop: NO]; 50 | [deck addCard: cardTwo atTop: NO]; 51 | 52 | STAssertEquals((NSUInteger)2, [deck.cards count], @"Should be 2 card in the deck at the beginning"); 53 | [deck drawRandomCard]; 54 | STAssertEquals((NSUInteger)1, [deck.cards count], @"After draw random card - only 1 card must be left in the deck"); 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /CardGame/Model/SetCard.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetCard.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 07.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "SetCard.h" 10 | 11 | @implementation SetCard 12 | 13 | + (BOOL) allValuesSameOrDifferent: (NSArray*)ii; 14 | { 15 | BOOL e1 = [ii[0] isEqual: ii[1]]; 16 | BOOL e2 = [ii[1] isEqual: ii[2]]; 17 | BOOL e3 = [ii[0] isEqual: ii[2]]; 18 | return (e1 && e2 && e3) || ((!e1) && (!e2) && (!e3)); 19 | } 20 | 21 | + (NSArray*) stringSymbols { 22 | return @[@"●", @"■", @"▲"]; 23 | } 24 | 25 | - (id) initWithNumber: (int)number symbol: (int)symbol color: (int)color shading: (int)shading 26 | { 27 | self = [super init]; 28 | 29 | if (self) { 30 | _number = number; 31 | _symbol = symbol; 32 | _color = color; 33 | _shading = shading; 34 | } 35 | 36 | return self; 37 | } 38 | 39 | - (NSString*) symbolString 40 | { 41 | return [[self class] stringSymbols][self.symbol]; 42 | } 43 | 44 | - (NSString*) contents 45 | { 46 | NSString* s = @""; 47 | for(int i = 0; i < self.number; i++) { 48 | s = [s stringByAppendingString: [self symbolString]]; 49 | } 50 | return s; 51 | } 52 | 53 | - (int) match:(NSArray *)cards 54 | { 55 | if ([cards count] != 2) 56 | return 0; 57 | 58 | SetCard* c1 = self; 59 | SetCard* c2 = cards[0]; 60 | SetCard* c3 = cards[1]; 61 | 62 | BOOL isSet = 63 | [SetCard allValuesSameOrDifferent: @[@(c1.number), @(c2.number), @(c3.number)]] && 64 | [SetCard allValuesSameOrDifferent: @[@(c1.symbol), @(c2.symbol), @(c3.symbol)]] && 65 | [SetCard allValuesSameOrDifferent: @[@(c1.color), @(c2.color), @(c3.color)]] && 66 | [SetCard allValuesSameOrDifferent: @[@(c1.shading), @(c2.shading), @(c3.shading)]]; 67 | 68 | return isSet? 4 : 0; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /CardGameTests/Model/SetCardTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetCardTest.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 07.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "SetCardTest.h" 10 | #import "SetCard.h" 11 | 12 | @implementation SetCardTest 13 | 14 | - (void) testCreation 15 | { 16 | SetCard* card = [[SetCard alloc] initWithNumber: 1 symbol: SYMBOL_CIRLE color: SETCOLOR_RED shading: SHADING_OPEN]; 17 | STAssertNotNil(card, @"Card should be created successfully"); 18 | } 19 | 20 | - (void) testContents 21 | { 22 | SetCard* card = [[SetCard alloc] initWithNumber:3 symbol: SYMBOL_CIRLE color: SETCOLOR_RED shading: SHADING_OPEN]; 23 | STAssertEqualObjects([card contents], @"●●●", @"Contents of 3 black open circles should be 3 circles string (solid)"); 24 | } 25 | 26 | - (void) testAllValuesSameOrDifferent 27 | { 28 | STAssertTrue(([SetCard allValuesSameOrDifferent: @[@(1), @(2), @(3)]]), @"1, 2, 3 are all different"); 29 | STAssertTrue(([SetCard allValuesSameOrDifferent: @[@(1), @(1), @(1)]]), @"1, 1, 1 are all same"); 30 | STAssertFalse(([SetCard allValuesSameOrDifferent: @[@(1), @(1), @(2)]]), @"1, 1, 2 neither all same nor all different"); 31 | } 32 | 33 | - (void) testMatch 34 | { 35 | SetCard* c1 = [[SetCard alloc] initWithNumber:1 symbol: SYMBOL_CIRLE color: SETCOLOR_RED shading: SHADING_OPEN]; 36 | SetCard* c2 = [[SetCard alloc] initWithNumber:2 symbol: SYMBOL_CIRLE color: SETCOLOR_RED shading: SHADING_OPEN]; 37 | SetCard* c3 = [[SetCard alloc] initWithNumber:3 symbol: SYMBOL_CIRLE color: SETCOLOR_RED shading: SHADING_OPEN]; 38 | STAssertEquals(([c1 match: @[c2, c3]]), 4, @"Same parameters in all cards, but number varies in all cards - it's a Set"); 39 | 40 | c1 = [[SetCard alloc] initWithNumber:1 symbol: SYMBOL_CIRLE color: SETCOLOR_RED shading: SHADING_OPEN]; 41 | c2 = [[SetCard alloc] initWithNumber:2 symbol: SYMBOL_SQUARE color: SETCOLOR_RED shading: SHADING_OPEN]; 42 | c3 = [[SetCard alloc] initWithNumber:3 symbol: SYMBOL_CIRLE color: SETCOLOR_RED shading: SHADING_OPEN]; 43 | STAssertEquals(([c1 match: @[c2, c3]]), 0, @"There are two circles - no Set"); 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /CardGame/CardGameAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // CardGameAppDelegate.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 21.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "CardGameAppDelegate.h" 10 | 11 | @implementation CardGameAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /CardGame/GameResultViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GameResultViewController.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 07.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "GameResultViewController.h" 10 | #import "GameResult.h" 11 | 12 | @interface GameResultViewController () 13 | @property (weak, nonatomic) IBOutlet UITextView *display; 14 | @property (weak, nonatomic) IBOutlet UISegmentedControl *sortSelectorControl; 15 | @end 16 | 17 | @implementation GameResultViewController 18 | 19 | - (void) viewWillAppear:(BOOL)animated 20 | { 21 | [super viewWillAppear: animated]; 22 | [self updateDisplay]; 23 | } 24 | 25 | - (SEL) sortSelector 26 | { 27 | NSUInteger index = self.sortSelectorControl.selectedSegmentIndex; 28 | switch (index) { 29 | case 0: return @selector(compareByDate:); 30 | case 1: return @selector(compareByScore:); 31 | case 2: return @selector(compareByDuration:); 32 | } 33 | return nil; 34 | } 35 | 36 | - (void) updateDisplay 37 | { 38 | NSMutableArray* matchGameResults = [[NSMutableArray alloc] init]; 39 | NSMutableArray* setGameResults = [[NSMutableArray alloc] init]; 40 | 41 | for(GameResult* gameResult in [GameResult allGameResults]) { 42 | if (gameResult.gameType == GAMETYPE_MATCH) { 43 | [matchGameResults addObject: gameResult]; 44 | } else if (gameResult.gameType == GAMETYPE_SET) { 45 | [setGameResults addObject: gameResult]; 46 | } 47 | } 48 | 49 | NSString* matchGameResultsString = [self sortedGameResultsString: matchGameResults header: @"Match Games"]; 50 | NSString* setGameResultsString = [self sortedGameResultsString: setGameResults header: @"Set Games"]; 51 | 52 | self.display.text = [@[matchGameResultsString, setGameResultsString] componentsJoinedByString: @"\n"]; 53 | } 54 | 55 | - (NSString*) sortedGameResultsString: (NSArray*) unsortedGameResults header: (NSString*) header 56 | { 57 | NSArray* results = [unsortedGameResults sortedArrayUsingSelector:[self sortSelector]]; 58 | NSString* gameLines = [results componentsJoinedByString: @"\n"]; 59 | 60 | NSString* result = [[[header stringByAppendingString: @"\n\n"] stringByAppendingString: gameLines] stringByAppendingString: @"\n"]; 61 | 62 | return result; 63 | } 64 | 65 | - (IBAction)sprtSelectionChanged { 66 | [self updateDisplay]; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /CardGame/Model/PlayingCard.m: -------------------------------------------------------------------------------- 1 | // 2 | // PlayingCard.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 23.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "PlayingCard.h" 10 | 11 | @implementation PlayingCard 12 | 13 | @synthesize suit = _suit; 14 | 15 | + (NSArray*)rankStrings 16 | { 17 | return @[@"?", @"A", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10", @"J", @"Q", @"K"]; 18 | } 19 | 20 | + (NSUInteger) maxRank { 21 | return [[self rankStrings] count] - 1; 22 | } 23 | 24 | + (NSArray*)validSuits 25 | { 26 | return @[@"♥", @"♦", @"♠", @"♣"]; 27 | } 28 | 29 | - (id) initWithContents: (NSString*)contents 30 | { 31 | self = [self init]; 32 | 33 | if (self) { 34 | NSUInteger length = [contents length]; 35 | if (!(length == 2 || length == 3)) { 36 | return nil; 37 | } 38 | 39 | NSString* rankString = [contents substringWithRange:NSMakeRange(0, length-1)]; // rank is all first chars minus last one 40 | NSString* suit = [contents substringWithRange:NSMakeRange(length-1, 1)]; // suit is last char 41 | self.rank = [[PlayingCard rankStrings] indexOfObject:rankString]; 42 | self.suit = suit; 43 | } 44 | 45 | return self; 46 | } 47 | 48 | - (NSString*) suit 49 | { 50 | return _suit?_suit:@"?"; 51 | } 52 | 53 | - (void) setSuit:(NSString*) aSuit 54 | { 55 | if ([[PlayingCard validSuits] containsObject:aSuit]) { 56 | _suit = aSuit; 57 | } 58 | } 59 | 60 | - (void) setRank:(NSUInteger)rank 61 | { 62 | if (rank <= [PlayingCard maxRank]) { 63 | _rank = rank; 64 | } 65 | } 66 | 67 | - (NSString*) contents 68 | { 69 | NSString* rankString = [PlayingCard rankStrings][self.rank]; 70 | return [rankString stringByAppendingString: self.suit]; 71 | } 72 | 73 | 74 | - (int) match: (NSArray*) cards 75 | { 76 | int suitsMatch = 0; 77 | int ranksMatch = 0; 78 | 79 | 80 | NSArray* combinations = @[]; 81 | if ([cards count] == 1) { 82 | combinations = @[ @[self, cards[0]] ]; 83 | } else if ([cards count] == 2) { 84 | combinations = @[ @[self, cards[0]], @[cards[0], cards[1]]]; 85 | } 86 | 87 | for(NSArray* twoCards in combinations) { 88 | PlayingCard* first = twoCards[0]; 89 | PlayingCard* second = twoCards[1]; 90 | 91 | if ([first.suit isEqualToString: second.suit]) { 92 | suitsMatch++; 93 | } else if (first.rank == second.rank) { 94 | ranksMatch++; 95 | } 96 | } 97 | 98 | int suitsScore = 5*suitsMatch-4; // Gives 1 for 1 suitsMatch, 6 for 2 suitsMatch 99 | int ranksScore = 6*ranksMatch-2; // Gives 4 for 1 ranksMatch, 10 for 2 ranksMatch 100 | 101 | return MAX(0, MAX(suitsScore, ranksScore)); 102 | } 103 | 104 | - (BOOL) isEqual:(id)object 105 | { 106 | if ([object isKindOfClass: [PlayingCard class]]) { 107 | PlayingCard* another = (PlayingCard*)object; 108 | return (self.rank == another.rank) && ([self.suit isEqualToString: another.suit]); 109 | } 110 | return NO; 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /CardGame/SetGameViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SetGameViewController.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 08.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "SetGameViewController.h" 10 | #import "SetGame.h" 11 | #import "SetCard.h" 12 | 13 | @interface SetGameViewController () 14 | 15 | @end 16 | 17 | @implementation SetGameViewController 18 | 19 | - (void) updateCardButton:(UIButton *)cardButton forCard:(Card *)card 20 | { 21 | cardButton.backgroundColor = card.isFaceUp?[UIColor lightGrayColor]: [UIColor whiteColor]; 22 | cardButton.selected = card.isFaceUp; 23 | cardButton.enabled = !card.isUnplayable; 24 | cardButton.alpha = card.isUnplayable ? 0.0 : 1.0; 25 | 26 | NSMutableAttributedString* a = 27 | [[NSMutableAttributedString alloc] 28 | initWithString:[card contents] 29 | attributes: [self attributesForCard: card]]; 30 | 31 | [cardButton setAttributedTitle: a forState: UIControlStateNormal]; 32 | [cardButton setAttributedTitle: a forState: UIControlStateSelected]; 33 | [cardButton setAttributedTitle: a forState: UIControlStateSelected|UIControlStateDisabled]; 34 | } 35 | 36 | - (NSDictionary*) attributesForCard: (Card*) card 37 | { 38 | SetCard* setCard = (SetCard*)card; 39 | 40 | // coloring 41 | UIColor* cardColor = @[[UIColor blackColor], [UIColor redColor], [UIColor greenColor], [UIColor blueColor]][setCard.color]; 42 | 43 | NSMutableParagraphStyle * paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 44 | [paragraphStyle setAlignment: NSTextAlignmentCenter]; 45 | 46 | NSNumber* strokeWidth = @(0); 47 | UIColor* strokeColor = [UIColor clearColor]; 48 | UIColor* foregroundColor = cardColor; 49 | 50 | if (setCard.shading == SHADING_OPEN) { 51 | strokeWidth = @(-10.0); 52 | strokeColor = cardColor; 53 | foregroundColor = [UIColor clearColor]; 54 | } else if (setCard.shading == SHADING_SHADE) { 55 | strokeWidth = @(-10.0); 56 | strokeColor = cardColor; 57 | foregroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent: 0.3]; 58 | } 59 | 60 | NSDictionary* attributes = 61 | @{ NSForegroundColorAttributeName: foregroundColor, 62 | NSParagraphStyleAttributeName: paragraphStyle, 63 | NSStrokeWidthAttributeName: strokeWidth, 64 | NSStrokeColorAttributeName: strokeColor 65 | }; 66 | 67 | return attributes; 68 | } 69 | 70 | - (NSAttributedString*) cardString:(Card*) card 71 | { 72 | NSMutableAttributedString* s = [[NSMutableAttributedString alloc] initWithString: [card contents] attributes: [self attributesForCard: card]]; 73 | 74 | return s; 75 | } 76 | 77 | - (CardMatchingGame*) createNewGame 78 | { 79 | return [[SetGame alloc] initWithCardCount: 24]; 80 | } 81 | 82 | - (GameResult*) createGameResult 83 | { 84 | GameResult* gameResult = [[GameResult alloc] init]; 85 | gameResult.gameType = GAMETYPE_SET; 86 | return gameResult; 87 | } 88 | 89 | 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Matchismo Card Game 2 | =================== 3 | 4 | This is my version of the Matchismo Card Game of the Standford University CS193P course. The project is driven by requirements defined in course assignments. One of the key feature of my version is that it's driven by tests from the beginning rather than simply writing code. 5 | 6 | Here is also other major features of this implementation. 7 | 8 | ## Features 9 | - fully test-driven model logic (each time something is being implemented the test written first) 10 | - granular commits - step by step addition of each little feature 11 | - easy navigation through significant updates with useful tags (e.g. lecture2-hw, lecture3-slides) 12 | - the match logic is universal for n-number of cards (2,3-card game is just an example) and still simple using simple formula (yes - math is good) 13 | 14 | ## Screenshots 15 | 16 | Card Match Game 17 | 18 | ![Card Match Game Screenshot](screenshot-match.png) 19 | 20 | Set Game 21 | 22 | ![Card Match Game Screenshot](screenshot-set.png) 23 | 24 | Game Scores 25 | 26 | ![Game Scores Screenshot](screenshot-scores.png) 27 | 28 | Settings 29 | 30 | ![Settings Screenshot](screenshot-settings.png) 31 | 32 | ## Release Notes 33 | 34 | ### Assignment 2 with Extra Credits Release: 5 May 2013 35 | This includes full Assignment 2 implementation with all extra credits added: 36 | - Beautiful icons for all tabs: Match, Set, Scores and Settings. 37 | - Game Scores for Match and Set Games 38 | - Settings tab which now allows to reset all score history. 39 | 40 | ### Assignment 2 Release: 27 April 2013 41 | Main feature of this release is a Set Card Game support including following features: 42 | 43 | - Full Set Card Game logic 44 | - The set card and last flip result displayed using attributes of the NSAttributedString 45 | - In a set game cards disappear when set picked to reduce distraction 46 | - We use two views, but share base logic of Match game using inheritance of the set card game controller from the base (match) card game controller. 47 | 48 | ### Assignment 1 + Extra Credit Release: 1 April 2013 49 | Fully-working Matchismo Assignment 1 implemented with all required tasks and extra credit, including: 50 | 51 | - Game play with 16 cards 52 | - Description of the last flip result, e.g. cards match or just flip of the card. 53 | - Easily start new game with "Deal" button 54 | - Selection of either 2-card matching or 3-card matching game. 55 | - Nice card design with custom image as back of the card. 56 | - **Extra Credit** Flip history with UI to slide through past flips 57 | 58 | ### Lecture 5 - Slides Game Score model & tab: 7 April 2013 59 | - Implemented GameResult class with logic for storing game results and synchronizing it to NSUserDefaults. 60 | - Added tabbed interface to switch between Game and Scores. 61 | - Scores can be sorted by date, score and duration. 62 | 63 | Notice: The Assignment 2 starts with notice that we should use our Assignment 1 code as base. So the question is do we really need to start from Assignment 1 and throw temporarily our Lecture 5: Game Score Implementation or keep our Game Score Tab? Since the Assignment 2 has Game Score Tab as Extra Credit Task 2 we will keep this functionality and start Assignment 2 with it rather than from the Assignment 1 code (which doesn't have Scores Tab). -------------------------------------------------------------------------------- /CardGame/Model/CardMatchingGame.m: -------------------------------------------------------------------------------- 1 | // 2 | // CardMatchingGame.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 26.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "CardMatchingGame.h" 10 | 11 | @interface CardMatchingGame() 12 | @property (strong, nonatomic) NSMutableArray* cards; 13 | @property (readwrite, nonatomic) int score; 14 | @property (readwrite, strong, nonatomic) FlipResult* lastFlipResult; 15 | @end 16 | 17 | @implementation CardMatchingGame 18 | 19 | @synthesize lastFlipResult = _lastFlipResult; 20 | 21 | - (id) init 22 | { 23 | return nil; 24 | } 25 | 26 | - (id) initWithCardCount:(NSUInteger)cardCount maxCardsToOpen: (NSUInteger)maxCardsToOpen usingDeck:(Deck*) deck 27 | { 28 | self = [super init]; 29 | 30 | if (self) { 31 | self.matchBonus = 4; 32 | self.flipCost = 1; 33 | self.mismatchPenalty = 2; 34 | 35 | self.maxCardsToOpen = maxCardsToOpen; 36 | 37 | for(int i = 0; i < cardCount; i++) { 38 | [self.cards addObject:[deck drawRandomCard]]; 39 | } 40 | } 41 | 42 | return self; 43 | } 44 | 45 | - (id) initWithCardCount:(NSUInteger)cardCount usingDeck:(Deck*) deck 46 | { 47 | return [self initWithCardCount: cardCount maxCardsToOpen: 2 usingDeck: deck]; 48 | } 49 | 50 | - (NSMutableArray*) cards { 51 | if (!_cards) 52 | _cards = [[NSMutableArray alloc] init]; 53 | return _cards; 54 | } 55 | 56 | - (FlipResult*) lastFlipResult 57 | { 58 | return _lastFlipResult?_lastFlipResult:[FlipResult NoneFlipResult]; 59 | } 60 | 61 | - (void) setLastFlipResult:(FlipResult*)lastFlipResult 62 | { 63 | _lastFlipResult = lastFlipResult; 64 | [self.flipHistory addObject: lastFlipResult]; 65 | } 66 | 67 | - (NSMutableArray*) flipHistory 68 | { 69 | if (!_flipHistory) 70 | _flipHistory = [[NSMutableArray alloc] init]; 71 | return _flipHistory; 72 | } 73 | 74 | - (Card*) cardAtIndex:(NSUInteger)index 75 | { 76 | return index < [self.cards count] ? self.cards[index] : nil; 77 | } 78 | 79 | - (void) flipCardAtIndex:(NSUInteger)index 80 | { 81 | Card* card = [self cardAtIndex: index]; 82 | if (!card.isUnplayable) { 83 | if (!card.isFaceUp) { 84 | NSArray* faceUpCards = [self faceUpCards]; 85 | if ([faceUpCards count] + 1 == self.maxCardsToOpen) { 86 | int matchScore = [card match: faceUpCards]; 87 | 88 | NSMutableArray* allCards = [[NSMutableArray alloc] init]; 89 | [allCards addObject: card]; 90 | [allCards addObjectsFromArray: faceUpCards]; 91 | 92 | if (matchScore) { 93 | int matchScoreWithBonus = matchScore * self.matchBonus; 94 | self.score += matchScore * self.matchBonus; 95 | self.lastFlipResult = [[FlipResult alloc] initWithType: FLIPRESULT_MATCH cards: allCards points: matchScoreWithBonus]; 96 | } else { 97 | self.score -= self.mismatchPenalty; 98 | self.lastFlipResult = [[FlipResult alloc] initWithType: FLIPRESULT_MISMATCH cards: allCards points: self.mismatchPenalty]; 99 | } 100 | 101 | card.unplayable = YES; 102 | for(Card* card in faceUpCards) { 103 | card.unplayable = YES; 104 | } 105 | } else { 106 | self.lastFlipResult = [[FlipResult alloc] initWithType:FLIPRESULT_FLIPPED_UP cards: @[card] points: 0]; 107 | } 108 | 109 | self.score -= self.flipCost; 110 | } 111 | card.faceUp = !card.isFaceUp; 112 | } 113 | } 114 | 115 | - (NSArray*) faceUpCards 116 | { 117 | NSMutableArray* facedUpCards = [[NSMutableArray alloc] init]; 118 | for(Card* card in self.cards) { 119 | if (card.isFaceUp && (!card.isUnplayable)) { 120 | [facedUpCards addObject: card]; 121 | } 122 | } 123 | return facedUpCards; 124 | } 125 | 126 | 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /CardGameTests/Model/PlayingCardTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // PlayingCardTest.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 23.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "PlayingCardTest.h" 10 | #import "PlayingCard.h" 11 | 12 | @implementation PlayingCardTest 13 | 14 | - (void) testCreation 15 | { 16 | PlayingCard* card = [[PlayingCard alloc] init]; 17 | card.rank = 0; 18 | STAssertEqualObjects([card contents], @"??", @"Card with zero rank and no suit should be undefined card"); 19 | 20 | card = [[PlayingCard alloc] init]; 21 | card.rank = 1; 22 | card.suit = @"♦"; 23 | STAssertEqualObjects([card contents], @"A♦", @"Card with rank 1 should be Ace"); 24 | 25 | card = [[PlayingCard alloc] init]; 26 | card.rank = 12; 27 | card.suit = @"♥"; 28 | STAssertEqualObjects([card contents], @"Q♥", @"Card with rank 12 should be Queen"); 29 | 30 | card = [[PlayingCard alloc] init]; 31 | card.rank = 2; 32 | card.suit = @"x"; 33 | STAssertEqualObjects([card contents], @"2?", @"The invalid suit should not be set"); 34 | 35 | card = [[PlayingCard alloc] init]; 36 | card.rank = 14; 37 | STAssertEqualObjects([card contents], @"??", @"Rank should not be set to improper value"); 38 | } 39 | 40 | - (void) testCreationWithContents 41 | { 42 | PlayingCard* card = [[PlayingCard alloc] initWithContents: @"A♦"]; 43 | STAssertEqualObjects(@"A♦", [card contents], @"Initialized card should be A♦"); 44 | 45 | card = [[PlayingCard alloc] initWithContents: @"10♦"]; 46 | STAssertEqualObjects(@"10♦", [card contents], @"Initialized card should be 10♦"); 47 | } 48 | 49 | - (void) testMaxRank 50 | { 51 | STAssertEquals((NSUInteger)13, [PlayingCard maxRank], @"Max rank of the playing card should be 13"); 52 | } 53 | 54 | - (void) testMatchSuits 55 | { 56 | PlayingCard* c1 = [[PlayingCard alloc] initWithContents: @"2♦"]; 57 | PlayingCard* c2 = [[PlayingCard alloc] initWithContents: @"A♦"]; 58 | 59 | int matchScore = [c1 match: @[c2]]; 60 | STAssertEquals(matchScore, 1, @"Two suits must match by score 1"); 61 | } 62 | 63 | - (void) testMatchRanks 64 | { 65 | PlayingCard* c1 = [[PlayingCard alloc] initWithContents: @"2♦"]; 66 | PlayingCard* c2 = [[PlayingCard alloc] initWithContents: @"2♥"]; 67 | 68 | int matchScore = [c1 match: @[c2]]; 69 | STAssertEquals(matchScore, 4, @"Two ranks must match by score 4"); 70 | } 71 | 72 | - (void) testMatchSuitsIn3Cards 73 | { 74 | PlayingCard* c1 = [[PlayingCard alloc] initWithContents: @"2♦"]; 75 | PlayingCard* c2 = [[PlayingCard alloc] initWithContents: @"A♦"]; 76 | PlayingCard* c3 = [[PlayingCard alloc] initWithContents: @"J♥"]; 77 | 78 | int matchScore = [c1 match: @[c2, c3]]; 79 | STAssertEquals(matchScore, 1, @"Two suits in 3 cards must match by score 1"); 80 | 81 | matchScore = [c3 match: @[c1, c2]]; 82 | STAssertEquals(matchScore, 1, @"There should be match, even if original card doesn't matching anything"); 83 | 84 | c3 = [[PlayingCard alloc] initWithContents: @"J♦"]; 85 | 86 | matchScore = [c1 match: @[c2, c3]]; 87 | STAssertEquals(matchScore, 6, @"Three suits in 3 cards must match by score 6"); 88 | } 89 | 90 | - (void) testMatchRanksIn3Cards 91 | { 92 | PlayingCard* c1 = [[PlayingCard alloc] initWithContents: @"2♦"]; 93 | PlayingCard* c2 = [[PlayingCard alloc] initWithContents: @"2♥"]; 94 | PlayingCard* c3 = [[PlayingCard alloc] initWithContents: @"J♥"]; 95 | 96 | int matchScore = [c1 match: @[c2, c3]]; 97 | STAssertEquals(matchScore, 4, @"Two ranks in 3 cards must match by score 4"); 98 | 99 | c3 = [[PlayingCard alloc] initWithContents: @"2♣"]; 100 | matchScore = [c1 match: @[c2, c3]]; 101 | STAssertEquals(matchScore, 10, @"Three ranks in 3 cards must match by score 10"); 102 | } 103 | 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /CardGame/Model/GameResult.m: -------------------------------------------------------------------------------- 1 | // 2 | // GameResult.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 06.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "GameResult.h" 10 | 11 | static NSDateFormatter* dateFormatter; 12 | static NSUserDefaults* userDefaults; 13 | 14 | @implementation GameResult 15 | 16 | + (NSUserDefaults*) userDefaults 17 | { 18 | return userDefaults?userDefaults:[NSUserDefaults standardUserDefaults]; 19 | } 20 | 21 | + (void) setUserDefaults: (NSUserDefaults*)aUserDefaults 22 | { 23 | userDefaults = aUserDefaults; 24 | } 25 | 26 | + (NSArray*) allGameResults 27 | { 28 | NSMutableArray* gameResults = [[NSMutableArray alloc] init]; 29 | 30 | NSDictionary* allGameResultsDictionary = [[[self class] userDefaults] dictionaryForKey: ALL_RESULTS_KEY]; 31 | for(id plist in [allGameResultsDictionary allValues]) { 32 | GameResult* gr = [[GameResult alloc] initFromPropertyList: plist]; 33 | [gameResults addObject: gr]; 34 | } 35 | 36 | return gameResults; 37 | } 38 | 39 | + (NSArray*) gameTypeStrings 40 | { 41 | return @[@"Match", @"Set"]; 42 | } 43 | 44 | + (NSDateFormatter*) dateFormatter 45 | { 46 | if (!dateFormatter) { 47 | dateFormatter = [[NSDateFormatter alloc] init]; 48 | [dateFormatter setDateFormat:@"M/d/yy h:mma"]; 49 | } 50 | return dateFormatter; 51 | } 52 | 53 | - (id) init 54 | { 55 | self = [super init]; 56 | if (self) { 57 | _startTime = [NSDate date]; 58 | _endTime = _startTime; 59 | _score = 0; 60 | } 61 | return self; 62 | } 63 | 64 | - (id) initFromPropertyList: (id) plist 65 | { 66 | self = [self init]; 67 | if (self) { 68 | if ([plist isKindOfClass: [NSDictionary class]]) { 69 | NSDictionary* dictionary = (NSDictionary*) plist; 70 | _startTime = dictionary[START_KEY]; 71 | _endTime = dictionary[END_KEY]; 72 | _score = [((NSNumber*)dictionary[SCORE_KEY]) intValue]; 73 | _gameType = [((NSNumber*)dictionary[GAMETYPE_KEY]) intValue]; 74 | 75 | if (!_startTime || !_endTime) 76 | self = nil; 77 | } 78 | } 79 | return self; 80 | } 81 | 82 | - (void) setScore:(int)score 83 | { 84 | _score = score; 85 | self.endTime = [NSDate date]; 86 | [self synchronize]; 87 | } 88 | 89 | - (NSTimeInterval) duration 90 | { 91 | return [self.endTime timeIntervalSinceDate: self.startTime]; 92 | } 93 | 94 | - (NSString*) description 95 | { 96 | NSString* startTimeString = [[[self class] dateFormatter] stringFromDate: self.startTime]; 97 | return [NSString stringWithFormat: @"%@ Score: %d (%@ %.0fs)", [self gameTypeString], self.score, startTimeString, [self duration]]; 98 | } 99 | 100 | - (void) synchronize 101 | { 102 | NSUserDefaults* defaults = [[self class] userDefaults]; 103 | NSMutableDictionary* allResults = [[defaults dictionaryForKey: ALL_RESULTS_KEY] mutableCopy]; 104 | 105 | if (!allResults) 106 | allResults = [[NSMutableDictionary alloc] init]; 107 | 108 | allResults[[self.startTime description]] = [self asPropertyList]; 109 | [defaults setObject: allResults forKey: ALL_RESULTS_KEY]; 110 | [defaults synchronize]; 111 | } 112 | 113 | - (NSDictionary*) asPropertyList 114 | { 115 | return @{START_KEY: self.startTime, END_KEY: self.endTime, SCORE_KEY: @(self.score), GAMETYPE_KEY: @(self.gameType)}; 116 | } 117 | 118 | - (NSComparisonResult) compareByDate: (GameResult*) another 119 | { 120 | return [self.startTime compare: another.startTime]; 121 | } 122 | 123 | - (NSComparisonResult) compareByScore: (GameResult*) another 124 | { 125 | return [@(another.score) compare: @(self.score)]; 126 | } 127 | 128 | - (NSComparisonResult) compareByDuration: (GameResult*) another 129 | { 130 | return [@([self duration]) compare: @([another duration])]; 131 | } 132 | 133 | - (NSString*) gameTypeString 134 | { 135 | return [[self class] gameTypeStrings][[self gameType]]; 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /CardGameTests/Model/GameResultTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // GameResultTest.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 06.04.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "GameResultTest.h" 10 | #import "GameResult.h" 11 | #import "MockUserDefaults.h" 12 | 13 | @interface GameResultTest() 14 | @property (strong, nonatomic) NSUserDefaults* userDefaults; 15 | @end 16 | 17 | @implementation GameResultTest 18 | 19 | 20 | - (NSDate*) sampleDate100pm 21 | { 22 | return [[GameResult dateFormatter] dateFromString: @"4/6/13 1:00PM"]; 23 | } 24 | 25 | - (NSDate*) sampleDate105pm 26 | { 27 | return [[GameResult dateFormatter] dateFromString: @"4/6/13 1:05PM"]; 28 | } 29 | 30 | - (GameResult*) sampleGameResultWith15Scores 31 | { 32 | GameResult* gr = [[GameResult alloc] init]; 33 | gr.score = 15; 34 | 35 | // we set dates manually for this test 36 | gr.startTime = [self sampleDate100pm]; 37 | gr.endTime = [self sampleDate105pm]; 38 | 39 | return gr; 40 | } 41 | 42 | - (void) setUp 43 | { 44 | [super setUp]; 45 | self.userDefaults = (NSUserDefaults*) [[MockUserDefaults alloc] init]; 46 | [GameResult setUserDefaults: self.userDefaults]; 47 | } 48 | 49 | - (void) testCreate 50 | { 51 | GameResult* gr = [[GameResult alloc] init]; 52 | STAssertNotNil(gr.startTime, @"When game is created the start time must be set"); 53 | } 54 | 55 | - (void) testUpdateScore 56 | { 57 | GameResult* gr = [[GameResult alloc] init]; 58 | 59 | NSDate* firstEndTime = gr.endTime; 60 | 61 | gr.score += 5; 62 | STAssertEquals(gr.score, 5, @"Score at this point should be updated to 5"); 63 | 64 | NSDate* endTimeAfterUpdatingScore = gr.endTime; 65 | STAssertTrue([firstEndTime compare: endTimeAfterUpdatingScore], @"After setting new score the end must be also updated"); 66 | } 67 | 68 | - (void) testDuration 69 | { 70 | GameResult* gr = [[GameResult alloc] init]; 71 | gr.score += 1; 72 | 73 | NSTimeInterval duration = [gr duration]; 74 | NSLog(@"Duration: %f", duration); 75 | } 76 | 77 | - (void) testDateFormatter 78 | { 79 | NSDateFormatter* dateFormatter = [GameResult dateFormatter]; 80 | NSDate* date = [dateFormatter dateFromString: @"4/6/13 2:30PM"]; 81 | NSDateComponents* cc = [[NSCalendar currentCalendar] components: NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:date]; 82 | STAssertEquals(cc.year, 2013, @"Year"); 83 | STAssertEquals(cc.month, 4, @"Month"); 84 | STAssertEquals(cc.day, 6, @"Day"); 85 | STAssertEquals(cc.hour, 14, @"Hour"); 86 | STAssertEquals(cc.minute, 30, @"Minute"); 87 | } 88 | 89 | - (void) testDescription 90 | { 91 | GameResult* gr = [self sampleGameResultWith15Scores]; 92 | STAssertEqualObjects([gr description], @"Match Score: 15 (4/6/13 1:00PM 300s)", @"Game description should be 15 scores started on 4 April 2013 and took 600 secs (5 mins)"); 93 | } 94 | 95 | - (void) testSyncronize 96 | { 97 | GameResult* gr = [self sampleGameResultWith15Scores]; 98 | [gr synchronize]; 99 | 100 | NSUserDefaults* defaults = self.userDefaults; 101 | NSDictionary* allResults = [defaults dictionaryForKey: ALL_RESULTS_KEY]; 102 | STAssertNotNil(allResults, @"We should have the results now after syncing"); 103 | 104 | NSDictionary* ourGameResult = allResults[[gr.startTime description]]; 105 | STAssertNotNil(ourGameResult, @"We should have the entry for our game result"); 106 | 107 | ourGameResult = allResults[[gr.startTime description]]; 108 | STAssertEqualObjects(ourGameResult[START_KEY], gr.startTime, @"The start time saved should match game's start time"); 109 | STAssertEqualObjects(ourGameResult[END_KEY], gr.endTime, @"The end time saved should match game's end time"); 110 | } 111 | 112 | - (void) testInitFromPropertyList 113 | { 114 | NSDictionary* plist = @{START_KEY: [self sampleDate100pm], END_KEY: [self sampleDate105pm], SCORE_KEY: @(15), GAMETYPE_KEY: @(GAMETYPE_SET)}; 115 | 116 | GameResult* gr = [[GameResult alloc] initFromPropertyList: plist]; 117 | STAssertEqualObjects(gr.startTime, [self sampleDate100pm], @"Start Time shold be sample 1:00PM time"); 118 | STAssertEqualObjects(gr.endTime, [self sampleDate105pm], @"End Time should be sample 1:05PM time"); 119 | STAssertEquals(gr.score, 15, @"Score should be 15"); 120 | STAssertEquals(gr.gameType, GAMETYPE_SET, @"Game type should be GAMETYPE_SET"); 121 | 122 | NSDictionary* broken = @{START_KEY: [self sampleDate100pm], SCORE_KEY: @(15)}; 123 | STAssertNil([[GameResult alloc] initFromPropertyList: broken], @"No game result should be created from broken plist"); 124 | } 125 | 126 | - (void) testGameType 127 | { 128 | GameResult* gr = [self sampleGameResultWith15Scores]; 129 | STAssertEquals(gr.gameType, GAMETYPE_MATCH, @"By default the game type should be GAMETYPE_MATCH"); 130 | 131 | gr.gameType = GAMETYPE_SET; 132 | STAssertEquals(gr.gameType, GAMETYPE_SET, @"Now gameType should be GAMETYPE_SET after setting it through property"); 133 | } 134 | 135 | - (void) testGameTypeString 136 | { 137 | GameResult* gr = [[GameResult alloc] init]; 138 | gr.gameType = GAMETYPE_SET; 139 | STAssertEquals([gr gameTypeString], @"Set", @"Game type string should be Set"); 140 | } 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /CardGameTests/Model/CardMatchingGameTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // CardMatchingGameTest.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 25.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "CardMatchingGameTest.h" 10 | #import "CardMatchingGame.h" 11 | #import "PlayingCardDeck.h" 12 | #import "MockDeck.h" 13 | #import "PlayingCard.h" 14 | #import "FlipResult.h" 15 | 16 | @implementation CardMatchingGameTest 17 | 18 | - (void) testCreation 19 | { 20 | CardMatchingGame* g = [[CardMatchingGame alloc] init]; 21 | STAssertNil(g, @"CardMathingGame should be only initialized with designated initializer"); 22 | 23 | g = [[CardMatchingGame alloc] initWithCardCount:12 usingDeck:[[PlayingCardDeck alloc] init]]; 24 | STAssertNotNil(g, @"Game should be initialized successfully using designated initializer"); 25 | STAssertNotNil([g cardAtIndex: 0], @"Should be a card at index 0"); 26 | STAssertNotNil([g cardAtIndex: 1], @"Should be a card at index 1"); 27 | } 28 | 29 | - (void) testCardAtIndex 30 | { 31 | Deck* deck = [[MockDeck alloc] init]; 32 | 33 | Card* c1 = [[PlayingCard alloc] initWithContents: @"2♦"]; 34 | Card* c2 = [[PlayingCard alloc] initWithContents: @"3♦"]; 35 | 36 | [deck addCard: c1 atTop: NO]; 37 | [deck addCard: c2 atTop: NO]; 38 | 39 | CardMatchingGame* game = [[CardMatchingGame alloc] initWithCardCount: 2 usingDeck: deck]; 40 | 41 | STAssertEquals(c1, [game cardAtIndex: 0], @"Card at index should return first card for index 0"); 42 | STAssertEquals(c2, [game cardAtIndex: 1], @"Card at index should return second card for index 1"); 43 | } 44 | 45 | - (void) testUserCanFlipOnlyPlayableCards 46 | { 47 | Deck* deck = [[MockDeck alloc] init]; 48 | 49 | Card* playableCard = [[PlayingCard alloc] initWithContents: @"A♦"]; 50 | playableCard.faceUp = NO; 51 | playableCard.unplayable = NO; 52 | 53 | Card* unplayableCard = [[PlayingCard alloc] initWithContents: @"3♦"]; 54 | unplayableCard.faceUp = YES; 55 | unplayableCard.unplayable = YES; 56 | 57 | [deck addCard: playableCard atTop: NO]; 58 | [deck addCard: unplayableCard atTop: NO]; 59 | 60 | CardMatchingGame* game = [[CardMatchingGame alloc] initWithCardCount: 2 usingDeck: deck]; 61 | 62 | [game flipCardAtIndex: 1]; 63 | STAssertTrue(unplayableCard.isFaceUp == YES, @"Flipping second unplayable card should do nothing, card shouldn't face down"); 64 | 65 | [game flipCardAtIndex: 0]; 66 | STAssertTrue(playableCard.isFaceUp == YES, @"Flipping first playable card should work, and card should be faced up"); 67 | } 68 | 69 | - (void) testGameScoresWhenTwoCardsOpened 70 | { 71 | Card* ace = [[PlayingCard alloc] initWithContents: @"A♦"]; 72 | Card* three = [[PlayingCard alloc] initWithContents: @"3♦"]; 73 | [self assertFlipCardsGame:@[ace, three] scores: 2 because: @"Score should be 2 (4 for matching 2 suits, -2 for two flips)"]; 74 | } 75 | 76 | - (void) testGameScoresDownWhenCardsMismatch 77 | { 78 | Card* c1 = [[PlayingCard alloc] initWithContents: @"A♦"]; 79 | Card* c2 = [[PlayingCard alloc] initWithContents: @"3♣"]; 80 | [self assertFlipCardsGame:@[c1, c2] scores: -4 because: @"Score should be -4 (-2 for mismatch, -2 for two flips)"]; 81 | } 82 | 83 | - (void) testGame3Cards 84 | { 85 | Card* c1 = [[PlayingCard alloc] initWithContents: @"A♦"]; 86 | Card* c2 = [[PlayingCard alloc] initWithContents: @"3♦"]; 87 | Card* c3 = [[PlayingCard alloc] initWithContents: @"2♦"]; 88 | [self assertFlipCardsGame:@[c1, c2, c3] maxCardsToOpen: 3 scores: 21 because: @"Score should be 21 (24 for match 3 suits, -3 for two flips)"]; 89 | 90 | c1 = [[PlayingCard alloc] initWithContents: @"A♦"]; 91 | c2 = [[PlayingCard alloc] initWithContents: @"3♦"]; 92 | c3 = [[PlayingCard alloc] initWithContents: @"7♣"]; 93 | [self assertFlipCardsGame:@[c1, c2, c3] maxCardsToOpen: 3 scores: 1 because: @"Score should be -2 (4 for match 2 suits, -3 for two flips)"]; 94 | } 95 | 96 | - (void) testLastFlipResultAndFlipHistory 97 | { 98 | Deck* deck = [[MockDeck alloc] init]; 99 | 100 | Card* c1 = [[PlayingCard alloc] initWithContents: @"A♦"]; 101 | Card* c2 = [[PlayingCard alloc] initWithContents: @"3♦"]; 102 | Card* c3 = [[PlayingCard alloc] initWithContents: @"2♣"]; 103 | Card* c4 = [[PlayingCard alloc] initWithContents: @"3♦"]; 104 | 105 | NSArray* cards = @[c1, c2, c3, c4]; 106 | 107 | for(Card* card in cards) { 108 | [deck addCard: card atTop: NO]; 109 | } 110 | 111 | CardMatchingGame* game = [[CardMatchingGame alloc] initWithCardCount: [cards count] usingDeck: deck]; 112 | 113 | [game flipCardAtIndex: 0]; 114 | FlipResult* lastFlipResult = [game lastFlipResult]; 115 | STAssertEquals(lastFlipResult.type, FLIPRESULT_FLIPPED_UP, @"Flipped up"); 116 | STAssertEqualObjects(lastFlipResult.cards, @[c1], @"A♦"); 117 | 118 | [game flipCardAtIndex: 1]; 119 | lastFlipResult = [game lastFlipResult]; 120 | STAssertEquals(lastFlipResult.type, FLIPRESULT_MATCH, @"Matched"); 121 | NSArray* c1c2 = @[c2,c1]; 122 | STAssertEqualObjects(lastFlipResult.cards, c1c2, @"3♦ & A♦"); 123 | STAssertEquals(lastFlipResult.points, 4, @"for 4 points"); 124 | 125 | [game flipCardAtIndex: 2]; 126 | lastFlipResult = [game lastFlipResult]; 127 | FlipResult* thirdFlipResult = lastFlipResult; 128 | STAssertEquals(lastFlipResult.type, FLIPRESULT_FLIPPED_UP, @"Flipped up"); 129 | STAssertEqualObjects(lastFlipResult.cards, @[c3], @"2♣"); 130 | 131 | [game flipCardAtIndex: 3]; 132 | lastFlipResult = [game lastFlipResult]; 133 | STAssertEquals(lastFlipResult.type, FLIPRESULT_MISMATCH, @"Don't match"); 134 | NSArray* c3c4 = @[c4,c3]; 135 | STAssertEqualObjects(lastFlipResult.cards, c3c4, @"3♦ & 2♣"); 136 | STAssertEquals(lastFlipResult.points, 2, @"2 points penalty"); 137 | 138 | STAssertEquals([game.flipHistory count], (NSUInteger)4, @"Game now should have flip history of 4 items"); 139 | STAssertEqualObjects(game.flipHistory[2], thirdFlipResult, @"The third item of flip (index=2) should be 'Flipped up 2♣'"); 140 | } 141 | 142 | - (void) assertFlipCardsGame: (NSArray*) cards scores: (int) score because: (NSString*) description 143 | { 144 | [self assertFlipCardsGame:cards maxCardsToOpen: 2 scores: score because: description]; 145 | } 146 | 147 | - (void) assertFlipCardsGame: (NSArray*) cards maxCardsToOpen: (int) maxCards scores: (int) score because: (NSString*) description 148 | { 149 | Deck* deck = [[MockDeck alloc] init]; 150 | 151 | for(Card* card in cards) { 152 | [deck addCard: card atTop: NO]; 153 | } 154 | 155 | CardMatchingGame* game = [[CardMatchingGame alloc] initWithCardCount: [cards count] maxCardsToOpen: maxCards usingDeck: deck]; 156 | 157 | for(int i = 0; i < [cards count]; i++) { 158 | [game flipCardAtIndex: i]; 159 | } 160 | 161 | STAssertEquals(game.score, score, description); 162 | for(Card* card in cards) { 163 | STAssertTrue(card.isUnplayable, [NSString stringWithFormat: @"Card %@ card should be unplayable", [card contents]]); 164 | } 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /CardGame/CardGameViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // CardGameViewController.m 3 | // CardGame 4 | // 5 | // Created by Vladimir on 21.03.13. 6 | // Copyright (c) 2013 Vladimir. All rights reserved. 7 | // 8 | 9 | #import "CardGameViewController.h" 10 | #import "PlayingCardDeck.h" 11 | #import "GameResult.h" 12 | #import "FlipResult.h" 13 | 14 | @interface CardGameViewController () 15 | 16 | @property (weak, nonatomic) IBOutlet UILabel *flipsLabel; 17 | @property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons; 18 | @property (weak, nonatomic) IBOutlet UILabel *scoreLabel; 19 | @property (weak, nonatomic) IBOutlet UILabel *lastFlipResultLabel; 20 | @property (weak, nonatomic) IBOutlet UISegmentedControl *cardsToOpenControl; 21 | @property (weak, nonatomic) IBOutlet UISlider *historySlider; 22 | @property (nonatomic) int flipCount; 23 | @property (strong, nonatomic) CardMatchingGame* game; 24 | @property (nonatomic) BOOL firstFlipMade; 25 | @property (strong, nonatomic) GameResult* gameResult; 26 | @property (nonatomic) int cardsToOpen; 27 | @end 28 | 29 | @implementation CardGameViewController 30 | 31 | - (void) setFlipCount:(int)flipCount 32 | { 33 | _flipCount = flipCount; 34 | self.flipsLabel.text = [NSString stringWithFormat: @"Flips: %d", self.flipCount]; 35 | NSLog(@"flips updated to %d", self.flipCount); 36 | } 37 | 38 | - (void) setCardButtons:(NSArray *)cardButtons 39 | { 40 | _cardButtons = cardButtons; 41 | [self updateUI]; 42 | } 43 | 44 | + (UIImage*) cardBackImage 45 | { 46 | return [UIImage imageNamed:@"cardback.png"]; 47 | } 48 | 49 | - (void)updateCardButton:(UIButton *)cardButton forCard:(Card *)card 50 | { 51 | [cardButton setTitle: [card contents] forState: UIControlStateSelected]; 52 | [cardButton setTitle: [card contents] forState: UIControlStateSelected|UIControlStateDisabled]; 53 | [cardButton setImage: card.isFaceUp?nil:[CardGameViewController cardBackImage] 54 | forState: UIControlStateNormal]; 55 | cardButton.imageEdgeInsets = UIEdgeInsetsMake(5, 5, 5, 5); 56 | cardButton.selected = card.faceUp; 57 | cardButton.enabled = !card.isUnplayable; 58 | cardButton.alpha = card.isUnplayable ? 0.3 : 1.0; 59 | } 60 | 61 | - (void) updateUI 62 | { 63 | for(UIButton* cardButton in self.cardButtons) { 64 | NSUInteger index = [self.cardButtons indexOfObject: cardButton]; 65 | Card* card = [self.game cardAtIndex: index]; 66 | 67 | [self updateCardButton:cardButton forCard:card]; 68 | } 69 | 70 | self.scoreLabel.text = [NSString stringWithFormat: @"Score: %d", self.game.score]; 71 | 72 | [self updateLastFlipMessage: self.game.lastFlipResult pastFlip: NO]; 73 | 74 | self.cardsToOpenControl.enabled = !self.firstFlipMade; 75 | 76 | 77 | int flipsInHistory = [self.game.flipHistory count]; 78 | [self.historySlider setMaximumValue: flipsInHistory?(flipsInHistory-1):0]; 79 | [self.historySlider setValue: self.historySlider.maximumValue]; 80 | } 81 | 82 | - (void) updateLastFlipMessage: (FlipResult*) flipResult pastFlip: (BOOL) pastFlip 83 | { 84 | NSAttributedString* flipResultContents = [self cardMatchMessageForFlipResult: flipResult]; 85 | self.lastFlipResultLabel.attributedText = [self wrapFlipResultMessageStyles: flipResultContents]; 86 | self.lastFlipResultLabel.alpha = pastFlip? 0.55 : 1; 87 | } 88 | 89 | - (NSAttributedString*) wrapFlipResultMessageStyles: (NSAttributedString*) message 90 | { 91 | NSMutableAttributedString* s = [[NSMutableAttributedString alloc] initWithAttributedString: message]; 92 | 93 | NSMutableParagraphStyle* paragraph = [[NSMutableParagraphStyle alloc] init]; 94 | paragraph.alignment = NSTextAlignmentCenter; 95 | paragraph.lineBreakMode = NSLineBreakByWordWrapping; 96 | 97 | [s addAttributes: @{ 98 | NSFontAttributeName: [UIFont systemFontOfSize: 14], 99 | NSParagraphStyleAttributeName: paragraph} 100 | range: NSMakeRange(0, s.string.length)]; 101 | 102 | return s; 103 | } 104 | 105 | - (NSAttributedString*) cardMatchMessageForFlipResult: (FlipResult*) flipResult 106 | { 107 | switch (flipResult.type) { 108 | case FLIPRESULT_FLIPPED_UP: 109 | return [self flippedUpMessage: flipResult.cards[0]]; 110 | case FLIPRESULT_MATCH: 111 | return [self cardsMatchMessage: flipResult.cards withPoints: flipResult.points]; 112 | case FLIPRESULT_MISMATCH: 113 | return [self cardsMismatchMessage: flipResult.cards withPoints: flipResult.points]; 114 | } 115 | return [[NSMutableAttributedString alloc] initWithString: @""]; 116 | } 117 | 118 | - (NSAttributedString*) flippedUpMessage: (Card*) card 119 | { 120 | NSMutableAttributedString* s = [[NSMutableAttributedString alloc] init]; 121 | [s appendAttributedString: [[NSMutableAttributedString alloc] initWithString: @"Flipped up "]]; 122 | [s appendAttributedString: [self cardsUnionMessage: @[card]]]; 123 | return s; 124 | } 125 | 126 | - (NSAttributedString*) cardsMatchMessage: (NSArray*) cards withPoints: (int) points 127 | { 128 | NSMutableAttributedString* s = [[NSMutableAttributedString alloc] init]; 129 | [s appendAttributedString: [[NSMutableAttributedString alloc] initWithString: @"Matched "]]; 130 | [s appendAttributedString: [self cardsUnionMessage: cards]]; 131 | NSString* forPointsString = [NSString stringWithFormat: @" for %d points", points]; 132 | [s appendAttributedString: [[NSMutableAttributedString alloc] initWithString: forPointsString]]; 133 | return s; 134 | } 135 | 136 | - (NSAttributedString*) cardsMismatchMessage:(NSArray*) cards withPoints: (int) points 137 | { 138 | NSMutableAttributedString* s = [[NSMutableAttributedString alloc] init]; 139 | [s appendAttributedString: [self cardsUnionMessage: cards]]; 140 | NSString* dontMatchString = [NSString stringWithFormat: @" don't match! %d points penalty!", points]; 141 | [s appendAttributedString: [[NSMutableAttributedString alloc] initWithString: dontMatchString]]; 142 | return s; 143 | } 144 | 145 | - (NSAttributedString*) cardsUnionMessage:(NSArray*) cards 146 | { 147 | NSMutableAttributedString* s = [[NSMutableAttributedString alloc] init]; 148 | int lastCardNumber = [cards count]; 149 | int currentCardNumber = 1; 150 | for (Card* card in cards) { 151 | [s appendAttributedString: [self cardString: card]]; 152 | if (currentCardNumber < lastCardNumber) { 153 | [s appendAttributedString: [[NSMutableAttributedString alloc] initWithString: @" & "]]; 154 | } 155 | currentCardNumber++; 156 | } 157 | return s; 158 | } 159 | 160 | - (NSAttributedString*) cardString:(Card*) card 161 | { 162 | return [[NSMutableAttributedString alloc] initWithString: [card contents]]; 163 | } 164 | 165 | - (CardMatchingGame*) createNewGame 166 | { 167 | Deck* deck = [[PlayingCardDeck alloc] init]; 168 | CardMatchingGame* game = [[CardMatchingGame alloc] initWithCardCount: [self.cardButtons count] usingDeck: deck]; 169 | game.maxCardsToOpen = self.cardsToOpen?self.cardsToOpen:2; 170 | return game; 171 | } 172 | 173 | - (CardMatchingGame*) game 174 | { 175 | if (!_game) { 176 | _game = [self createNewGame]; 177 | self.firstFlipMade = NO; 178 | } 179 | return _game; 180 | } 181 | 182 | - (GameResult*) gameResult 183 | { 184 | if (!_gameResult) 185 | _gameResult = [self createGameResult]; 186 | 187 | return _gameResult; 188 | } 189 | 190 | - (GameResult*) createGameResult 191 | { 192 | return [[GameResult alloc] init]; 193 | } 194 | 195 | - (IBAction)flipCard:(UIButton *)sender { 196 | self.firstFlipMade = YES; 197 | NSUInteger index = [self.cardButtons indexOfObject: sender]; 198 | [self.game flipCardAtIndex: index]; 199 | self.flipCount++; 200 | self.gameResult.score = self.game.score; 201 | [self updateUI]; 202 | } 203 | 204 | - (IBAction)deal:(UIButton *)sender { 205 | self.game = nil; 206 | self.gameResult = nil; 207 | self.flipCount = 0; 208 | [self updateUI]; 209 | } 210 | 211 | - (IBAction)changedCardsToOpen:(UISegmentedControl *)sender { 212 | self.cardsToOpen = self.cardsToOpenControl.selectedSegmentIndex == 0 ? 2 : 3; 213 | self.game.maxCardsToOpen = self.cardsToOpen; 214 | } 215 | 216 | - (IBAction)historySlide:(UISlider *)sender { 217 | int index = self.historySlider.value; 218 | 219 | if ([self.game.flipHistory count]) { 220 | BOOL pastFlip = index < ([self.game.flipHistory count] - 1); 221 | [self updateLastFlipMessage: self.game.flipHistory[index] pastFlip: pastFlip]; 222 | } 223 | } 224 | 225 | @end 226 | -------------------------------------------------------------------------------- /CardGame.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E70509301704EB1700CC4A83 /* cardback.png in Resources */ = {isa = PBXBuildFile; fileRef = E705092F1704EB1700CC4A83 /* cardback.png */; }; 11 | E71FD6A91700E2C400D55D24 /* CardMatchingGameTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E71FD6A81700E2C400D55D24 /* CardMatchingGameTest.m */; }; 12 | E71FD6AC1700E5C500D55D24 /* CardMatchingGame.m in Sources */ = {isa = PBXBuildFile; fileRef = E71FD6AB1700E5C500D55D24 /* CardMatchingGame.m */; }; 13 | E71FD6AD1700E5C500D55D24 /* CardMatchingGame.m in Sources */ = {isa = PBXBuildFile; fileRef = E71FD6AB1700E5C500D55D24 /* CardMatchingGame.m */; }; 14 | E71FD6B01700F75800D55D24 /* MockDeck.m in Sources */ = {isa = PBXBuildFile; fileRef = E71FD6AF1700F75800D55D24 /* MockDeck.m */; }; 15 | E71FD6B11700F75800D55D24 /* MockDeck.m in Sources */ = {isa = PBXBuildFile; fileRef = E71FD6AF1700F75800D55D24 /* MockDeck.m */; }; 16 | E744345717176FB6001BAC82 /* FlipResult.m in Sources */ = {isa = PBXBuildFile; fileRef = E744345617176FB6001BAC82 /* FlipResult.m */; }; 17 | E744345817176FB6001BAC82 /* FlipResult.m in Sources */ = {isa = PBXBuildFile; fileRef = E744345617176FB6001BAC82 /* FlipResult.m */; }; 18 | E744345B1717709C001BAC82 /* FlipResultTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E744345A1717709C001BAC82 /* FlipResultTest.m */; }; 19 | E767CE641730751900F06F4F /* match-tab.png in Resources */ = {isa = PBXBuildFile; fileRef = E767CE621730751900F06F4F /* match-tab.png */; }; 20 | E767CE651730751900F06F4F /* set-tab.png in Resources */ = {isa = PBXBuildFile; fileRef = E767CE631730751900F06F4F /* set-tab.png */; }; 21 | E767CE6917307B6F00F06F4F /* score-tab.png in Resources */ = {isa = PBXBuildFile; fileRef = E767CE6817307B6F00F06F4F /* score-tab.png */; }; 22 | E778872D1711F3AB0012B5E4 /* SetCard.m in Sources */ = {isa = PBXBuildFile; fileRef = E778872C1711F3AB0012B5E4 /* SetCard.m */; }; 23 | E778872E1711F3AB0012B5E4 /* SetCard.m in Sources */ = {isa = PBXBuildFile; fileRef = E778872C1711F3AB0012B5E4 /* SetCard.m */; }; 24 | E77887311711F3D60012B5E4 /* SetCardTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E77887301711F3D60012B5E4 /* SetCardTest.m */; }; 25 | E778873417120C3F0012B5E4 /* SetCardDeckTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E778873317120C3E0012B5E4 /* SetCardDeckTest.m */; }; 26 | E778873717120C7A0012B5E4 /* SetCardDeck.m in Sources */ = {isa = PBXBuildFile; fileRef = E778873617120C7A0012B5E4 /* SetCardDeck.m */; }; 27 | E778873817120C7A0012B5E4 /* SetCardDeck.m in Sources */ = {isa = PBXBuildFile; fileRef = E778873617120C7A0012B5E4 /* SetCardDeck.m */; }; 28 | E778873B171215AF0012B5E4 /* SetGameTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E778873A171215AF0012B5E4 /* SetGameTest.m */; }; 29 | E778873E171216030012B5E4 /* SetGame.m in Sources */ = {isa = PBXBuildFile; fileRef = E778873D171216020012B5E4 /* SetGame.m */; }; 30 | E778873F171216030012B5E4 /* SetGame.m in Sources */ = {isa = PBXBuildFile; fileRef = E778873D171216020012B5E4 /* SetGame.m */; }; 31 | E778874217121D600012B5E4 /* SetGameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E778874117121D600012B5E4 /* SetGameViewController.m */; }; 32 | E778874317121D600012B5E4 /* SetGameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E778874117121D600012B5E4 /* SetGameViewController.m */; }; 33 | E79178FF17115C4B00F90F3B /* GameResultViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E79178FE17115C4B00F90F3B /* GameResultViewController.m */; }; 34 | E791790217116B6700F90F3B /* MockUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = E791790117116B6700F90F3B /* MockUserDefaults.m */; }; 35 | E7B1707C16FCFF8700B4B9B6 /* PlayingCard.m in Sources */ = {isa = PBXBuildFile; fileRef = E7B1707B16FCFF8700B4B9B6 /* PlayingCard.m */; }; 36 | E7B1707D16FCFF8800B4B9B6 /* PlayingCard.m in Sources */ = {isa = PBXBuildFile; fileRef = E7B1707B16FCFF8700B4B9B6 /* PlayingCard.m */; }; 37 | E7B1708016FCFFDC00B4B9B6 /* PlayingCardTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E7B1707F16FCFFDC00B4B9B6 /* PlayingCardTest.m */; }; 38 | E7B1708316FD0F1400B4B9B6 /* PlayingCardDeckTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E7B1708216FD0F1400B4B9B6 /* PlayingCardDeckTest.m */; }; 39 | E7B1708616FD0F4D00B4B9B6 /* PlayingCardDeck.m in Sources */ = {isa = PBXBuildFile; fileRef = E7B1708516FD0F4D00B4B9B6 /* PlayingCardDeck.m */; }; 40 | E7B1708716FD0F4D00B4B9B6 /* PlayingCardDeck.m in Sources */ = {isa = PBXBuildFile; fileRef = E7B1708516FD0F4D00B4B9B6 /* PlayingCardDeck.m */; }; 41 | E7D4D4BE16FA4F1F00408B8A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7D4D4BD16FA4F1F00408B8A /* UIKit.framework */; }; 42 | E7D4D4C016FA4F1F00408B8A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7D4D4BF16FA4F1F00408B8A /* Foundation.framework */; }; 43 | E7D4D4C216FA4F1F00408B8A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7D4D4C116FA4F1F00408B8A /* CoreGraphics.framework */; }; 44 | E7D4D4C816FA4F1F00408B8A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E7D4D4C616FA4F1F00408B8A /* InfoPlist.strings */; }; 45 | E7D4D4CA16FA4F1F00408B8A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E7D4D4C916FA4F1F00408B8A /* main.m */; }; 46 | E7D4D4CE16FA4F1F00408B8A /* CardGameAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E7D4D4CD16FA4F1F00408B8A /* CardGameAppDelegate.m */; }; 47 | E7D4D4D016FA4F1F00408B8A /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = E7D4D4CF16FA4F1F00408B8A /* Default.png */; }; 48 | E7D4D4D216FA4F1F00408B8A /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E7D4D4D116FA4F1F00408B8A /* Default@2x.png */; }; 49 | E7D4D4D416FA4F1F00408B8A /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E7D4D4D316FA4F1F00408B8A /* Default-568h@2x.png */; }; 50 | E7D4D4D716FA4F1F00408B8A /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E7D4D4D516FA4F1F00408B8A /* MainStoryboard.storyboard */; }; 51 | E7D4D4DA16FA4F1F00408B8A /* CardGameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E7D4D4D916FA4F1F00408B8A /* CardGameViewController.m */; }; 52 | E7D4D4E216FA4F1F00408B8A /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7D4D4E116FA4F1F00408B8A /* SenTestingKit.framework */; }; 53 | E7D4D4E316FA4F1F00408B8A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7D4D4BD16FA4F1F00408B8A /* UIKit.framework */; }; 54 | E7D4D4E416FA4F1F00408B8A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7D4D4BF16FA4F1F00408B8A /* Foundation.framework */; }; 55 | E7D4D4EC16FA4F1F00408B8A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E7D4D4EA16FA4F1F00408B8A /* InfoPlist.strings */; }; 56 | E7D4D4FA16FA504A00408B8A /* Deck.m in Sources */ = {isa = PBXBuildFile; fileRef = E7D4D4F916FA504A00408B8A /* Deck.m */; }; 57 | E7D4D4FB16FA509300408B8A /* Deck.m in Sources */ = {isa = PBXBuildFile; fileRef = E7D4D4F916FA504A00408B8A /* Deck.m */; }; 58 | E7D4D4FE16FA50C700408B8A /* DeckTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E7D4D4FD16FA50C700408B8A /* DeckTest.m */; }; 59 | E7D4D50116FA51D100408B8A /* Card.m in Sources */ = {isa = PBXBuildFile; fileRef = E7D4D50016FA51D100408B8A /* Card.m */; }; 60 | E7D4D50216FA51D100408B8A /* Card.m in Sources */ = {isa = PBXBuildFile; fileRef = E7D4D50016FA51D100408B8A /* Card.m */; }; 61 | E7E79A391735A458004A9504 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E7E79A381735A458004A9504 /* SettingsViewController.m */; }; 62 | E7E79A3B1735A7F8004A9504 /* settings-tab.png in Resources */ = {isa = PBXBuildFile; fileRef = E7E79A3A1735A7F8004A9504 /* settings-tab.png */; }; 63 | E7F1959C1710AB80000163EF /* GameResultTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E7F1959B1710AB80000163EF /* GameResultTest.m */; }; 64 | E7F1959F1710ABC9000163EF /* GameResult.m in Sources */ = {isa = PBXBuildFile; fileRef = E7F1959E1710ABC9000163EF /* GameResult.m */; }; 65 | E7F195A01710ABC9000163EF /* GameResult.m in Sources */ = {isa = PBXBuildFile; fileRef = E7F1959E1710ABC9000163EF /* GameResult.m */; }; 66 | /* End PBXBuildFile section */ 67 | 68 | /* Begin PBXContainerItemProxy section */ 69 | E7D4D4E516FA4F1F00408B8A /* PBXContainerItemProxy */ = { 70 | isa = PBXContainerItemProxy; 71 | containerPortal = E7D4D4B216FA4F1F00408B8A /* Project object */; 72 | proxyType = 1; 73 | remoteGlobalIDString = E7D4D4B916FA4F1F00408B8A; 74 | remoteInfo = CardGame; 75 | }; 76 | /* End PBXContainerItemProxy section */ 77 | 78 | /* Begin PBXFileReference section */ 79 | E705092F1704EB1700CC4A83 /* cardback.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cardback.png; sourceTree = ""; }; 80 | E71FD6A71700E2C400D55D24 /* CardMatchingGameTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CardMatchingGameTest.h; path = Model/CardMatchingGameTest.h; sourceTree = ""; }; 81 | E71FD6A81700E2C400D55D24 /* CardMatchingGameTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CardMatchingGameTest.m; path = Model/CardMatchingGameTest.m; sourceTree = ""; }; 82 | E71FD6AA1700E5C500D55D24 /* CardMatchingGame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CardMatchingGame.h; path = Model/CardMatchingGame.h; sourceTree = ""; }; 83 | E71FD6AB1700E5C500D55D24 /* CardMatchingGame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CardMatchingGame.m; path = Model/CardMatchingGame.m; sourceTree = ""; }; 84 | E71FD6AE1700F75800D55D24 /* MockDeck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MockDeck.h; path = Model/MockDeck.h; sourceTree = ""; }; 85 | E71FD6AF1700F75800D55D24 /* MockDeck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MockDeck.m; path = Model/MockDeck.m; sourceTree = ""; }; 86 | E744345517176FB6001BAC82 /* FlipResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlipResult.h; path = Model/FlipResult.h; sourceTree = ""; }; 87 | E744345617176FB6001BAC82 /* FlipResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FlipResult.m; path = Model/FlipResult.m; sourceTree = ""; }; 88 | E74434591717709C001BAC82 /* FlipResultTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlipResultTest.h; path = Model/FlipResultTest.h; sourceTree = ""; }; 89 | E744345A1717709C001BAC82 /* FlipResultTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FlipResultTest.m; path = Model/FlipResultTest.m; sourceTree = ""; }; 90 | E767CE621730751900F06F4F /* match-tab.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "match-tab.png"; sourceTree = ""; }; 91 | E767CE631730751900F06F4F /* set-tab.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "set-tab.png"; sourceTree = ""; }; 92 | E767CE6817307B6F00F06F4F /* score-tab.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "score-tab.png"; sourceTree = ""; }; 93 | E778872B1711F3AB0012B5E4 /* SetCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SetCard.h; path = Model/SetCard.h; sourceTree = ""; }; 94 | E778872C1711F3AB0012B5E4 /* SetCard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SetCard.m; path = Model/SetCard.m; sourceTree = ""; }; 95 | E778872F1711F3D60012B5E4 /* SetCardTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SetCardTest.h; path = Model/SetCardTest.h; sourceTree = ""; }; 96 | E77887301711F3D60012B5E4 /* SetCardTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SetCardTest.m; path = Model/SetCardTest.m; sourceTree = ""; }; 97 | E778873217120C3E0012B5E4 /* SetCardDeckTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SetCardDeckTest.h; path = Model/SetCardDeckTest.h; sourceTree = ""; }; 98 | E778873317120C3E0012B5E4 /* SetCardDeckTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SetCardDeckTest.m; path = Model/SetCardDeckTest.m; sourceTree = ""; }; 99 | E778873517120C7A0012B5E4 /* SetCardDeck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SetCardDeck.h; path = Model/SetCardDeck.h; sourceTree = ""; }; 100 | E778873617120C7A0012B5E4 /* SetCardDeck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SetCardDeck.m; path = Model/SetCardDeck.m; sourceTree = ""; }; 101 | E7788739171215AE0012B5E4 /* SetGameTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SetGameTest.h; path = Model/SetGameTest.h; sourceTree = ""; }; 102 | E778873A171215AF0012B5E4 /* SetGameTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SetGameTest.m; path = Model/SetGameTest.m; sourceTree = ""; }; 103 | E778873C171216020012B5E4 /* SetGame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SetGame.h; path = Model/SetGame.h; sourceTree = ""; }; 104 | E778873D171216020012B5E4 /* SetGame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SetGame.m; path = Model/SetGame.m; sourceTree = ""; }; 105 | E778874017121D600012B5E4 /* SetGameViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SetGameViewController.h; sourceTree = ""; }; 106 | E778874117121D600012B5E4 /* SetGameViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SetGameViewController.m; sourceTree = ""; }; 107 | E79178FD17115C4B00F90F3B /* GameResultViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameResultViewController.h; sourceTree = ""; }; 108 | E79178FE17115C4B00F90F3B /* GameResultViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GameResultViewController.m; sourceTree = ""; }; 109 | E791790017116B6700F90F3B /* MockUserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MockUserDefaults.h; path = Model/MockUserDefaults.h; sourceTree = ""; }; 110 | E791790117116B6700F90F3B /* MockUserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MockUserDefaults.m; path = Model/MockUserDefaults.m; sourceTree = ""; }; 111 | E7B1707A16FCFF8700B4B9B6 /* PlayingCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlayingCard.h; path = Model/PlayingCard.h; sourceTree = ""; }; 112 | E7B1707B16FCFF8700B4B9B6 /* PlayingCard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PlayingCard.m; path = Model/PlayingCard.m; sourceTree = ""; }; 113 | E7B1707E16FCFFDC00B4B9B6 /* PlayingCardTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlayingCardTest.h; path = Model/PlayingCardTest.h; sourceTree = ""; }; 114 | E7B1707F16FCFFDC00B4B9B6 /* PlayingCardTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PlayingCardTest.m; path = Model/PlayingCardTest.m; sourceTree = ""; }; 115 | E7B1708116FD0F1400B4B9B6 /* PlayingCardDeckTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlayingCardDeckTest.h; path = Model/PlayingCardDeckTest.h; sourceTree = ""; }; 116 | E7B1708216FD0F1400B4B9B6 /* PlayingCardDeckTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PlayingCardDeckTest.m; path = Model/PlayingCardDeckTest.m; sourceTree = ""; }; 117 | E7B1708416FD0F4D00B4B9B6 /* PlayingCardDeck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlayingCardDeck.h; path = Model/PlayingCardDeck.h; sourceTree = ""; }; 118 | E7B1708516FD0F4D00B4B9B6 /* PlayingCardDeck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PlayingCardDeck.m; path = Model/PlayingCardDeck.m; sourceTree = ""; }; 119 | E7D4D4BA16FA4F1F00408B8A /* CardGame.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CardGame.app; sourceTree = BUILT_PRODUCTS_DIR; }; 120 | E7D4D4BD16FA4F1F00408B8A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 121 | E7D4D4BF16FA4F1F00408B8A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 122 | E7D4D4C116FA4F1F00408B8A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 123 | E7D4D4C516FA4F1F00408B8A /* CardGame-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CardGame-Info.plist"; sourceTree = ""; }; 124 | E7D4D4C716FA4F1F00408B8A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 125 | E7D4D4C916FA4F1F00408B8A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 126 | E7D4D4CB16FA4F1F00408B8A /* CardGame-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CardGame-Prefix.pch"; sourceTree = ""; }; 127 | E7D4D4CC16FA4F1F00408B8A /* CardGameAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CardGameAppDelegate.h; sourceTree = ""; }; 128 | E7D4D4CD16FA4F1F00408B8A /* CardGameAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CardGameAppDelegate.m; sourceTree = ""; }; 129 | E7D4D4CF16FA4F1F00408B8A /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 130 | E7D4D4D116FA4F1F00408B8A /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 131 | E7D4D4D316FA4F1F00408B8A /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 132 | E7D4D4D616FA4F1F00408B8A /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = ""; }; 133 | E7D4D4D816FA4F1F00408B8A /* CardGameViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CardGameViewController.h; sourceTree = ""; }; 134 | E7D4D4D916FA4F1F00408B8A /* CardGameViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CardGameViewController.m; sourceTree = ""; }; 135 | E7D4D4E016FA4F1F00408B8A /* CardGameTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CardGameTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; 136 | E7D4D4E116FA4F1F00408B8A /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; 137 | E7D4D4E916FA4F1F00408B8A /* CardGameTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CardGameTests-Info.plist"; sourceTree = ""; }; 138 | E7D4D4EB16FA4F1F00408B8A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 139 | E7D4D4F816FA504A00408B8A /* Deck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Deck.h; path = Model/Deck.h; sourceTree = ""; }; 140 | E7D4D4F916FA504A00408B8A /* Deck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Deck.m; path = Model/Deck.m; sourceTree = ""; }; 141 | E7D4D4FC16FA50C700408B8A /* DeckTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DeckTest.h; path = Model/DeckTest.h; sourceTree = ""; }; 142 | E7D4D4FD16FA50C700408B8A /* DeckTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DeckTest.m; path = Model/DeckTest.m; sourceTree = ""; }; 143 | E7D4D4FF16FA51D100408B8A /* Card.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Card.h; path = Model/Card.h; sourceTree = ""; }; 144 | E7D4D50016FA51D100408B8A /* Card.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Card.m; path = Model/Card.m; sourceTree = ""; }; 145 | E7E79A371735A458004A9504 /* SettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = ""; }; 146 | E7E79A381735A458004A9504 /* SettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = ""; }; 147 | E7E79A3A1735A7F8004A9504 /* settings-tab.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "settings-tab.png"; sourceTree = ""; }; 148 | E7F1959A1710AB80000163EF /* GameResultTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameResultTest.h; path = Model/GameResultTest.h; sourceTree = ""; }; 149 | E7F1959B1710AB80000163EF /* GameResultTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GameResultTest.m; path = Model/GameResultTest.m; sourceTree = ""; }; 150 | E7F1959D1710ABC9000163EF /* GameResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GameResult.h; path = Model/GameResult.h; sourceTree = ""; }; 151 | E7F1959E1710ABC9000163EF /* GameResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GameResult.m; path = Model/GameResult.m; sourceTree = ""; }; 152 | /* End PBXFileReference section */ 153 | 154 | /* Begin PBXFrameworksBuildPhase section */ 155 | E7D4D4B716FA4F1F00408B8A /* Frameworks */ = { 156 | isa = PBXFrameworksBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | E7D4D4BE16FA4F1F00408B8A /* UIKit.framework in Frameworks */, 160 | E7D4D4C016FA4F1F00408B8A /* Foundation.framework in Frameworks */, 161 | E7D4D4C216FA4F1F00408B8A /* CoreGraphics.framework in Frameworks */, 162 | ); 163 | runOnlyForDeploymentPostprocessing = 0; 164 | }; 165 | E7D4D4DC16FA4F1F00408B8A /* Frameworks */ = { 166 | isa = PBXFrameworksBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | E7D4D4E216FA4F1F00408B8A /* SenTestingKit.framework in Frameworks */, 170 | E7D4D4E316FA4F1F00408B8A /* UIKit.framework in Frameworks */, 171 | E7D4D4E416FA4F1F00408B8A /* Foundation.framework in Frameworks */, 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | /* End PBXFrameworksBuildPhase section */ 176 | 177 | /* Begin PBXGroup section */ 178 | E71FD6A61700E02E00D55D24 /* Model */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | E7D4D4FC16FA50C700408B8A /* DeckTest.h */, 182 | E7D4D4FD16FA50C700408B8A /* DeckTest.m */, 183 | E7B1707E16FCFFDC00B4B9B6 /* PlayingCardTest.h */, 184 | E7B1707F16FCFFDC00B4B9B6 /* PlayingCardTest.m */, 185 | E778872F1711F3D60012B5E4 /* SetCardTest.h */, 186 | E77887301711F3D60012B5E4 /* SetCardTest.m */, 187 | E7B1708116FD0F1400B4B9B6 /* PlayingCardDeckTest.h */, 188 | E7B1708216FD0F1400B4B9B6 /* PlayingCardDeckTest.m */, 189 | E778873217120C3E0012B5E4 /* SetCardDeckTest.h */, 190 | E778873317120C3E0012B5E4 /* SetCardDeckTest.m */, 191 | E71FD6A71700E2C400D55D24 /* CardMatchingGameTest.h */, 192 | E71FD6A81700E2C400D55D24 /* CardMatchingGameTest.m */, 193 | E74434591717709C001BAC82 /* FlipResultTest.h */, 194 | E744345A1717709C001BAC82 /* FlipResultTest.m */, 195 | E7788739171215AE0012B5E4 /* SetGameTest.h */, 196 | E778873A171215AF0012B5E4 /* SetGameTest.m */, 197 | E71FD6AE1700F75800D55D24 /* MockDeck.h */, 198 | E71FD6AF1700F75800D55D24 /* MockDeck.m */, 199 | E7F1959A1710AB80000163EF /* GameResultTest.h */, 200 | E7F1959B1710AB80000163EF /* GameResultTest.m */, 201 | E791790017116B6700F90F3B /* MockUserDefaults.h */, 202 | E791790117116B6700F90F3B /* MockUserDefaults.m */, 203 | ); 204 | name = Model; 205 | sourceTree = ""; 206 | }; 207 | E733B6CB16FF81320061ADB2 /* Model */ = { 208 | isa = PBXGroup; 209 | children = ( 210 | E7D4D4F816FA504A00408B8A /* Deck.h */, 211 | E7D4D4F916FA504A00408B8A /* Deck.m */, 212 | E7D4D4FF16FA51D100408B8A /* Card.h */, 213 | E7D4D50016FA51D100408B8A /* Card.m */, 214 | E7B1707A16FCFF8700B4B9B6 /* PlayingCard.h */, 215 | E7B1707B16FCFF8700B4B9B6 /* PlayingCard.m */, 216 | E7B1708416FD0F4D00B4B9B6 /* PlayingCardDeck.h */, 217 | E7B1708516FD0F4D00B4B9B6 /* PlayingCardDeck.m */, 218 | E778872B1711F3AB0012B5E4 /* SetCard.h */, 219 | E778872C1711F3AB0012B5E4 /* SetCard.m */, 220 | E778873517120C7A0012B5E4 /* SetCardDeck.h */, 221 | E778873617120C7A0012B5E4 /* SetCardDeck.m */, 222 | E71FD6AA1700E5C500D55D24 /* CardMatchingGame.h */, 223 | E71FD6AB1700E5C500D55D24 /* CardMatchingGame.m */, 224 | E744345517176FB6001BAC82 /* FlipResult.h */, 225 | E744345617176FB6001BAC82 /* FlipResult.m */, 226 | E778873C171216020012B5E4 /* SetGame.h */, 227 | E778873D171216020012B5E4 /* SetGame.m */, 228 | E7F1959D1710ABC9000163EF /* GameResult.h */, 229 | E7F1959E1710ABC9000163EF /* GameResult.m */, 230 | ); 231 | name = Model; 232 | sourceTree = ""; 233 | }; 234 | E7D4D4B116FA4F1F00408B8A = { 235 | isa = PBXGroup; 236 | children = ( 237 | E7D4D4C316FA4F1F00408B8A /* CardGame */, 238 | E7D4D4E716FA4F1F00408B8A /* CardGameTests */, 239 | E7D4D4BC16FA4F1F00408B8A /* Frameworks */, 240 | E7D4D4BB16FA4F1F00408B8A /* Products */, 241 | ); 242 | sourceTree = ""; 243 | }; 244 | E7D4D4BB16FA4F1F00408B8A /* Products */ = { 245 | isa = PBXGroup; 246 | children = ( 247 | E7D4D4BA16FA4F1F00408B8A /* CardGame.app */, 248 | E7D4D4E016FA4F1F00408B8A /* CardGameTests.octest */, 249 | ); 250 | name = Products; 251 | sourceTree = ""; 252 | }; 253 | E7D4D4BC16FA4F1F00408B8A /* Frameworks */ = { 254 | isa = PBXGroup; 255 | children = ( 256 | E7D4D4BD16FA4F1F00408B8A /* UIKit.framework */, 257 | E7D4D4BF16FA4F1F00408B8A /* Foundation.framework */, 258 | E7D4D4C116FA4F1F00408B8A /* CoreGraphics.framework */, 259 | E7D4D4E116FA4F1F00408B8A /* SenTestingKit.framework */, 260 | ); 261 | name = Frameworks; 262 | sourceTree = ""; 263 | }; 264 | E7D4D4C316FA4F1F00408B8A /* CardGame */ = { 265 | isa = PBXGroup; 266 | children = ( 267 | E7D4D4CC16FA4F1F00408B8A /* CardGameAppDelegate.h */, 268 | E7D4D4CD16FA4F1F00408B8A /* CardGameAppDelegate.m */, 269 | E7D4D4D516FA4F1F00408B8A /* MainStoryboard.storyboard */, 270 | E7D4D4D816FA4F1F00408B8A /* CardGameViewController.h */, 271 | E7D4D4D916FA4F1F00408B8A /* CardGameViewController.m */, 272 | E778874017121D600012B5E4 /* SetGameViewController.h */, 273 | E778874117121D600012B5E4 /* SetGameViewController.m */, 274 | E79178FD17115C4B00F90F3B /* GameResultViewController.h */, 275 | E79178FE17115C4B00F90F3B /* GameResultViewController.m */, 276 | E7E79A371735A458004A9504 /* SettingsViewController.h */, 277 | E7E79A381735A458004A9504 /* SettingsViewController.m */, 278 | E7D4D4C416FA4F1F00408B8A /* Supporting Files */, 279 | E733B6CB16FF81320061ADB2 /* Model */, 280 | ); 281 | path = CardGame; 282 | sourceTree = ""; 283 | }; 284 | E7D4D4C416FA4F1F00408B8A /* Supporting Files */ = { 285 | isa = PBXGroup; 286 | children = ( 287 | E705092F1704EB1700CC4A83 /* cardback.png */, 288 | E767CE621730751900F06F4F /* match-tab.png */, 289 | E767CE631730751900F06F4F /* set-tab.png */, 290 | E767CE6817307B6F00F06F4F /* score-tab.png */, 291 | E7E79A3A1735A7F8004A9504 /* settings-tab.png */, 292 | E7D4D4C516FA4F1F00408B8A /* CardGame-Info.plist */, 293 | E7D4D4C616FA4F1F00408B8A /* InfoPlist.strings */, 294 | E7D4D4C916FA4F1F00408B8A /* main.m */, 295 | E7D4D4CB16FA4F1F00408B8A /* CardGame-Prefix.pch */, 296 | E7D4D4CF16FA4F1F00408B8A /* Default.png */, 297 | E7D4D4D116FA4F1F00408B8A /* Default@2x.png */, 298 | E7D4D4D316FA4F1F00408B8A /* Default-568h@2x.png */, 299 | ); 300 | name = "Supporting Files"; 301 | sourceTree = ""; 302 | }; 303 | E7D4D4E716FA4F1F00408B8A /* CardGameTests */ = { 304 | isa = PBXGroup; 305 | children = ( 306 | E7D4D4E816FA4F1F00408B8A /* Supporting Files */, 307 | E71FD6A61700E02E00D55D24 /* Model */, 308 | ); 309 | path = CardGameTests; 310 | sourceTree = ""; 311 | }; 312 | E7D4D4E816FA4F1F00408B8A /* Supporting Files */ = { 313 | isa = PBXGroup; 314 | children = ( 315 | E7D4D4E916FA4F1F00408B8A /* CardGameTests-Info.plist */, 316 | E7D4D4EA16FA4F1F00408B8A /* InfoPlist.strings */, 317 | ); 318 | name = "Supporting Files"; 319 | sourceTree = ""; 320 | }; 321 | /* End PBXGroup section */ 322 | 323 | /* Begin PBXNativeTarget section */ 324 | E7D4D4B916FA4F1F00408B8A /* CardGame */ = { 325 | isa = PBXNativeTarget; 326 | buildConfigurationList = E7D4D4F216FA4F1F00408B8A /* Build configuration list for PBXNativeTarget "CardGame" */; 327 | buildPhases = ( 328 | E7D4D4B616FA4F1F00408B8A /* Sources */, 329 | E7D4D4B716FA4F1F00408B8A /* Frameworks */, 330 | E7D4D4B816FA4F1F00408B8A /* Resources */, 331 | ); 332 | buildRules = ( 333 | ); 334 | dependencies = ( 335 | ); 336 | name = CardGame; 337 | productName = CardGame; 338 | productReference = E7D4D4BA16FA4F1F00408B8A /* CardGame.app */; 339 | productType = "com.apple.product-type.application"; 340 | }; 341 | E7D4D4DF16FA4F1F00408B8A /* CardGameTests */ = { 342 | isa = PBXNativeTarget; 343 | buildConfigurationList = E7D4D4F516FA4F1F00408B8A /* Build configuration list for PBXNativeTarget "CardGameTests" */; 344 | buildPhases = ( 345 | E7D4D4DB16FA4F1F00408B8A /* Sources */, 346 | E7D4D4DC16FA4F1F00408B8A /* Frameworks */, 347 | E7D4D4DD16FA4F1F00408B8A /* Resources */, 348 | E7D4D4DE16FA4F1F00408B8A /* ShellScript */, 349 | ); 350 | buildRules = ( 351 | ); 352 | dependencies = ( 353 | E7D4D4E616FA4F1F00408B8A /* PBXTargetDependency */, 354 | ); 355 | name = CardGameTests; 356 | productName = CardGameTests; 357 | productReference = E7D4D4E016FA4F1F00408B8A /* CardGameTests.octest */; 358 | productType = "com.apple.product-type.bundle"; 359 | }; 360 | /* End PBXNativeTarget section */ 361 | 362 | /* Begin PBXProject section */ 363 | E7D4D4B216FA4F1F00408B8A /* Project object */ = { 364 | isa = PBXProject; 365 | attributes = { 366 | CLASSPREFIX = CardGame; 367 | LastUpgradeCheck = 0460; 368 | ORGANIZATIONNAME = Vladimir; 369 | }; 370 | buildConfigurationList = E7D4D4B516FA4F1F00408B8A /* Build configuration list for PBXProject "CardGame" */; 371 | compatibilityVersion = "Xcode 3.2"; 372 | developmentRegion = English; 373 | hasScannedForEncodings = 0; 374 | knownRegions = ( 375 | en, 376 | ); 377 | mainGroup = E7D4D4B116FA4F1F00408B8A; 378 | productRefGroup = E7D4D4BB16FA4F1F00408B8A /* Products */; 379 | projectDirPath = ""; 380 | projectRoot = ""; 381 | targets = ( 382 | E7D4D4B916FA4F1F00408B8A /* CardGame */, 383 | E7D4D4DF16FA4F1F00408B8A /* CardGameTests */, 384 | ); 385 | }; 386 | /* End PBXProject section */ 387 | 388 | /* Begin PBXResourcesBuildPhase section */ 389 | E7D4D4B816FA4F1F00408B8A /* Resources */ = { 390 | isa = PBXResourcesBuildPhase; 391 | buildActionMask = 2147483647; 392 | files = ( 393 | E7D4D4C816FA4F1F00408B8A /* InfoPlist.strings in Resources */, 394 | E7D4D4D016FA4F1F00408B8A /* Default.png in Resources */, 395 | E7D4D4D216FA4F1F00408B8A /* Default@2x.png in Resources */, 396 | E7D4D4D416FA4F1F00408B8A /* Default-568h@2x.png in Resources */, 397 | E7D4D4D716FA4F1F00408B8A /* MainStoryboard.storyboard in Resources */, 398 | E70509301704EB1700CC4A83 /* cardback.png in Resources */, 399 | E767CE641730751900F06F4F /* match-tab.png in Resources */, 400 | E767CE651730751900F06F4F /* set-tab.png in Resources */, 401 | E767CE6917307B6F00F06F4F /* score-tab.png in Resources */, 402 | E7E79A3B1735A7F8004A9504 /* settings-tab.png in Resources */, 403 | ); 404 | runOnlyForDeploymentPostprocessing = 0; 405 | }; 406 | E7D4D4DD16FA4F1F00408B8A /* Resources */ = { 407 | isa = PBXResourcesBuildPhase; 408 | buildActionMask = 2147483647; 409 | files = ( 410 | E7D4D4EC16FA4F1F00408B8A /* InfoPlist.strings in Resources */, 411 | ); 412 | runOnlyForDeploymentPostprocessing = 0; 413 | }; 414 | /* End PBXResourcesBuildPhase section */ 415 | 416 | /* Begin PBXShellScriptBuildPhase section */ 417 | E7D4D4DE16FA4F1F00408B8A /* ShellScript */ = { 418 | isa = PBXShellScriptBuildPhase; 419 | buildActionMask = 2147483647; 420 | files = ( 421 | ); 422 | inputPaths = ( 423 | ); 424 | outputPaths = ( 425 | ); 426 | runOnlyForDeploymentPostprocessing = 0; 427 | shellPath = /bin/sh; 428 | shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; 429 | }; 430 | /* End PBXShellScriptBuildPhase section */ 431 | 432 | /* Begin PBXSourcesBuildPhase section */ 433 | E7D4D4B616FA4F1F00408B8A /* Sources */ = { 434 | isa = PBXSourcesBuildPhase; 435 | buildActionMask = 2147483647; 436 | files = ( 437 | E7D4D4CA16FA4F1F00408B8A /* main.m in Sources */, 438 | E7D4D4CE16FA4F1F00408B8A /* CardGameAppDelegate.m in Sources */, 439 | E7D4D4DA16FA4F1F00408B8A /* CardGameViewController.m in Sources */, 440 | E7D4D4FA16FA504A00408B8A /* Deck.m in Sources */, 441 | E7D4D50116FA51D100408B8A /* Card.m in Sources */, 442 | E7B1707C16FCFF8700B4B9B6 /* PlayingCard.m in Sources */, 443 | E7B1708616FD0F4D00B4B9B6 /* PlayingCardDeck.m in Sources */, 444 | E71FD6AC1700E5C500D55D24 /* CardMatchingGame.m in Sources */, 445 | E71FD6B01700F75800D55D24 /* MockDeck.m in Sources */, 446 | E7F1959F1710ABC9000163EF /* GameResult.m in Sources */, 447 | E79178FF17115C4B00F90F3B /* GameResultViewController.m in Sources */, 448 | E778872D1711F3AB0012B5E4 /* SetCard.m in Sources */, 449 | E778873717120C7A0012B5E4 /* SetCardDeck.m in Sources */, 450 | E778873E171216030012B5E4 /* SetGame.m in Sources */, 451 | E778874217121D600012B5E4 /* SetGameViewController.m in Sources */, 452 | E744345717176FB6001BAC82 /* FlipResult.m in Sources */, 453 | E7E79A391735A458004A9504 /* SettingsViewController.m in Sources */, 454 | ); 455 | runOnlyForDeploymentPostprocessing = 0; 456 | }; 457 | E7D4D4DB16FA4F1F00408B8A /* Sources */ = { 458 | isa = PBXSourcesBuildPhase; 459 | buildActionMask = 2147483647; 460 | files = ( 461 | E7D4D4FB16FA509300408B8A /* Deck.m in Sources */, 462 | E7D4D4FE16FA50C700408B8A /* DeckTest.m in Sources */, 463 | E7D4D50216FA51D100408B8A /* Card.m in Sources */, 464 | E7B1707D16FCFF8800B4B9B6 /* PlayingCard.m in Sources */, 465 | E7B1708016FCFFDC00B4B9B6 /* PlayingCardTest.m in Sources */, 466 | E7B1708316FD0F1400B4B9B6 /* PlayingCardDeckTest.m in Sources */, 467 | E7B1708716FD0F4D00B4B9B6 /* PlayingCardDeck.m in Sources */, 468 | E71FD6A91700E2C400D55D24 /* CardMatchingGameTest.m in Sources */, 469 | E71FD6AD1700E5C500D55D24 /* CardMatchingGame.m in Sources */, 470 | E71FD6B11700F75800D55D24 /* MockDeck.m in Sources */, 471 | E7F1959C1710AB80000163EF /* GameResultTest.m in Sources */, 472 | E7F195A01710ABC9000163EF /* GameResult.m in Sources */, 473 | E791790217116B6700F90F3B /* MockUserDefaults.m in Sources */, 474 | E778872E1711F3AB0012B5E4 /* SetCard.m in Sources */, 475 | E77887311711F3D60012B5E4 /* SetCardTest.m in Sources */, 476 | E778873417120C3F0012B5E4 /* SetCardDeckTest.m in Sources */, 477 | E778873817120C7A0012B5E4 /* SetCardDeck.m in Sources */, 478 | E778873B171215AF0012B5E4 /* SetGameTest.m in Sources */, 479 | E778873F171216030012B5E4 /* SetGame.m in Sources */, 480 | E778874317121D600012B5E4 /* SetGameViewController.m in Sources */, 481 | E744345817176FB6001BAC82 /* FlipResult.m in Sources */, 482 | E744345B1717709C001BAC82 /* FlipResultTest.m in Sources */, 483 | ); 484 | runOnlyForDeploymentPostprocessing = 0; 485 | }; 486 | /* End PBXSourcesBuildPhase section */ 487 | 488 | /* Begin PBXTargetDependency section */ 489 | E7D4D4E616FA4F1F00408B8A /* PBXTargetDependency */ = { 490 | isa = PBXTargetDependency; 491 | target = E7D4D4B916FA4F1F00408B8A /* CardGame */; 492 | targetProxy = E7D4D4E516FA4F1F00408B8A /* PBXContainerItemProxy */; 493 | }; 494 | /* End PBXTargetDependency section */ 495 | 496 | /* Begin PBXVariantGroup section */ 497 | E7D4D4C616FA4F1F00408B8A /* InfoPlist.strings */ = { 498 | isa = PBXVariantGroup; 499 | children = ( 500 | E7D4D4C716FA4F1F00408B8A /* en */, 501 | ); 502 | name = InfoPlist.strings; 503 | sourceTree = ""; 504 | }; 505 | E7D4D4D516FA4F1F00408B8A /* MainStoryboard.storyboard */ = { 506 | isa = PBXVariantGroup; 507 | children = ( 508 | E7D4D4D616FA4F1F00408B8A /* en */, 509 | ); 510 | name = MainStoryboard.storyboard; 511 | sourceTree = ""; 512 | }; 513 | E7D4D4EA16FA4F1F00408B8A /* InfoPlist.strings */ = { 514 | isa = PBXVariantGroup; 515 | children = ( 516 | E7D4D4EB16FA4F1F00408B8A /* en */, 517 | ); 518 | name = InfoPlist.strings; 519 | sourceTree = ""; 520 | }; 521 | /* End PBXVariantGroup section */ 522 | 523 | /* Begin XCBuildConfiguration section */ 524 | E7D4D4F016FA4F1F00408B8A /* Debug */ = { 525 | isa = XCBuildConfiguration; 526 | buildSettings = { 527 | ALWAYS_SEARCH_USER_PATHS = NO; 528 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 529 | CLANG_CXX_LIBRARY = "libc++"; 530 | CLANG_ENABLE_OBJC_ARC = YES; 531 | CLANG_WARN_CONSTANT_CONVERSION = YES; 532 | CLANG_WARN_EMPTY_BODY = YES; 533 | CLANG_WARN_ENUM_CONVERSION = YES; 534 | CLANG_WARN_INT_CONVERSION = YES; 535 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 536 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 537 | COPY_PHASE_STRIP = NO; 538 | GCC_C_LANGUAGE_STANDARD = gnu99; 539 | GCC_DYNAMIC_NO_PIC = NO; 540 | GCC_OPTIMIZATION_LEVEL = 0; 541 | GCC_PREPROCESSOR_DEFINITIONS = ( 542 | "DEBUG=1", 543 | "$(inherited)", 544 | ); 545 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 546 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 547 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 548 | GCC_WARN_UNUSED_VARIABLE = YES; 549 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 550 | ONLY_ACTIVE_ARCH = YES; 551 | SDKROOT = iphoneos; 552 | }; 553 | name = Debug; 554 | }; 555 | E7D4D4F116FA4F1F00408B8A /* Release */ = { 556 | isa = XCBuildConfiguration; 557 | buildSettings = { 558 | ALWAYS_SEARCH_USER_PATHS = NO; 559 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 560 | CLANG_CXX_LIBRARY = "libc++"; 561 | CLANG_ENABLE_OBJC_ARC = YES; 562 | CLANG_WARN_CONSTANT_CONVERSION = YES; 563 | CLANG_WARN_EMPTY_BODY = YES; 564 | CLANG_WARN_ENUM_CONVERSION = YES; 565 | CLANG_WARN_INT_CONVERSION = YES; 566 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 567 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 568 | COPY_PHASE_STRIP = YES; 569 | GCC_C_LANGUAGE_STANDARD = gnu99; 570 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 571 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 572 | GCC_WARN_UNUSED_VARIABLE = YES; 573 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 574 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 575 | SDKROOT = iphoneos; 576 | VALIDATE_PRODUCT = YES; 577 | }; 578 | name = Release; 579 | }; 580 | E7D4D4F316FA4F1F00408B8A /* Debug */ = { 581 | isa = XCBuildConfiguration; 582 | buildSettings = { 583 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 584 | GCC_PREFIX_HEADER = "CardGame/CardGame-Prefix.pch"; 585 | INFOPLIST_FILE = "CardGame/CardGame-Info.plist"; 586 | PRODUCT_NAME = "$(TARGET_NAME)"; 587 | WRAPPER_EXTENSION = app; 588 | }; 589 | name = Debug; 590 | }; 591 | E7D4D4F416FA4F1F00408B8A /* Release */ = { 592 | isa = XCBuildConfiguration; 593 | buildSettings = { 594 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 595 | GCC_PREFIX_HEADER = "CardGame/CardGame-Prefix.pch"; 596 | INFOPLIST_FILE = "CardGame/CardGame-Info.plist"; 597 | PRODUCT_NAME = "$(TARGET_NAME)"; 598 | WRAPPER_EXTENSION = app; 599 | }; 600 | name = Release; 601 | }; 602 | E7D4D4F616FA4F1F00408B8A /* Debug */ = { 603 | isa = XCBuildConfiguration; 604 | buildSettings = { 605 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/CardGame.app/CardGame"; 606 | FRAMEWORK_SEARCH_PATHS = ( 607 | "\"$(SDKROOT)/Developer/Library/Frameworks\"", 608 | "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", 609 | ); 610 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 611 | GCC_PREFIX_HEADER = "CardGame/CardGame-Prefix.pch"; 612 | INFOPLIST_FILE = "CardGameTests/CardGameTests-Info.plist"; 613 | PRODUCT_NAME = "$(TARGET_NAME)"; 614 | TEST_HOST = "$(BUNDLE_LOADER)"; 615 | WRAPPER_EXTENSION = octest; 616 | }; 617 | name = Debug; 618 | }; 619 | E7D4D4F716FA4F1F00408B8A /* Release */ = { 620 | isa = XCBuildConfiguration; 621 | buildSettings = { 622 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/CardGame.app/CardGame"; 623 | FRAMEWORK_SEARCH_PATHS = ( 624 | "\"$(SDKROOT)/Developer/Library/Frameworks\"", 625 | "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", 626 | ); 627 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 628 | GCC_PREFIX_HEADER = "CardGame/CardGame-Prefix.pch"; 629 | INFOPLIST_FILE = "CardGameTests/CardGameTests-Info.plist"; 630 | PRODUCT_NAME = "$(TARGET_NAME)"; 631 | TEST_HOST = "$(BUNDLE_LOADER)"; 632 | WRAPPER_EXTENSION = octest; 633 | }; 634 | name = Release; 635 | }; 636 | /* End XCBuildConfiguration section */ 637 | 638 | /* Begin XCConfigurationList section */ 639 | E7D4D4B516FA4F1F00408B8A /* Build configuration list for PBXProject "CardGame" */ = { 640 | isa = XCConfigurationList; 641 | buildConfigurations = ( 642 | E7D4D4F016FA4F1F00408B8A /* Debug */, 643 | E7D4D4F116FA4F1F00408B8A /* Release */, 644 | ); 645 | defaultConfigurationIsVisible = 0; 646 | defaultConfigurationName = Release; 647 | }; 648 | E7D4D4F216FA4F1F00408B8A /* Build configuration list for PBXNativeTarget "CardGame" */ = { 649 | isa = XCConfigurationList; 650 | buildConfigurations = ( 651 | E7D4D4F316FA4F1F00408B8A /* Debug */, 652 | E7D4D4F416FA4F1F00408B8A /* Release */, 653 | ); 654 | defaultConfigurationIsVisible = 0; 655 | defaultConfigurationName = Release; 656 | }; 657 | E7D4D4F516FA4F1F00408B8A /* Build configuration list for PBXNativeTarget "CardGameTests" */ = { 658 | isa = XCConfigurationList; 659 | buildConfigurations = ( 660 | E7D4D4F616FA4F1F00408B8A /* Debug */, 661 | E7D4D4F716FA4F1F00408B8A /* Release */, 662 | ); 663 | defaultConfigurationIsVisible = 0; 664 | defaultConfigurationName = Release; 665 | }; 666 | /* End XCConfigurationList section */ 667 | }; 668 | rootObject = E7D4D4B216FA4F1F00408B8A /* Project object */; 669 | } 670 | -------------------------------------------------------------------------------- /CardGame/en.lproj/MainStoryboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 31 | 47 | 63 | 79 | 95 | 111 | 127 | 143 | 159 | 175 | 191 | 207 | 223 | 239 | 255 | 271 | 278 | 285 | 299 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 484 | 503 | 522 | 541 | 560 | 579 | 598 | 617 | 636 | 655 | 674 | 693 | 712 | 731 | 750 | 769 | 788 | 807 | 826 | 845 | 864 | 883 | 902 | 921 | 928 | 935 | 949 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | --------------------------------------------------------------------------------