Source/CheckersGame.m
author Jens Alfke <jens@mooseyard.com>
Wed Jul 09 17:07:45 2008 -0700 (2008-07-09)
changeset 15 73f8c889f053
parent 12 4e567e11f45f
child 16 28392c9a969f
permissions -rw-r--r--
More tweaks, including a "reversed" property for Grids to show the second player's perspective without turning the pieces upside-down.
     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         [self setNumberOfPlayers: 2];
    57         
    58         PreloadSound(@"Tink");
    59         PreloadSound(@"Funk");
    60         PreloadSound(@"Blow");
    61         PreloadSound(@"Pop");
    62     }
    63     return self;
    64 }
    65 
    66 - (CGImageRef) iconForPlayer: (int)playerNum
    67 {
    68     return GetCGImageNamed( playerNum==0 ?@"Green.png" :@"Red.png" );
    69 }
    70 
    71 - (Piece*) pieceForPlayer: (int)playerNum
    72 {
    73     Piece *p = [[Piece alloc] init];
    74     p.bounds = CGRectMake(0,0,floor(_grid.spacing.width),floor(_grid.spacing.height));
    75     p.style = (playerNum ?kPieceStyle2 :kPieceStyle1);
    76     p.owner = [self.players objectAtIndex: playerNum];
    77     p.name = playerNum ?@"2" :@"1";
    78     return [p autorelease];
    79 }
    80 
    81 - (void) makeKing: (Piece*)piece
    82 {
    83     piece.scale = 1.4;
    84     piece.tag = YES;        // tag property stores the 'king' flag
    85     piece.name = piece.owner.index ?@"4" :@"3";
    86 }
    87 
    88 - (void) setUpBoard
    89 {
    90     RectGrid *grid = [[RectGrid alloc] initWithRows: 8 columns: 8 frame: _board.bounds];
    91     _grid = grid;
    92     [_board addSublayer: _grid];
    93     CGPoint pos = _grid.position;
    94     pos.x = floor((_board.bounds.size.width-grid.frame.size.width)/2);
    95     grid.position = pos;
    96     grid.allowsMoves = YES;
    97     grid.allowsCaptures = NO;
    98     grid.cellColor = CreateGray(0.0, 0.25);
    99     grid.altCellColor = CreateGray(1.0, 0.25);
   100     grid.lineColor = nil;
   101     grid.reversed = ! [[self.players objectAtIndex: 0] isLocal];
   102 
   103     for( int i=0; i<32; i++ ) {
   104         int row = i/4;
   105         [_grid addCellAtRow: row column: 2*(i%4) + (row&1)];
   106     }
   107     [_grid release]; // its superlayer still retains it
   108 }
   109 
   110 - (NSString*) initialStateString            {return @"111111111111--------222222222222";}
   111 - (NSString*) stateString                   {return _grid.stateString;}
   112 - (void) setStateString: (NSString*)state   {_grid.stateString = state;}
   113 
   114 - (Piece*) makePieceNamed: (NSString*)name
   115 {
   116     int which = [name characterAtIndex: 0] - '1';
   117     if( which >=0 && which < 4 ) {
   118         Piece *piece = [self pieceForPlayer: (which & 1)];
   119         if( which & 2 ) 
   120             [self makeKing: piece];
   121         return piece;
   122     } else
   123         return nil;
   124 }
   125 
   126 
   127 - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   128 {
   129     Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
   130     if( bit.tag )
   131         if( dst==src.bl || dst==src.br || dst==src.l || dst==src.r
   132            || (src.bl.bit.unfriendly && dst==src.bl.bl) || (src.br.bit.unfriendly && dst==src.br.br) )
   133             return YES;    
   134     return dst==src.fl || dst==src.fr
   135         || (src.fl.bit.unfriendly && dst==src.fl.fl) || (src.fr.bit.unfriendly && dst==src.fr.fr);
   136 }
   137 
   138 - (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   139 {
   140     Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
   141     int playerIndex = self.currentPlayer.index;
   142     
   143     Turn *turn = self.currentTurn;
   144     if( turn.move.length==0 )
   145         [turn addToMove: src.name];
   146     [turn addToMove: @"-"];
   147     [turn addToMove: dst.name];
   148     
   149     BOOL isKing = bit.tag;
   150     PlaySound(isKing ?@"Funk" :@"Tink");
   151 
   152     // "King" a piece that made it to the last row:
   153     if( !isKing && (dst.row == (playerIndex ?0 :7)) ) {
   154         PlaySound(@"Blow");
   155         [self makeKing: (Piece*)bit];
   156         [turn addToMove: @"*"];
   157         // don't set isKing flag - piece can't jump again after being kinged.
   158     }
   159 
   160     // Check for a capture:
   161     NSArray *line = [src lineToCell: dst inclusive: NO];
   162     if( line.count==1 ) {
   163         Square *capture = [line objectAtIndex: 0];
   164         [capture destroyBit];
   165         [turn addToMove: @"!"];
   166         PlaySound(@"Pop");
   167         
   168         // Now check if another capture is possible. If so, don't end the turn:
   169         if( (dst.fl.bit.unfriendly && dst.fl.fl.empty) || (dst.fr.bit.unfriendly && dst.fr.fr.empty) )
   170             return;
   171         if( isKing )
   172             if( (dst.bl.bit.unfriendly && dst.bl.bl.empty) || (dst.br.bit.unfriendly && dst.br.br.empty) )
   173                 return;
   174     }
   175     
   176     [self endTurn];
   177 }
   178 
   179 - (Player*) checkForWinner
   180 {
   181     NSCountedSet *remaining = _grid.countPiecesByPlayer;
   182     if( remaining.count==1 )
   183         return [remaining anyObject];
   184     else
   185         return nil;
   186 }
   187 
   188 
   189 - (BOOL) applyMoveString: (NSString*)move
   190 {
   191     GridCell *src = nil;
   192     for( NSString *ident in [move componentsSeparatedByString: @"-"] ) {
   193         while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] )
   194             ident = [ident substringToIndex: ident.length-1];
   195         GridCell *dst = [_grid cellWithName: ident];
   196         if( dst == nil )
   197             return NO;
   198         if( src && ! [self animateMoveFrom: src to: dst] )
   199             return NO;
   200         src = dst;
   201     }
   202     return YES;
   203 }
   204 
   205 
   206 @end