jens@0: /* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. jens@0: http://developer.apple.com/samplecode/GeekGameBoard/ jens@0: Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. jens@0: jens@0: Redistribution and use in source and binary forms, with or without modification, are permitted jens@0: provided that the following conditions are met: jens@0: jens@0: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@0: and the following disclaimer. jens@0: * Redistributions in binary form must reproduce the above copyright notice, this list of jens@0: conditions and the following disclaimer in the documentation and/or other materials provided jens@0: with the distribution. jens@0: jens@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@0: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@0: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@0: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@0: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@0: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@0: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@0: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@0: */ jens@0: #import "CheckersGame.h" jens@0: #import "Grid.h" jens@0: #import "Piece.h" jens@0: #import "QuartzUtils.h" jens@1: #import "GGBUtils.h" jens@0: jens@0: jens@22: #define kKingScale 1.4 jens@22: jens@22: jens@0: @implementation CheckersGame jens@0: jens@0: jens@11: static NSMutableDictionary *kPieceStyle1, *kPieceStyle2; jens@11: jens@11: + (void) initialize jens@11: { jens@11: if( self == [CheckersGame class] ) { jens@11: kPieceStyle1 = [[NSMutableDictionary alloc] initWithObjectsAndKeys: jens@11: (id)GetCGImageNamed(@"Green.png"), @"contents", jens@11: kCAGravityResizeAspect, @"contentsGravity", jens@11: kCAFilterLinear, @"minificationFilter", jens@11: nil]; jens@11: kPieceStyle2 = [[NSMutableDictionary alloc] initWithObjectsAndKeys: jens@11: (id)GetCGImageNamed(@"Red.png"), @"contents", jens@11: kCAGravityResizeAspect, @"contentsGravity", jens@11: kCAFilterLinear, @"minificationFilter", jens@11: nil]; jens@11: } jens@11: } jens@11: jens@11: jens@10: - (id) init jens@10: { jens@10: self = [super init]; jens@10: if (self != nil) { jens@10: [self setNumberOfPlayers: 2]; jens@10: } jens@10: return self; jens@10: } jens@10: jens@10: - (CGImageRef) iconForPlayer: (int)playerNum jens@10: { jens@10: return GetCGImageNamed( playerNum==0 ?@"Green.png" :@"Red.png" ); jens@10: } jens@10: jens@22: - (void) _transformPiece: (Piece*)piece jens@22: { jens@22: CGFloat scale = piece.tag ?kKingScale :1.0; jens@22: piece.transform = CATransform3DMakeScale(scale, scale/cos(self.tablePerspectiveAngle), scale); jens@22: piece.anchorPoint = CGPointMake(0.5, 0.5*cos(self.tablePerspectiveAngle)); jens@22: } jens@22: jens@7: - (Piece*) pieceForPlayer: (int)playerNum jens@0: { jens@11: Piece *p = [[Piece alloc] init]; jens@16: p.bounds = CGRectMake(0,0,floor(_board.spacing.width),floor(_board.spacing.height)); jens@11: p.style = (playerNum ?kPieceStyle2 :kPieceStyle1); jens@7: p.owner = [self.players objectAtIndex: playerNum]; jens@7: p.name = playerNum ?@"2" :@"1"; jens@22: [self _transformPiece: p]; jens@7: return [p autorelease]; jens@0: } jens@0: jens@22: - (void) perspectiveChanged jens@22: { jens@22: for( GridCell *cell in _board.cells ) { jens@22: Piece *piece = (Piece*) cell.bit; jens@22: if( piece ) jens@22: [self _transformPiece: piece]; jens@22: } jens@22: } jens@22: jens@10: - (void) makeKing: (Piece*)piece jens@10: { jens@12: piece.tag = YES; // tag property stores the 'king' flag jens@10: piece.name = piece.owner.index ?@"4" :@"3"; jens@23: [self _transformPiece: piece]; jens@10: } jens@10: jens@10: - (void) setUpBoard jens@0: { jens@19: PreloadSound(@"Tink"); jens@19: PreloadSound(@"Funk"); jens@19: PreloadSound(@"Blow"); jens@19: PreloadSound(@"Pop"); jens@19: jens@16: RectGrid *board = [[RectGrid alloc] initWithRows: 8 columns: 8 frame: _table.bounds]; jens@16: _board = board; jens@16: [_table addSublayer: _board]; jens@16: CGPoint pos = _board.position; jens@16: pos.x = floor((_table.bounds.size.width-board.frame.size.width)/2); jens@16: board.position = pos; jens@16: board.allowsMoves = YES; jens@16: board.allowsCaptures = NO; jens@16: board.cellColor = CreateGray(0.0, 0.5); jens@16: board.altCellColor = CreateGray(1.0, 0.25); jens@16: board.lineColor = nil; jens@16: board.reversed = ! [[self.players objectAtIndex: 0] isLocal]; jens@7: jens@7: for( int i=0; i<32; i++ ) { jens@7: int row = i/4; jens@16: [_board addCellAtRow: row column: 2*(i%4) + (row&1)]; jens@7: } jens@16: [_board release]; // its superlayer still retains it jens@0: } jens@0: jens@12: - (NSString*) initialStateString {return @"111111111111--------222222222222";} jens@16: - (NSString*) stateString {return _board.stateString;} jens@16: - (void) setStateString: (NSString*)state {_board.stateString = state;} jens@12: jens@12: - (Piece*) makePieceNamed: (NSString*)name jens@7: { jens@12: int which = [name characterAtIndex: 0] - '1'; jens@12: if( which >=0 && which < 4 ) { jens@12: Piece *piece = [self pieceForPlayer: (which & 1)]; jens@12: if( which & 2 ) jens@12: [self makeKing: piece]; jens@12: return piece; jens@12: } else jens@12: return nil; jens@7: } jens@7: jens@0: jens@0: - (BOOL) canBit: (Bit*)bit moveFrom: (id)srcHolder to: (id)dstHolder jens@0: { jens@0: Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder; jens@12: if( bit.tag ) jens@0: if( dst==src.bl || dst==src.br || dst==src.l || dst==src.r jens@0: || (src.bl.bit.unfriendly && dst==src.bl.bl) || (src.br.bit.unfriendly && dst==src.br.br) ) jens@0: return YES; jens@0: return dst==src.fl || dst==src.fr jens@0: || (src.fl.bit.unfriendly && dst==src.fl.fl) || (src.fr.bit.unfriendly && dst==src.fr.fr); jens@0: } jens@0: jens@0: - (void) bit: (Bit*)bit movedFrom: (id)srcHolder to: (id)dstHolder jens@0: { jens@0: Square *src=(Square*)srcHolder, *dst=(Square*)dstHolder; jens@0: int playerIndex = self.currentPlayer.index; jens@7: jens@10: Turn *turn = self.currentTurn; jens@10: if( turn.move.length==0 ) jens@10: [turn addToMove: src.name]; jens@10: [turn addToMove: @"-"]; jens@10: [turn addToMove: dst.name]; jens@7: jens@12: BOOL isKing = bit.tag; jens@1: PlaySound(isKing ?@"Funk" :@"Tink"); jens@0: jens@0: // "King" a piece that made it to the last row: jens@12: if( !isKing && (dst.row == (playerIndex ?0 :7)) ) { jens@12: PlaySound(@"Blow"); jens@12: [self makeKing: (Piece*)bit]; jens@12: [turn addToMove: @"*"]; jens@12: // don't set isKing flag - piece can't jump again after being kinged. jens@12: } jens@0: jens@0: // Check for a capture: jens@12: NSArray *line = [src lineToCell: dst inclusive: NO]; jens@12: if( line.count==1 ) { jens@12: Square *capture = [line objectAtIndex: 0]; jens@10: [capture destroyBit]; jens@10: [turn addToMove: @"!"]; jens@12: PlaySound(@"Pop"); jens@0: jens@0: // Now check if another capture is possible. If so, don't end the turn: jens@0: if( (dst.fl.bit.unfriendly && dst.fl.fl.empty) || (dst.fr.bit.unfriendly && dst.fr.fr.empty) ) jens@0: return; jens@0: if( isKing ) jens@0: if( (dst.bl.bit.unfriendly && dst.bl.bl.empty) || (dst.br.bit.unfriendly && dst.br.br.empty) ) jens@0: return; jens@0: } jens@0: jens@0: [self endTurn]; jens@0: } jens@0: jens@20: #pragma mark - jens@20: #pragma mark CHECK FOR WIN: jens@20: jens@20: static BOOL canOpponentMoveOrJump( GridCell *first, GridCell *second ) { jens@20: return first.empty || (first.bit.friendly && second.empty); jens@20: } jens@20: jens@20: - (BOOL) canOpponentMoveFrom: (GridCell*)src jens@20: { jens@20: if( ! src.bit.unfriendly ) jens@20: return NO; jens@20: Square *square = (Square*)src; jens@20: if( square.bit.tag ) // remember, it's opponent's piece, so directions are reversed jens@20: if( canOpponentMoveOrJump(square.fl,square.fl.fl) || canOpponentMoveOrJump(square.fr,square.fr.fr) ) jens@20: return YES; jens@20: return canOpponentMoveOrJump(square.bl,square.bl.bl) || canOpponentMoveOrJump(square.br,square.br.br); jens@20: } jens@20: jens@0: - (Player*) checkForWinner jens@0: { jens@20: for( GridCell *cell in _board.cells ) jens@20: if( [self canOpponentMoveFrom: cell] ) { snej@25: //NSLog(@"Checkers: %@ can move from %@",self.currentPlayer.nextPlayer,cell); jens@20: return nil; jens@20: } jens@20: return self.currentPlayer; jens@0: } jens@0: jens@0: jens@7: - (BOOL) applyMoveString: (NSString*)move jens@7: { jens@7: GridCell *src = nil; jens@10: for( NSString *ident in [move componentsSeparatedByString: @"-"] ) { jens@10: while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] ) jens@10: ident = [ident substringToIndex: ident.length-1]; jens@16: GridCell *dst = [_board cellWithName: ident]; jens@10: if( dst == nil ) jens@10: return NO; jens@10: if( src && ! [self animateMoveFrom: src to: dst] ) jens@10: return NO; jens@7: src = dst; jens@7: } jens@7: return YES; jens@7: } jens@7: jens@7: jens@0: @end