Source/CheckersGame.m
author Jens Alfke <jens@mooseyard.com>
Sat Jul 05 17:46:43 2008 -0700 (2008-07-05)
changeset 11 436cbdf56810
parent 10 6c78cc6bd7a6
child 12 4e567e11f45f
permissions -rw-r--r--
* Improved drag-and-drop (supports CandyBar)
* Fixed DiscPiece
* Inheritable layer styles
etc.
     1 /*  This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
     2     http://developer.apple.com/samplecode/GeekGameBoard/
     3     Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
     4 
     5     Redistribution and use in source and binary forms, with or without modification, are permitted
     6     provided that the following conditions are met:
     7 
     8     * Redistributions of source code must retain the above copyright notice, this list of conditions
     9       and the following disclaimer.
    10     * Redistributions in binary form must reproduce the above copyright notice, this list of
    11       conditions and the following disclaimer in the documentation and/or other materials provided
    12       with the distribution.
    13 
    14     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
    15     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
    16     FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
    17     BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    18     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
    19     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
    20     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
    21     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    22 */
    23 #import "CheckersGame.h"
    24 #import "Grid.h"
    25 #import "Piece.h"
    26 #import "QuartzUtils.h"
    27 #import "GGBUtils.h"
    28 
    29 
    30 @implementation CheckersGame
    31 
    32 
    33 static NSMutableDictionary *kPieceStyle1, *kPieceStyle2;
    34 
    35 + (void) initialize
    36 {
    37     if( self == [CheckersGame class] ) {
    38         kPieceStyle1 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
    39                         (id)GetCGImageNamed(@"Green.png"), @"contents",
    40                         kCAGravityResizeAspect, @"contentsGravity",
    41                         kCAFilterLinear, @"minificationFilter",
    42                         nil];
    43         kPieceStyle2 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
    44                         (id)GetCGImageNamed(@"Red.png"), @"contents",
    45                         kCAGravityResizeAspect, @"contentsGravity",
    46                         kCAFilterLinear, @"minificationFilter",
    47                         nil];
    48     }
    49 }
    50 
    51 
    52 - (id) init
    53 {
    54     self = [super init];
    55     if (self != nil) {
    56         _cells = [[NSMutableArray alloc] init];
    57         [self setNumberOfPlayers: 2];
    58         
    59         PreloadSound(@"Tink");
    60         PreloadSound(@"Funk");
    61         PreloadSound(@"Blow");
    62         PreloadSound(@"Pop");
    63     }
    64     return self;
    65 }
    66 
    67 - (CGImageRef) iconForPlayer: (int)playerNum
    68 {
    69     return GetCGImageNamed( playerNum==0 ?@"Green.png" :@"Red.png" );
    70 }
    71 
    72 - (Piece*) pieceForPlayer: (int)playerNum
    73 {
    74     Piece *p = [[Piece alloc] init];
    75     p.bounds = CGRectMake(0,0,floor(_grid.spacing.width),floor(_grid.spacing.height));
    76     p.style = (playerNum ?kPieceStyle2 :kPieceStyle1);
    77     p.owner = [self.players objectAtIndex: playerNum];
    78     p.name = playerNum ?@"2" :@"1";
    79     return [p autorelease];
    80 }
    81 
    82 - (void) makeKing: (Piece*)piece
    83 {
    84     piece.scale = 1.4;
    85     [piece setValue: @"King" forKey: @"King"];
    86     piece.name = piece.owner.index ?@"4" :@"3";
    87 }
    88 
    89 - (void) setUpBoard
    90 {
    91     RectGrid *grid = [[RectGrid alloc] initWithRows: 8 columns: 8 frame: _board.bounds];
    92     _grid = grid;
    93     [_board addSublayer: _grid];
    94     CGPoint pos = _grid.position;
    95     pos.x = floor((_board.bounds.size.width-grid.frame.size.width)/2);
    96     grid.position = pos;
    97     grid.allowsMoves = YES;
    98     grid.allowsCaptures = NO;
    99     grid.cellColor = CreateGray(0.0, 0.25);
   100     grid.altCellColor = CreateGray(1.0, 0.25);
   101     grid.lineColor = nil;
   102 
   103     [grid addAllCells];
   104     [_cells removeAllObjects];
   105     for( int i=0; i<32; i++ ) {
   106         int row = i/4;
   107         [_cells addObject: [_grid cellAtRow: row column: 2*(i%4) + (row&1)]];
   108     }
   109 }
   110 
   111 - (void) dealloc
   112 {
   113     [_cells release];
   114     [_grid release];
   115     [super dealloc];
   116 }
   117 
   118 
   119 - (NSString*) initialStateString
   120 {
   121     return @"111111111111--------222222222222";
   122 }
   123 
   124 - (NSString*) stateString
   125 {
   126     unichar state[_cells.count];
   127     int i = 0;
   128     for( GridCell *cell in _cells ) {
   129         NSString *ident = cell.bit.name;
   130         if( ident )
   131             state[i++] = [ident characterAtIndex: 0];
   132         else
   133             state[i++] = '-';
   134     }
   135     return [NSString stringWithCharacters: state length: i];
   136 }
   137 
   138 - (void) setStateString: (NSString*)state
   139 {
   140     _numPieces[0] = _numPieces[1] = 0;
   141     int i = 0;
   142     for( GridCell *cell in _cells ) {
   143         Piece *piece;
   144         int which = [state characterAtIndex: i++] - '1';
   145         if( which >=0 && which < 4 ) {
   146             int player = (which & 1);
   147             piece = [self pieceForPlayer: player];
   148             _numPieces[player]++;
   149             if( which & 2 ) 
   150                 [self makeKing: piece];
   151         } else
   152             piece = nil;
   153         cell.bit = piece;
   154     }    
   155 }
   156 
   157 
   158 - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   159 {
   160     Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
   161     if( [bit valueForKey: @"King"] )
   162         if( dst==src.bl || dst==src.br || dst==src.l || dst==src.r
   163            || (src.bl.bit.unfriendly && dst==src.bl.bl) || (src.br.bit.unfriendly && dst==src.br.br) )
   164             return YES;    
   165     return dst==src.fl || dst==src.fr
   166         || (src.fl.bit.unfriendly && dst==src.fl.fl) || (src.fr.bit.unfriendly && dst==src.fr.fr);
   167 }
   168 
   169 - (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   170 {
   171     Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
   172     int playerIndex = self.currentPlayer.index;
   173     
   174     Turn *turn = self.currentTurn;
   175     if( turn.move.length==0 )
   176         [turn addToMove: src.name];
   177     [turn addToMove: @"-"];
   178     [turn addToMove: dst.name];
   179     
   180     BOOL isKing = ([bit valueForKey: @"King"] != nil);
   181     PlaySound(isKing ?@"Funk" :@"Tink");
   182 
   183     // "King" a piece that made it to the last row:
   184     if( dst.row == (playerIndex ?0 :7) )
   185         if( ! isKing ) {
   186             PlaySound(@"Blow");
   187             [self makeKing: (Piece*)bit];
   188             [turn addToMove: @"*"];
   189             // don't set isKing flag - piece can't jump again after being kinged.
   190         }
   191 
   192     // Check for a capture:
   193     Square *capture = nil;
   194     if(dst==src.fl.fl)
   195         capture = src.fl;
   196     else if(dst==src.fr.fr)
   197         capture = src.fr;
   198     else if(dst==src.bl.bl)
   199         capture = src.bl;
   200     else if(dst==src.br.br)
   201         capture = src.br;
   202     
   203     if( capture ) {
   204         PlaySound(@"Pop");
   205         _numPieces[capture.bit.owner.index]--;
   206         [capture destroyBit];
   207         [turn addToMove: @"!"];
   208         
   209         // Now check if another capture is possible. If so, don't end the turn:
   210         if( (dst.fl.bit.unfriendly && dst.fl.fl.empty) || (dst.fr.bit.unfriendly && dst.fr.fr.empty) )
   211             return;
   212         if( isKing )
   213             if( (dst.bl.bit.unfriendly && dst.bl.bl.empty) || (dst.br.bit.unfriendly && dst.br.br.empty) )
   214                 return;
   215     }
   216     
   217     [self endTurn];
   218 }
   219 
   220 - (Player*) checkForWinner
   221 {
   222     // Whoever runs out of pieces loses:
   223     if( _numPieces[0]==0 )
   224         return [self.players objectAtIndex: 1];
   225     else if( _numPieces[1]==0 )
   226         return [self.players objectAtIndex: 0];
   227     else
   228         return nil;
   229 }
   230 
   231 
   232 - (BOOL) applyMoveString: (NSString*)move
   233 {
   234     GridCell *src = nil;
   235     for( NSString *ident in [move componentsSeparatedByString: @"-"] ) {
   236         while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] )
   237             ident = [ident substringToIndex: ident.length-1];
   238         GridCell *dst = [_grid cellWithName: ident];
   239         if( dst == nil )
   240             return NO;
   241         if( src && ! [self animateMoveFrom: src to: dst] )
   242             return NO;
   243         src = dst;
   244     }
   245     return YES;
   246 }
   247 
   248 
   249 @end