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