Source/CheckersGame.m
author Jens Alfke <jens@mooseyard.com>
Wed May 28 12:47:10 2008 -0700 (2008-05-28)
changeset 8 45c82a071aca
parent 1 3eb7be1dd7b6
child 9 a59acc683080
permissions -rw-r--r--
* Got it working with latest iPhone SDK.
* Fixed some text alignment issues that showed up on PlayingCards.
* Working on persistence and move-tracking for Game.
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@7
    33
- (Piece*) pieceForPlayer: (int)playerNum
jens@0
    34
{
jens@7
    35
    Piece *p = [[Piece alloc] initWithImageNamed: (playerNum==0 ?@"Green Ball.png" :@"Red Ball.png") 
jens@7
    36
                                           scale: floor(_grid.spacing.width * 0.8)];
jens@7
    37
    p.owner = [self.players objectAtIndex: playerNum];
jens@7
    38
    p.name = playerNum ?@"2" :@"1";
jens@7
    39
    return [p autorelease];
jens@0
    40
}
jens@0
    41
jens@0
    42
- (Grid*) x_makeGrid
jens@0
    43
{
jens@0
    44
    RectGrid *grid = [[[RectGrid alloc] initWithRows: 8 columns: 8 frame: _board.bounds] autorelease];
jens@7
    45
    _grid = grid;
jens@7
    46
    CGPoint pos = _grid.position;
jens@0
    47
    pos.x = floor((_board.bounds.size.width-grid.frame.size.width)/2);
jens@0
    48
    grid.position = pos;
jens@0
    49
    grid.allowsMoves = YES;
jens@0
    50
    grid.allowsCaptures = NO;
jens@1
    51
    grid.cellColor = CreateGray(0.0, 0.25);
jens@1
    52
    grid.altCellColor = CreateGray(1.0, 0.25);
jens@0
    53
    grid.lineColor = nil;
jens@7
    54
jens@7
    55
    [grid addAllCells];
jens@7
    56
    for( int i=0; i<32; i++ ) {
jens@7
    57
        int row = i/4;
jens@7
    58
        [_cells addObject: [_grid cellAtRow: row column: 2*(i%4) + (row&1)]];
jens@7
    59
    }
jens@7
    60
    self.stateString = @"111111111111--------222222222222";
jens@0
    61
    return grid;
jens@0
    62
}
jens@0
    63
jens@0
    64
jens@1
    65
- (id) initWithBoard: (GGBLayer*)board
jens@0
    66
{
jens@0
    67
    self = [super initWithBoard: board];
jens@0
    68
    if (self != nil) {
jens@0
    69
        [self setNumberOfPlayers: 2];
jens@7
    70
        _cells = [[NSMutableArray alloc] init];
jens@7
    71
        [self x_makeGrid];
jens@7
    72
        [board addSublayer: _grid];
jens@0
    73
        [self nextPlayer];
jens@7
    74
        
jens@7
    75
        PreloadSound(@"Tink");
jens@7
    76
        PreloadSound(@"Funk");
jens@7
    77
        PreloadSound(@"Blow");
jens@7
    78
        PreloadSound(@"Pop");
jens@0
    79
    }
jens@0
    80
    return self;
jens@0
    81
}
jens@0
    82
jens@7
    83
- (void) dealloc
jens@7
    84
{
jens@7
    85
    [_cells release];
jens@7
    86
    [_grid release];
jens@7
    87
    [super dealloc];
jens@7
    88
}
jens@7
    89
jens@7
    90
jens@7
    91
- (NSString*) stateString
jens@7
    92
{
jens@7
    93
    unichar state[_cells.count];
jens@7
    94
    int i = 0;
jens@7
    95
    for( GridCell *cell in _cells ) {
jens@7
    96
        NSString *ident = cell.bit.name;
jens@7
    97
        if( ident )
jens@7
    98
            state[i++] = [ident characterAtIndex: 0];
jens@7
    99
        else
jens@7
   100
            state[i++] = '-';
jens@7
   101
    }
jens@7
   102
    return [NSString stringWithCharacters: state length: i];
jens@7
   103
}
jens@7
   104
jens@7
   105
- (void) setStateString: (NSString*)state
jens@7
   106
{
jens@7
   107
    _numPieces[0] = _numPieces[1] = 0;
jens@7
   108
    int i = 0;
jens@7
   109
    for( GridCell *cell in _cells ) {
jens@7
   110
        Piece *piece;
jens@7
   111
        switch( [state characterAtIndex: i++] ) {
jens@7
   112
            case '1': piece = [self pieceForPlayer: 0]; _numPieces[0]++; break;
jens@7
   113
            case '2': piece = [self pieceForPlayer: 1]; _numPieces[1]++; break;
jens@7
   114
            default:  piece = nil; break;
jens@7
   115
        }
jens@7
   116
        cell.bit = piece;
jens@7
   117
    }    
jens@7
   118
}
jens@7
   119
jens@0
   120
jens@0
   121
- (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
jens@0
   122
{
jens@0
   123
    Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
jens@0
   124
    if( [bit valueForKey: @"King"] )
jens@0
   125
        if( dst==src.bl || dst==src.br || dst==src.l || dst==src.r
jens@0
   126
           || (src.bl.bit.unfriendly && dst==src.bl.bl) || (src.br.bit.unfriendly && dst==src.br.br) )
jens@0
   127
            return YES;    
jens@0
   128
    return dst==src.fl || dst==src.fr
jens@0
   129
        || (src.fl.bit.unfriendly && dst==src.fl.fl) || (src.fr.bit.unfriendly && dst==src.fr.fr);
jens@0
   130
}
jens@0
   131
jens@0
   132
- (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
jens@0
   133
{
jens@0
   134
    Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
jens@0
   135
    int playerIndex = self.currentPlayer.index;
jens@7
   136
    
jens@7
   137
    if( self.currentMove.length==0 )
jens@7
   138
        [self.currentMove appendString: src.name];
jens@7
   139
    [self.currentMove appendString: dst.name];
jens@7
   140
    
jens@0
   141
    BOOL isKing = ([bit valueForKey: @"King"] != nil);
jens@1
   142
    PlaySound(isKing ?@"Funk" :@"Tink");
jens@0
   143
jens@0
   144
    // "King" a piece that made it to the last row:
jens@0
   145
    if( dst.row == (playerIndex ?0 :7) )
jens@0
   146
        if( ! isKing ) {
jens@1
   147
            PlaySound(@"Blow");
jens@0
   148
            bit.scale = 1.4;
jens@0
   149
            [bit setValue: @"King" forKey: @"King"];
jens@0
   150
            // don't set isKing flag - piece can't jump again after being kinged.
jens@0
   151
        }
jens@0
   152
jens@0
   153
    // Check for a capture:
jens@0
   154
    Square *capture = nil;
jens@0
   155
    if(dst==src.fl.fl)
jens@0
   156
        capture = src.fl;
jens@0
   157
    else if(dst==src.fr.fr)
jens@0
   158
        capture = src.fr;
jens@0
   159
    else if(dst==src.bl.bl)
jens@0
   160
        capture = src.bl;
jens@0
   161
    else if(dst==src.br.br)
jens@0
   162
        capture = src.br;
jens@0
   163
    
jens@0
   164
    if( capture ) {
jens@1
   165
        PlaySound(@"Pop");
jens@0
   166
        Bit *bit = capture.bit;
jens@0
   167
        _numPieces[bit.owner.index]--;
jens@0
   168
        [bit destroy];
jens@0
   169
        
jens@0
   170
        // Now check if another capture is possible. If so, don't end the turn:
jens@0
   171
        if( (dst.fl.bit.unfriendly && dst.fl.fl.empty) || (dst.fr.bit.unfriendly && dst.fr.fr.empty) )
jens@0
   172
            return;
jens@0
   173
        if( isKing )
jens@0
   174
            if( (dst.bl.bit.unfriendly && dst.bl.bl.empty) || (dst.br.bit.unfriendly && dst.br.br.empty) )
jens@0
   175
                return;
jens@0
   176
    }
jens@0
   177
    
jens@0
   178
    [self endTurn];
jens@0
   179
}
jens@0
   180
jens@0
   181
- (Player*) checkForWinner
jens@0
   182
{
jens@0
   183
    // Whoever runs out of pieces loses:
jens@0
   184
    if( _numPieces[0]==0 )
jens@0
   185
        return [self.players objectAtIndex: 1];
jens@0
   186
    else if( _numPieces[1]==0 )
jens@0
   187
        return [self.players objectAtIndex: 0];
jens@0
   188
    else
jens@0
   189
        return nil;
jens@0
   190
}
jens@0
   191
jens@0
   192
jens@7
   193
- (BOOL) applyMoveString: (NSString*)move
jens@7
   194
{
jens@7
   195
    int length = move.length;
jens@7
   196
    if( length<4 || (length&1) )
jens@7
   197
        return NO;
jens@7
   198
    GridCell *src = nil;
jens@7
   199
    for( int i=0; i<length; i+=2 ) {
jens@7
   200
        NSString *ident = [move substringWithRange: NSMakeRange(i,2)];
jens@7
   201
        GridCell *dst = [_grid cellWithName: ident];
jens@7
   202
        if( i > 0 )
jens@7
   203
            if( ! [self animateMoveFrom: src to: dst] )
jens@7
   204
                return NO;
jens@7
   205
        src = dst;
jens@7
   206
    }
jens@7
   207
    return YES;
jens@7
   208
}
jens@7
   209
jens@7
   210
jens@0
   211
@end