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 "Game.h" jens@0: #import "Bit.h" jens@7: #import "BitHolder.h" jens@7: #import "QuartzUtils.h" jens@0: jens@0: jens@0: @interface Game () jens@0: @property (copy) NSArray *players; jens@0: @property (assign) Player *currentPlayer, *winner; jens@0: @end jens@0: jens@0: jens@0: @implementation Game jens@0: jens@0: jens@8: + (NSString*) identifier jens@0: { jens@0: NSString* name = [self description]; jens@0: if( [name hasSuffix: @"Game"] ) jens@0: name = [name substringToIndex: name.length-4]; jens@0: return name; jens@0: } jens@0: jens@0: jens@8: + (NSString*) displayName jens@8: { jens@8: return [self identifier]; jens@8: } jens@8: jens@8: jens@8: - (id) initWithUniqueID: (NSString*)uuid jens@8: { jens@8: NSParameterAssert(uuid); jens@8: self = [super init]; jens@8: if (self != nil) { jens@8: _uniqueID = [uuid copy]; jens@8: _board = [[GGBLayer alloc] init]; jens@8: // Store a pointer to myself as the value of the "Game" property jens@8: // of my root layer. (CALayers can have arbitrary KV properties stored into them.) jens@8: // This is used by the -[CALayer game] category method defined below, to find the Game. jens@8: [_board setValue: self forKey: @"Game"]; jens@8: jens@8: _currentMove = [[NSMutableString alloc] init]; jens@8: } jens@8: return self; jens@8: } jens@8: jens@8: - (id) init jens@8: { jens@8: CFUUIDRef uuidRef = CFUUIDCreate(NULL); jens@8: NSString* uuid = (id) CFUUIDCreateString(NULL,uuidRef); jens@8: self = [self initWithUniqueID: uuid]; jens@8: CFRelease(uuid); jens@8: CFRelease(uuidRef); jens@8: return self; jens@8: } jens@8: jens@1: - (id) initWithBoard: (GGBLayer*)board jens@0: { jens@8: self = [self init]; jens@0: if (self != nil) { jens@7: _states = [[NSMutableArray alloc] init]; jens@7: _moves = [[NSMutableArray alloc] init]; jens@0: _board = [board retain]; jens@0: [board setValue: self forKey: @"Game"]; jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: jens@0: - (void) dealloc jens@0: { jens@0: [_board release]; jens@0: [_players release]; jens@7: [_currentMove release]; jens@7: [_states release]; jens@7: [_moves release]; jens@0: [super dealloc]; jens@0: } jens@0: jens@0: jens@7: @synthesize players=_players, currentPlayer=_currentPlayer, winner=_winner, jens@8: currentMove=_currentMove, states=_states, moves=_moves, uniqueID=_uniqueID; jens@0: jens@0: jens@0: - (void) setNumberOfPlayers: (unsigned)n jens@0: { jens@0: NSMutableArray *players = [NSMutableArray arrayWithCapacity: n]; jens@0: for( int i=1; i<=n; i++ ) { jens@0: Player *player = [[Player alloc] initWithGame: self]; jens@0: player.name = [NSString stringWithFormat: @"Player %i",i]; jens@0: [players addObject: player]; jens@0: [player release]; jens@0: } jens@0: self.winner = nil; jens@0: self.currentPlayer = nil; jens@0: self.players = players; jens@0: } jens@0: jens@0: jens@7: - (void) addToMove: (NSString*)str; jens@7: { jens@7: [_currentMove appendString: str]; jens@7: } jens@7: jens@7: jens@7: - (BOOL) _rememberState jens@7: { jens@7: if( self.isLatestTurn ) { jens@7: [_states addObject: self.stateString]; jens@7: return YES; jens@7: } else jens@7: return NO; jens@7: } jens@7: jens@7: jens@0: - (void) nextPlayer jens@0: { jens@7: BOOL latestTurn = [self _rememberState]; jens@0: if( ! _currentPlayer ) { jens@0: NSLog(@"*** The %@ Begins! ***", self.class); jens@0: self.currentPlayer = [_players objectAtIndex: 0]; jens@0: } else { jens@0: self.currentPlayer = _currentPlayer.nextPlayer; jens@7: if( latestTurn ) { jens@7: [self willChangeValueForKey: @"currentTurn"]; jens@7: _currentTurn++; jens@7: [self didChangeValueForKey: @"currentTurn"]; jens@7: } jens@0: } jens@0: NSLog(@"Current player is %@",_currentPlayer); jens@0: } jens@0: jens@0: jens@0: - (void) endTurn jens@0: { jens@7: NSLog(@"--- End of turn (move was '%@')", _currentMove); jens@7: if( self.isLatestTurn ) { jens@8: NSString *move = [[_currentMove copy] autorelease]; jens@8: [_currentMove setString: @""]; jens@7: [self willChangeValueForKey: @"maxTurn"]; jens@8: [_moves addObject: move]; jens@7: [self didChangeValueForKey: @"maxTurn"]; jens@7: } jens@7: jens@0: Player *winner = [self checkForWinner]; jens@0: if( winner ) { jens@0: NSLog(@"*** The %@ Ends! The winner is %@ ! ***", self.class, winner); jens@7: [self _rememberState]; jens@0: self.winner = winner; jens@0: } else jens@0: [self nextPlayer]; jens@0: } jens@0: jens@0: jens@8: #pragma mark - jens@8: #pragma mark STORED TURNS: jens@8: jens@8: jens@7: - (unsigned) maxTurn jens@7: { jens@7: return _moves.count; jens@7: } jens@7: jens@7: - (unsigned) currentTurn jens@7: { jens@7: return _currentTurn; jens@7: } jens@7: jens@7: - (void) setCurrentTurn: (unsigned)turn jens@7: { jens@7: NSParameterAssert(turn<=self.maxTurn); jens@7: if( turn != _currentTurn ) { jens@7: if( turn==_currentTurn+1 ) { jens@7: [self applyMoveString: [_moves objectAtIndex: _currentTurn]]; jens@7: } else { jens@7: [CATransaction begin]; jens@7: [CATransaction setValue:(id)kCFBooleanTrue jens@7: forKey:kCATransactionDisableActions]; jens@7: self.stateString = [_states objectAtIndex: turn]; jens@7: [CATransaction commit]; jens@7: } jens@7: _currentTurn = turn; jens@7: self.currentPlayer = [_players objectAtIndex: (turn % _players.count)]; jens@7: } jens@7: } jens@7: jens@7: jens@7: - (BOOL) isLatestTurn jens@7: { jens@7: return _currentTurn == MAX(_states.count,1)-1; jens@7: } jens@7: jens@7: jens@7: - (BOOL) animateMoveFrom: (BitHolder*)src to: (BitHolder*)dst jens@7: { jens@7: if( src==nil || dst==nil || dst==src ) jens@7: return NO; jens@7: Bit *bit = [src canDragBit: src.bit]; jens@7: if( ! bit || ! [dst canDropBit: bit atPoint: GetCGRectCenter(dst.bounds)] jens@7: || ! [self canBit: bit moveFrom: src to: dst] ) jens@7: return NO; jens@7: jens@7: ChangeSuperlayer(bit, _board.superlayer, -1); jens@7: bit.pickedUp = YES; jens@7: dst.highlighted = YES; jens@7: [bit performSelector: @selector(setPickedUp:) withObject:nil afterDelay: 0.15]; jens@7: CGPoint endPosition = [dst convertPoint: GetCGRectCenter(dst.bounds) toLayer: bit.superlayer]; jens@7: [bit animateAndBlock: @"position" jens@8: #if TARGET_OS_IPHONE jens@8: from: [NSValue valueWithCGPoint: bit.position] jens@8: to: [NSValue valueWithCGPoint: endPosition] jens@8: #else jens@7: from: [NSValue valueWithPoint: NSPointFromCGPoint(bit.position)] jens@7: to: [NSValue valueWithPoint: NSPointFromCGPoint(endPosition)] jens@8: #endif jens@7: duration: 0.25]; jens@7: dst.bit = bit; jens@7: dst.highlighted = NO; jens@7: bit.pickedUp = NO; jens@7: jens@7: [src draggedBit: bit to: dst]; jens@7: [self bit: bit movedFrom: src to: dst]; jens@7: src = dst; jens@7: return YES; jens@7: } jens@7: jens@7: jens@0: #pragma mark - jens@0: #pragma mark GAMEPLAY METHODS TO BE OVERRIDDEN: jens@0: jens@0: jens@4: + (BOOL) landscapeOriented jens@4: { jens@4: return NO; jens@4: } jens@4: jens@4: jens@0: - (BOOL) canBit: (Bit*)bit moveFrom: (id)src jens@0: { jens@0: return YES; jens@0: } jens@0: jens@0: - (BOOL) canBit: (Bit*)bit moveFrom: (id)src to: (id)dst jens@0: { jens@0: return YES; jens@0: } jens@0: jens@0: - (void) bit: (Bit*)bit movedFrom: (id)src to: (id)dst jens@0: { jens@0: [self endTurn]; jens@0: } jens@0: jens@3: - (Bit*) bitToPlaceInHolder: (id)holder jens@3: { jens@3: return nil; jens@3: } jens@3: jens@3: jens@0: - (BOOL) clickedBit: (Bit*)bit jens@0: { jens@0: return YES; jens@0: } jens@0: jens@0: - (Player*) checkForWinner jens@0: { jens@0: return nil; jens@0: } jens@0: jens@0: jens@7: - (NSString*) stateString {return @"";} jens@7: - (void) setStateString: (NSString*)s { } jens@7: jens@7: - (BOOL) applyMoveString: (NSString*)move {return NO;} jens@7: jens@0: @end jens@0: jens@0: jens@0: jens@0: jens@0: @implementation Player jens@0: jens@0: jens@0: - (id) initWithGame: (Game*)game jens@0: { jens@0: self = [super init]; jens@0: if (self != nil) { jens@0: _game = game; jens@0: } jens@0: return self; jens@0: } jens@0: jens@8: - (id) initWithCoder: (NSCoder*)decoder jens@8: { jens@8: self = [self init]; jens@8: if( self ) { jens@8: _game = [decoder decodeObjectForKey: @"game"]; jens@8: _name = [[decoder decodeObjectForKey: @"name"] copy]; jens@8: } jens@8: return self; jens@8: } jens@8: jens@8: - (void) encodeWithCoder: (NSCoder*)coder jens@8: { jens@8: [coder encodeObject: _game forKey: @"game"]; jens@8: [coder encodeObject: _name forKey: @"name"]; jens@8: } jens@8: jens@8: - (void) dealloc jens@8: { jens@8: [_name release]; jens@8: [super dealloc]; jens@8: } jens@8: jens@0: jens@0: @synthesize game=_game, name=_name; jens@0: jens@0: - (BOOL) isCurrent {return self == _game.currentPlayer;} jens@0: - (BOOL) isFriendly {return self == _game.currentPlayer;} // could be overridden for games with partners jens@0: - (BOOL) isUnfriendly {return ! self.friendly;} jens@0: jens@0: - (int) index jens@0: { jens@0: return [_game.players indexOfObjectIdenticalTo: self]; jens@0: } jens@0: jens@0: - (Player*) nextPlayer jens@0: { jens@0: return [_game.players objectAtIndex: (self.index+1) % _game.players.count]; jens@0: } jens@0: jens@0: - (Player*) previousPlayer jens@0: { jens@0: return [_game.players objectAtIndex: (self.index-1) % _game.players.count]; jens@0: } jens@0: jens@0: - (NSString*) description jens@0: { jens@0: return [NSString stringWithFormat: @"%@[%@]", self.class,self.name]; jens@0: } jens@0: jens@0: @end jens@0: jens@0: jens@0: jens@0: jens@0: @implementation CALayer (Game) jens@0: jens@0: - (Game*) game jens@0: { jens@0: // The Game object stores a pointer to itself as the value of the "Game" property jens@0: // of its root layer. (CALayers can have arbitrary KV properties stored into them.) jens@0: for( CALayer *layer = self; layer; layer=layer.superlayer ) { jens@0: Game *game = [layer valueForKey: @"Game"]; jens@0: if( game ) jens@0: return game; jens@0: } jens@0: NSAssert1(NO,@"Couldn't look up Game from %@",self); jens@0: return nil; jens@0: } jens@0: jens@0: @end