1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/Source/KlondikeGame.m Fri Mar 07 11:43:02 2008 -0800
1.3 @@ -0,0 +1,181 @@
1.4 +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
1.5 + http://developer.apple.com/samplecode/GeekGameBoard/
1.6 + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
1.7 +
1.8 + Redistribution and use in source and binary forms, with or without modification, are permitted
1.9 + provided that the following conditions are met:
1.10 +
1.11 + * Redistributions of source code must retain the above copyright notice, this list of conditions
1.12 + and the following disclaimer.
1.13 + * Redistributions in binary form must reproduce the above copyright notice, this list of
1.14 + conditions and the following disclaimer in the documentation and/or other materials provided
1.15 + with the distribution.
1.16 +
1.17 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
1.18 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
1.19 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
1.20 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1.21 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1.22 + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1.23 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
1.24 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.25 +*/
1.26 +#import "KlondikeGame.h"
1.27 +#import "Deck.h"
1.28 +#import "PlayingCard.h"
1.29 +#import "Stack.h"
1.30 +
1.31 +
1.32 +#define kStackHeight 500
1.33 +
1.34 +
1.35 +/** WARNING: THIS CODE REQUIRES GARBAGE COLLECTION!
1.36 + ** This sample application uses Objective-C 2.0 garbage collection.
1.37 + ** Therefore, the source code in this file does NOT perform manual object memory management.
1.38 + ** If you reuse any of this code in a process that isn't garbage collected, you will need to
1.39 + ** add all necessary retain/release/autorelease calls, and implement -dealloc methods,
1.40 + ** otherwise unpleasant leakage will occur!
1.41 + **/
1.42 +
1.43 +
1.44 +@implementation KlondikeGame
1.45 +
1.46 +
1.47 +- (id) initWithBoard: (CALayer*)board
1.48 +{
1.49 + self = [super initWithBoard: board];
1.50 + if (self != nil) {
1.51 + [self setNumberOfPlayers: 1];
1.52 +
1.53 + _deck = [[Deck alloc] initWithCardsOfClass: [PlayingCard class]];
1.54 + [_deck shuffle];
1.55 + _deck.position = CGPointMake(kCardWidth/2+16,kCardHeight/2+16);
1.56 + [board addSublayer: _deck];
1.57 +
1.58 + _sink = [[Deck alloc] init];
1.59 + _sink.position = CGPointMake(3*kCardWidth/2+32,kCardHeight/2+16);
1.60 + [board addSublayer: _sink];
1.61 +
1.62 + for( CardSuit suit=kSuitClubs; suit<=kSuitSpades; suit++ ) {
1.63 + Deck *aces = [[Deck alloc] init];
1.64 + aces.position = CGPointMake(kCardWidth/2+16+(kCardWidth+16)*(suit%2),
1.65 + 120+kCardHeight+(kCardHeight+16)*(suit/2));
1.66 + [board addSublayer: aces];
1.67 + _aces[suit] = aces;
1.68 + }
1.69 +
1.70 + for( int s=0; s<7; s++ ) {
1.71 + Stack *stack = [[Stack alloc] initWithStartPos: CGPointMake(kCardWidth/2,
1.72 + kStackHeight-kCardHeight/2.0)
1.73 + spacing: CGSizeMake(0,-22)];
1.74 + stack.frame = CGRectMake(260+s*(kCardWidth+16),16, kCardWidth,kStackHeight);
1.75 + stack.backgroundColor = nil;
1.76 + stack.dragAsStacks = YES;
1.77 + [board addSublayer: stack];
1.78 +
1.79 + // According to the rules, one card should be added to each stack in turn, instead
1.80 + // of populating entire stacks one at a time. However, if one trusts the Deck's
1.81 + // -shuffle method (which uses the random() function, seeded with a high-entropy
1.82 + // cryptographically-strong value), it shouldn't make any difference :-)
1.83 + for( int c=0; c<=s; c++ )
1.84 + [stack addBit: [_deck removeTopCard]];
1.85 + ((Card*)stack.bits.lastObject).faceUp = YES;
1.86 + }
1.87 +
1.88 + [self nextPlayer];
1.89 + }
1.90 + return self;
1.91 +}
1.92 +
1.93 +
1.94 +- (BOOL) clickedBit: (Bit*)bit
1.95 +{
1.96 + if( [bit isKindOfClass: [Card class]] ) {
1.97 + Card *card = (Card*)bit;
1.98 + if( card.holder == _deck ) {
1.99 + // Click on deck deals 3 cards to the sink:
1.100 + for( int i=0; i<3; i++ ) {
1.101 + Card *card = [_deck removeTopCard];
1.102 + if( card ) {
1.103 + [_sink addCard: card];
1.104 + card.faceUp = YES;
1.105 + }
1.106 + }
1.107 + [self endTurn];
1.108 + return YES;
1.109 + } else if( card.holder == _sink ) {
1.110 + // Clicking the sink when the deck is empty re-deals:
1.111 + if( _deck.empty ) {
1.112 + [_deck addCards: [_sink removeAllCards]];
1.113 + [_deck flip];
1.114 + [self endTurn];
1.115 + return YES;
1.116 + }
1.117 + } else {
1.118 + // Click on a card elsewhere turns it face-up:
1.119 + if( ! card.faceUp ) {
1.120 + card.faceUp = YES;
1.121 + return YES;
1.122 + }
1.123 + }
1.124 + }
1.125 + return NO;
1.126 +}
1.127 +
1.128 +
1.129 +- (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)src
1.130 +{
1.131 + if( [bit isKindOfClass: [DraggedStack class]] ) {
1.132 + Card *bottomSrc = [[(DraggedStack*)bit bits] objectAtIndex: 0];
1.133 + if( ! bottomSrc.faceUp )
1.134 + return NO;
1.135 + }
1.136 + return YES;
1.137 +}
1.138 +
1.139 +
1.140 +- (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)src to: (id<BitHolder>)dst
1.141 +{
1.142 + if( src==_deck || dst==_deck || dst==_sink )
1.143 + return NO;
1.144 +
1.145 + // Find the bottom card being moved, and the top card it's moving onto:
1.146 + PlayingCard *bottomSrc;
1.147 + if( [bit isKindOfClass: [DraggedStack class]] )
1.148 + bottomSrc = [[(DraggedStack*)bit bits] objectAtIndex: 0];
1.149 + else
1.150 + bottomSrc = (PlayingCard*)bit;
1.151 +
1.152 + PlayingCard *topDst;
1.153 + if( [dst isKindOfClass: [Deck class]] ) {
1.154 + // Dragging to an ace pile:
1.155 + if( ! [bit isKindOfClass: [Card class]] )
1.156 + return NO;
1.157 + topDst = (PlayingCard*) ((Deck*)dst).topCard;
1.158 + if( topDst == nil )
1.159 + return bottomSrc.rank == kRankAce;
1.160 + else
1.161 + return bottomSrc.suit == topDst.suit && bottomSrc.rank == topDst.rank+1;
1.162 +
1.163 + } else {
1.164 + // Dragging to a card stack:
1.165 + topDst = (PlayingCard*) ((Stack*)dst).topBit;
1.166 + if( topDst == nil )
1.167 + return bottomSrc.rank == kRankKing;
1.168 + else
1.169 + return bottomSrc.color != topDst.color && bottomSrc.rank == topDst.rank-1;
1.170 + }
1.171 +}
1.172 +
1.173 +
1.174 +- (Player*) checkForWinner
1.175 +{
1.176 + for( CardSuit suit=kSuitClubs; suit<=kSuitSpades; suit++ )
1.177 + if( _aces[suit].cards.count < 13 )
1.178 + return nil;
1.179 + return _currentPlayer;
1.180 +}
1.181 +
1.182 +
1.183 +
1.184 +@end