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