Source/CheckersGame.m
author Jens Alfke <jens@mooseyard.com>
Sun Feb 06 16:31:03 2011 -0800 (2011-02-06)
changeset 29 0b1c315ffc64
parent 23 efe5d4523a23
permissions -rw-r--r--
Minor compiler-compatibility fixes.
     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 #define kKingScale 1.4
    31 
    32 
    33 @implementation CheckersGame
    34 
    35 
    36 static NSMutableDictionary *kPieceStyle1, *kPieceStyle2;
    37 
    38 + (void) initialize
    39 {
    40     if( self == [CheckersGame class] ) {
    41         kPieceStyle1 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
    42                         (id)GetCGImageNamed(@"Green.png"), @"contents",
    43                         kCAGravityResizeAspect, @"contentsGravity",
    44                         kCAFilterLinear, @"minificationFilter",
    45                         nil];
    46         kPieceStyle2 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
    47                         (id)GetCGImageNamed(@"Red.png"), @"contents",
    48                         kCAGravityResizeAspect, @"contentsGravity",
    49                         kCAFilterLinear, @"minificationFilter",
    50                         nil];
    51     }
    52 }
    53 
    54 
    55 - (id) init
    56 {
    57     self = [super init];
    58     if (self != nil) {
    59         [self setNumberOfPlayers: 2];
    60     }
    61     return self;
    62 }
    63 
    64 - (CGImageRef) iconForPlayer: (int)playerNum
    65 {
    66     return GetCGImageNamed( playerNum==0 ?@"Green.png" :@"Red.png" );
    67 }
    68 
    69 - (void) _transformPiece: (Piece*)piece
    70 {
    71     CGFloat scale = piece.tag ?kKingScale :1.0;
    72     piece.transform = CATransform3DMakeScale(scale, scale/cos(self.tablePerspectiveAngle), scale);
    73     piece.anchorPoint = CGPointMake(0.5, 0.5*cos(self.tablePerspectiveAngle));
    74 }
    75 
    76 - (Piece*) pieceForPlayer: (int)playerNum
    77 {
    78     Piece *p = [[Piece alloc] init];
    79     p.bounds = CGRectMake(0,0,floor(_board.spacing.width),floor(_board.spacing.height));
    80     p.style = (playerNum ?kPieceStyle2 :kPieceStyle1);
    81     p.owner = [self.players objectAtIndex: playerNum];
    82     p.name = playerNum ?@"2" :@"1";
    83     [self _transformPiece: p];
    84     return [p autorelease];
    85 }
    86 
    87 - (void) perspectiveChanged
    88 {
    89     for( GridCell *cell in _board.cells ) {
    90         Piece *piece = (Piece*) cell.bit;
    91         if( piece )
    92             [self _transformPiece: piece];
    93     }
    94 }
    95 
    96 - (void) makeKing: (Piece*)piece
    97 {
    98     piece.tag = YES;        // tag property stores the 'king' flag
    99     piece.name = piece.owner.index ?@"4" :@"3";
   100     [self _transformPiece: piece];
   101 }
   102 
   103 - (void) setUpBoard
   104 {
   105     PreloadSound(@"Tink");
   106     PreloadSound(@"Funk");
   107     PreloadSound(@"Blow");
   108     PreloadSound(@"Pop");
   109 
   110     RectGrid *board = [[RectGrid alloc] initWithRows: 8 columns: 8 frame: _table.bounds];
   111     _board = board;
   112     [_table addSublayer: _board];
   113     CGPoint pos = _board.position;
   114     pos.x = floor((_table.bounds.size.width-board.frame.size.width)/2);
   115     board.position = pos;
   116     board.allowsMoves = YES;
   117     board.allowsCaptures = NO;
   118     board.cellColor    = CreateGray(0.0, 0.5);
   119     board.altCellColor = CreateGray(1.0, 0.25);
   120     board.lineColor = nil;
   121     board.reversed = ! [[self.players objectAtIndex: 0] isLocal];
   122 
   123     for( int i=0; i<32; i++ ) {
   124         int row = i/4;
   125         [_board addCellAtRow: row column: 2*(i%4) + (row&1)];
   126     }
   127     [_board release]; // its superlayer still retains it
   128 }
   129 
   130 - (NSString*) initialStateString            {return @"111111111111--------222222222222";}
   131 - (NSString*) stateString                   {return _board.stateString;}
   132 - (void) setStateString: (NSString*)state   {_board.stateString = state;}
   133 
   134 - (Piece*) makePieceNamed: (NSString*)name
   135 {
   136     int which = [name characterAtIndex: 0] - '1';
   137     if( which >=0 && which < 4 ) {
   138         Piece *piece = [self pieceForPlayer: (which & 1)];
   139         if( which & 2 ) 
   140             [self makeKing: piece];
   141         return piece;
   142     } else
   143         return nil;
   144 }
   145 
   146 
   147 - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   148 {
   149     Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
   150     if( bit.tag )
   151         if( dst==src.bl || dst==src.br || dst==src.l || dst==src.r
   152            || (src.bl.bit.unfriendly && dst==src.bl.bl) || (src.br.bit.unfriendly && dst==src.br.br) )
   153             return YES;    
   154     return dst==src.fl || dst==src.fr
   155         || (src.fl.bit.unfriendly && dst==src.fl.fl) || (src.fr.bit.unfriendly && dst==src.fr.fr);
   156 }
   157 
   158 - (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   159 {
   160     Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
   161     int playerIndex = self.currentPlayer.index;
   162     
   163     Turn *turn = self.currentTurn;
   164     if( turn.move.length==0 )
   165         [turn addToMove: src.name];
   166     [turn addToMove: @"-"];
   167     [turn addToMove: dst.name];
   168     
   169     BOOL isKing = bit.tag;
   170     PlaySound(isKing ?@"Funk" :@"Tink");
   171 
   172     // "King" a piece that made it to the last row:
   173     if( !isKing && (dst.row == (playerIndex ?0 :7)) ) {
   174         PlaySound(@"Blow");
   175         [self makeKing: (Piece*)bit];
   176         [turn addToMove: @"*"];
   177         // don't set isKing flag - piece can't jump again after being kinged.
   178     }
   179 
   180     // Check for a capture:
   181     NSArray *line = [src lineToCell: dst inclusive: NO];
   182     if( line.count==1 ) {
   183         Square *capture = [line objectAtIndex: 0];
   184         [capture destroyBit];
   185         [turn addToMove: @"!"];
   186         PlaySound(@"Pop");
   187         
   188         // Now check if another capture is possible. If so, don't end the turn:
   189         if( (dst.fl.bit.unfriendly && dst.fl.fl.empty) || (dst.fr.bit.unfriendly && dst.fr.fr.empty) )
   190             return;
   191         if( isKing )
   192             if( (dst.bl.bit.unfriendly && dst.bl.bl.empty) || (dst.br.bit.unfriendly && dst.br.br.empty) )
   193                 return;
   194     }
   195     
   196     [self endTurn];
   197 }
   198 
   199 #pragma mark -
   200 #pragma mark CHECK FOR WIN:
   201 
   202 static BOOL canOpponentMoveOrJump( GridCell *first, GridCell *second ) {
   203     return first.empty || (first.bit.friendly && second.empty);
   204 }
   205 
   206 - (BOOL) canOpponentMoveFrom: (GridCell*)src
   207 {
   208     if( ! src.bit.unfriendly )
   209         return NO;
   210     Square *square = (Square*)src;
   211     if( square.bit.tag )           // remember, it's opponent's piece, so directions are reversed
   212         if( canOpponentMoveOrJump(square.fl,square.fl.fl) || canOpponentMoveOrJump(square.fr,square.fr.fr) )
   213             return YES;
   214     return canOpponentMoveOrJump(square.bl,square.bl.bl) || canOpponentMoveOrJump(square.br,square.br.br);
   215 }
   216 
   217 - (Player*) checkForWinner
   218 {
   219     for( GridCell *cell in _board.cells )
   220         if( [self canOpponentMoveFrom: cell] ) {
   221             //NSLog(@"Checkers: %@ can move from %@",self.currentPlayer.nextPlayer,cell);
   222             return nil;
   223         }
   224     return self.currentPlayer;
   225 }
   226 
   227 
   228 - (BOOL) applyMoveString: (NSString*)move
   229 {
   230     GridCell *src = nil;
   231     for( NSString *ident in [move componentsSeparatedByString: @"-"] ) {
   232         while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] )
   233             ident = [ident substringToIndex: ident.length-1];
   234         GridCell *dst = [_board cellWithName: ident];
   235         if( dst == nil )
   236             return NO;
   237         if( src && ! [self animateMoveFrom: src to: dst] )
   238             return NO;
   239         src = dst;
   240     }
   241     return YES;
   242 }
   243 
   244 
   245 @end