Source/CheckersGame.m
author Jens Alfke <jens@mooseyard.com>
Thu Jul 03 17:44:30 2008 -0700 (2008-07-03)
changeset 10 6c78cc6bd7a6
parent 9 a59acc683080
child 11 436cbdf56810
permissions -rw-r--r--
Lots of reworking. Completed support for game history, including Turn class. Changed Game API around quite a bit.
     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 - (id) init
    34 {
    35     self = [super init];
    36     if (self != nil) {
    37         _cells = [[NSMutableArray alloc] init];
    38         [self setNumberOfPlayers: 2];
    39         
    40         PreloadSound(@"Tink");
    41         PreloadSound(@"Funk");
    42         PreloadSound(@"Blow");
    43         PreloadSound(@"Pop");
    44     }
    45     return self;
    46 }
    47 
    48 - (CGImageRef) iconForPlayer: (int)playerNum
    49 {
    50     return GetCGImageNamed( playerNum==0 ?@"Green.png" :@"Red.png" );
    51 }
    52 
    53 - (Piece*) pieceForPlayer: (int)playerNum
    54 {
    55     Piece *p = [[Piece alloc] initWithImageNamed: (playerNum==0 ?@"Green.png" :@"Red.png") 
    56                                            scale: floor(_grid.spacing.width * 1.0)];
    57     p.owner = [self.players objectAtIndex: playerNum];
    58     p.name = playerNum ?@"2" :@"1";
    59     return [p autorelease];
    60 }
    61 
    62 - (void) makeKing: (Piece*)piece
    63 {
    64     piece.scale = 1.4;
    65     [piece setValue: @"King" forKey: @"King"];
    66     piece.name = piece.owner.index ?@"4" :@"3";
    67 }
    68 
    69 - (void) setUpBoard
    70 {
    71     RectGrid *grid = [[[RectGrid alloc] initWithRows: 8 columns: 8 frame: _board.bounds] autorelease];
    72     _grid = grid;
    73     [_board addSublayer: _grid];
    74     CGPoint pos = _grid.position;
    75     pos.x = floor((_board.bounds.size.width-grid.frame.size.width)/2);
    76     grid.position = pos;
    77     grid.allowsMoves = YES;
    78     grid.allowsCaptures = NO;
    79     grid.cellColor = CreateGray(0.0, 0.25);
    80     grid.altCellColor = CreateGray(1.0, 0.25);
    81     grid.lineColor = nil;
    82 
    83     [grid addAllCells];
    84     [_cells removeAllObjects];
    85     for( int i=0; i<32; i++ ) {
    86         int row = i/4;
    87         [_cells addObject: [_grid cellAtRow: row column: 2*(i%4) + (row&1)]];
    88     }
    89 }
    90 
    91 - (void) dealloc
    92 {
    93     [_cells release];
    94     [_grid release];
    95     [super dealloc];
    96 }
    97 
    98 
    99 - (NSString*) initialStateString
   100 {
   101     return @"111111111111--------222222222222";
   102 }
   103 
   104 - (NSString*) stateString
   105 {
   106     unichar state[_cells.count];
   107     int i = 0;
   108     for( GridCell *cell in _cells ) {
   109         NSString *ident = cell.bit.name;
   110         if( ident )
   111             state[i++] = [ident characterAtIndex: 0];
   112         else
   113             state[i++] = '-';
   114     }
   115     return [NSString stringWithCharacters: state length: i];
   116 }
   117 
   118 - (void) setStateString: (NSString*)state
   119 {
   120     _numPieces[0] = _numPieces[1] = 0;
   121     int i = 0;
   122     for( GridCell *cell in _cells ) {
   123         Piece *piece;
   124         int which = [state characterAtIndex: i++] - '1';
   125         if( which >=0 && which < 4 ) {
   126             int player = (which & 1);
   127             piece = [self pieceForPlayer: player];
   128             _numPieces[player]++;
   129             if( which & 2 ) 
   130                 [self makeKing: piece];
   131         } else
   132             piece = nil;
   133         cell.bit = piece;
   134     }    
   135 }
   136 
   137 
   138 - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   139 {
   140     Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
   141     if( [bit valueForKey: @"King"] )
   142         if( dst==src.bl || dst==src.br || dst==src.l || dst==src.r
   143            || (src.bl.bit.unfriendly && dst==src.bl.bl) || (src.br.bit.unfriendly && dst==src.br.br) )
   144             return YES;    
   145     return dst==src.fl || dst==src.fr
   146         || (src.fl.bit.unfriendly && dst==src.fl.fl) || (src.fr.bit.unfriendly && dst==src.fr.fr);
   147 }
   148 
   149 - (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   150 {
   151     Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
   152     int playerIndex = self.currentPlayer.index;
   153     
   154     Turn *turn = self.currentTurn;
   155     if( turn.move.length==0 )
   156         [turn addToMove: src.name];
   157     [turn addToMove: @"-"];
   158     [turn addToMove: dst.name];
   159     
   160     BOOL isKing = ([bit valueForKey: @"King"] != nil);
   161     PlaySound(isKing ?@"Funk" :@"Tink");
   162 
   163     // "King" a piece that made it to the last row:
   164     if( dst.row == (playerIndex ?0 :7) )
   165         if( ! isKing ) {
   166             PlaySound(@"Blow");
   167             [self makeKing: (Piece*)bit];
   168             [turn addToMove: @"*"];
   169             // don't set isKing flag - piece can't jump again after being kinged.
   170         }
   171 
   172     // Check for a capture:
   173     Square *capture = nil;
   174     if(dst==src.fl.fl)
   175         capture = src.fl;
   176     else if(dst==src.fr.fr)
   177         capture = src.fr;
   178     else if(dst==src.bl.bl)
   179         capture = src.bl;
   180     else if(dst==src.br.br)
   181         capture = src.br;
   182     
   183     if( capture ) {
   184         PlaySound(@"Pop");
   185         _numPieces[capture.bit.owner.index]--;
   186         [capture destroyBit];
   187         [turn addToMove: @"!"];
   188         
   189         // Now check if another capture is possible. If so, don't end the turn:
   190         if( (dst.fl.bit.unfriendly && dst.fl.fl.empty) || (dst.fr.bit.unfriendly && dst.fr.fr.empty) )
   191             return;
   192         if( isKing )
   193             if( (dst.bl.bit.unfriendly && dst.bl.bl.empty) || (dst.br.bit.unfriendly && dst.br.br.empty) )
   194                 return;
   195     }
   196     
   197     [self endTurn];
   198 }
   199 
   200 - (Player*) checkForWinner
   201 {
   202     // Whoever runs out of pieces loses:
   203     if( _numPieces[0]==0 )
   204         return [self.players objectAtIndex: 1];
   205     else if( _numPieces[1]==0 )
   206         return [self.players objectAtIndex: 0];
   207     else
   208         return nil;
   209 }
   210 
   211 
   212 - (BOOL) applyMoveString: (NSString*)move
   213 {
   214     GridCell *src = nil;
   215     for( NSString *ident in [move componentsSeparatedByString: @"-"] ) {
   216         while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] )
   217             ident = [ident substringToIndex: ident.length-1];
   218         GridCell *dst = [_grid cellWithName: ident];
   219         if( dst == nil )
   220             return NO;
   221         if( src && ! [self animateMoveFrom: src to: dst] )
   222             return NO;
   223         src = dst;
   224     }
   225     return YES;
   226 }
   227 
   228 
   229 @end