jens@0: /* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. jens@0: http://developer.apple.com/samplecode/GeekGameBoard/ jens@0: Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. jens@0: jens@0: Redistribution and use in source and binary forms, with or without modification, are permitted jens@0: provided that the following conditions are met: jens@0: jens@0: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@0: and the following disclaimer. jens@0: * Redistributions in binary form must reproduce the above copyright notice, this list of jens@0: conditions and the following disclaimer in the documentation and/or other materials provided jens@0: with the distribution. jens@0: jens@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@0: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@0: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@0: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@0: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@0: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@0: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@0: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@0: */ jens@0: #import "KlondikeGame.h" jens@0: #import "Deck.h" jens@0: #import "PlayingCard.h" jens@0: #import "Stack.h" jens@0: jens@0: jens@0: #define kStackHeight 500 jens@0: jens@0: jens@0: /** WARNING: THIS CODE REQUIRES GARBAGE COLLECTION! jens@0: ** This sample application uses Objective-C 2.0 garbage collection. jens@0: ** Therefore, the source code in this file does NOT perform manual object memory management. jens@0: ** If you reuse any of this code in a process that isn't garbage collected, you will need to jens@0: ** add all necessary retain/release/autorelease calls, and implement -dealloc methods, jens@0: ** otherwise unpleasant leakage will occur! jens@0: **/ jens@0: jens@0: jens@0: @implementation KlondikeGame jens@0: jens@0: jens@0: - (id) initWithBoard: (CALayer*)board jens@0: { jens@0: self = [super initWithBoard: board]; jens@0: if (self != nil) { jens@0: [self setNumberOfPlayers: 1]; jens@0: jens@0: _deck = [[Deck alloc] initWithCardsOfClass: [PlayingCard class]]; jens@0: [_deck shuffle]; jens@0: _deck.position = CGPointMake(kCardWidth/2+16,kCardHeight/2+16); jens@0: [board addSublayer: _deck]; jens@0: jens@0: _sink = [[Deck alloc] init]; jens@0: _sink.position = CGPointMake(3*kCardWidth/2+32,kCardHeight/2+16); jens@0: [board addSublayer: _sink]; jens@0: jens@0: for( CardSuit suit=kSuitClubs; suit<=kSuitSpades; suit++ ) { jens@0: Deck *aces = [[Deck alloc] init]; jens@0: aces.position = CGPointMake(kCardWidth/2+16+(kCardWidth+16)*(suit%2), jens@0: 120+kCardHeight+(kCardHeight+16)*(suit/2)); jens@0: [board addSublayer: aces]; jens@0: _aces[suit] = aces; jens@0: } jens@0: jens@0: for( int s=0; s<7; s++ ) { jens@0: Stack *stack = [[Stack alloc] initWithStartPos: CGPointMake(kCardWidth/2, jens@0: kStackHeight-kCardHeight/2.0) jens@0: spacing: CGSizeMake(0,-22)]; jens@0: stack.frame = CGRectMake(260+s*(kCardWidth+16),16, kCardWidth,kStackHeight); jens@0: stack.backgroundColor = nil; jens@0: stack.dragAsStacks = YES; jens@0: [board addSublayer: stack]; jens@0: jens@0: // According to the rules, one card should be added to each stack in turn, instead jens@0: // of populating entire stacks one at a time. However, if one trusts the Deck's jens@0: // -shuffle method (which uses the random() function, seeded with a high-entropy jens@0: // cryptographically-strong value), it shouldn't make any difference :-) jens@0: for( int c=0; c<=s; c++ ) jens@0: [stack addBit: [_deck removeTopCard]]; jens@0: ((Card*)stack.bits.lastObject).faceUp = YES; jens@0: } jens@0: jens@0: [self nextPlayer]; jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: jens@0: - (BOOL) clickedBit: (Bit*)bit jens@0: { jens@0: if( [bit isKindOfClass: [Card class]] ) { jens@0: Card *card = (Card*)bit; jens@0: if( card.holder == _deck ) { jens@0: // Click on deck deals 3 cards to the sink: jens@0: for( int i=0; i<3; i++ ) { jens@0: Card *card = [_deck removeTopCard]; jens@0: if( card ) { jens@0: [_sink addCard: card]; jens@0: card.faceUp = YES; jens@0: } jens@0: } jens@0: [self endTurn]; jens@0: return YES; jens@0: } else if( card.holder == _sink ) { jens@0: // Clicking the sink when the deck is empty re-deals: jens@0: if( _deck.empty ) { jens@0: [_deck addCards: [_sink removeAllCards]]; jens@0: [_deck flip]; jens@0: [self endTurn]; jens@0: return YES; jens@0: } jens@0: } else { jens@0: // Click on a card elsewhere turns it face-up: jens@0: if( ! card.faceUp ) { jens@0: card.faceUp = YES; jens@0: return YES; jens@0: } jens@0: } jens@0: } jens@0: return NO; jens@0: } jens@0: jens@0: jens@0: - (BOOL) canBit: (Bit*)bit moveFrom: (id)src jens@0: { jens@0: if( [bit isKindOfClass: [DraggedStack class]] ) { jens@0: Card *bottomSrc = [[(DraggedStack*)bit bits] objectAtIndex: 0]; jens@0: if( ! bottomSrc.faceUp ) jens@0: return NO; jens@0: } jens@0: return YES; jens@0: } jens@0: jens@0: jens@0: - (BOOL) canBit: (Bit*)bit moveFrom: (id)src to: (id)dst jens@0: { jens@0: if( src==_deck || dst==_deck || dst==_sink ) jens@0: return NO; jens@0: jens@0: // Find the bottom card being moved, and the top card it's moving onto: jens@0: PlayingCard *bottomSrc; jens@0: if( [bit isKindOfClass: [DraggedStack class]] ) jens@0: bottomSrc = [[(DraggedStack*)bit bits] objectAtIndex: 0]; jens@0: else jens@0: bottomSrc = (PlayingCard*)bit; jens@0: jens@0: PlayingCard *topDst; jens@0: if( [dst isKindOfClass: [Deck class]] ) { jens@0: // Dragging to an ace pile: jens@0: if( ! [bit isKindOfClass: [Card class]] ) jens@0: return NO; jens@0: topDst = (PlayingCard*) ((Deck*)dst).topCard; jens@0: if( topDst == nil ) jens@0: return bottomSrc.rank == kRankAce; jens@0: else jens@0: return bottomSrc.suit == topDst.suit && bottomSrc.rank == topDst.rank+1; jens@0: jens@0: } else { jens@0: // Dragging to a card stack: jens@0: topDst = (PlayingCard*) ((Stack*)dst).topBit; jens@0: if( topDst == nil ) jens@0: return bottomSrc.rank == kRankKing; jens@0: else jens@0: return bottomSrc.color != topDst.color && bottomSrc.rank == topDst.rank-1; jens@0: } jens@0: } jens@0: jens@0: jens@0: - (Player*) checkForWinner jens@0: { jens@0: for( CardSuit suit=kSuitClubs; suit<=kSuitSpades; suit++ ) jens@0: if( _aces[suit].cards.count < 13 ) jens@0: return nil; jens@0: return _currentPlayer; jens@0: } jens@0: jens@0: jens@0: jens@0: @end