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.
5 Redistribution and use in source and binary forms, with or without modification, are permitted
6 provided that the following conditions are met:
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.
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.
23 #import "CheckersGame.h"
26 #import "QuartzUtils.h"
30 @implementation CheckersGame
37 _cells = [[NSMutableArray alloc] init];
38 [self setNumberOfPlayers: 2];
40 PreloadSound(@"Tink");
41 PreloadSound(@"Funk");
42 PreloadSound(@"Blow");
48 - (CGImageRef) iconForPlayer: (int)playerNum
50 return GetCGImageNamed( playerNum==0 ?@"Green.png" :@"Red.png" );
53 - (Piece*) pieceForPlayer: (int)playerNum
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];
62 - (void) makeKing: (Piece*)piece
65 [piece setValue: @"King" forKey: @"King"];
66 piece.name = piece.owner.index ?@"4" :@"3";
71 RectGrid *grid = [[[RectGrid alloc] initWithRows: 8 columns: 8 frame: _board.bounds] autorelease];
73 [_board addSublayer: _grid];
74 CGPoint pos = _grid.position;
75 pos.x = floor((_board.bounds.size.width-grid.frame.size.width)/2);
77 grid.allowsMoves = YES;
78 grid.allowsCaptures = NO;
79 grid.cellColor = CreateGray(0.0, 0.25);
80 grid.altCellColor = CreateGray(1.0, 0.25);
84 [_cells removeAllObjects];
85 for( int i=0; i<32; i++ ) {
87 [_cells addObject: [_grid cellAtRow: row column: 2*(i%4) + (row&1)]];
99 - (NSString*) initialStateString
101 return @"111111111111--------222222222222";
104 - (NSString*) stateString
106 unichar state[_cells.count];
108 for( GridCell *cell in _cells ) {
109 NSString *ident = cell.bit.name;
111 state[i++] = [ident characterAtIndex: 0];
115 return [NSString stringWithCharacters: state length: i];
118 - (void) setStateString: (NSString*)state
120 _numPieces[0] = _numPieces[1] = 0;
122 for( GridCell *cell in _cells ) {
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]++;
130 [self makeKing: piece];
138 - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
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) )
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);
149 - (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
151 Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder;
152 int playerIndex = self.currentPlayer.index;
154 Turn *turn = self.currentTurn;
155 if( turn.move.length==0 )
156 [turn addToMove: src.name];
157 [turn addToMove: @"-"];
158 [turn addToMove: dst.name];
160 BOOL isKing = ([bit valueForKey: @"King"] != nil);
161 PlaySound(isKing ?@"Funk" :@"Tink");
163 // "King" a piece that made it to the last row:
164 if( dst.row == (playerIndex ?0 :7) )
167 [self makeKing: (Piece*)bit];
168 [turn addToMove: @"*"];
169 // don't set isKing flag - piece can't jump again after being kinged.
172 // Check for a capture:
173 Square *capture = nil;
176 else if(dst==src.fr.fr)
178 else if(dst==src.bl.bl)
180 else if(dst==src.br.br)
185 _numPieces[capture.bit.owner.index]--;
186 [capture destroyBit];
187 [turn addToMove: @"!"];
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) )
193 if( (dst.bl.bit.unfriendly && dst.bl.bl.empty) || (dst.br.bit.unfriendly && dst.br.br.empty) )
200 - (Player*) checkForWinner
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];
212 - (BOOL) applyMoveString: (NSString*)move
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];
221 if( src && ! [self animateMoveFrom: src to: dst] )