Game class now tracks board state and moves, as strings, and can step through its history.
Fixed another bug in Go (you could drag your captured stones back to the board!)
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.
26 #import "QuartzUtils.h"
30 @property (copy) NSArray *players;
31 @property (assign) Player *currentPlayer, *winner;
38 + (NSString*) displayName
40 NSString* name = [self description];
41 if( [name hasSuffix: @"Game"] )
42 name = [name substringToIndex: name.length-4];
47 - (id) initWithBoard: (GGBLayer*)board
51 _states = [[NSMutableArray alloc] init];
52 _moves = [[NSMutableArray alloc] init];
53 _currentMove = [[NSMutableString alloc] init];
54 _board = [board retain];
55 // Store a pointer to myself as the value of the "Game" property
56 // of my root layer. (CALayers can have arbitrary KV properties stored into them.)
57 // This is used by the -[CALayer game] category method defined below, to find the Game.
58 [board setValue: self forKey: @"Game"];
68 [_currentMove release];
75 @synthesize players=_players, currentPlayer=_currentPlayer, winner=_winner,
76 currentMove=_currentMove, states=_states, moves=_moves;
79 - (void) setNumberOfPlayers: (unsigned)n
81 NSMutableArray *players = [NSMutableArray arrayWithCapacity: n];
82 for( int i=1; i<=n; i++ ) {
83 Player *player = [[Player alloc] initWithGame: self];
84 player.name = [NSString stringWithFormat: @"Player %i",i];
85 [players addObject: player];
89 self.currentPlayer = nil;
90 self.players = players;
94 - (void) addToMove: (NSString*)str;
96 [_currentMove appendString: str];
100 - (BOOL) _rememberState
102 if( self.isLatestTurn ) {
103 [_states addObject: self.stateString];
112 BOOL latestTurn = [self _rememberState];
113 if( ! _currentPlayer ) {
114 NSLog(@"*** The %@ Begins! ***", self.class);
115 self.currentPlayer = [_players objectAtIndex: 0];
117 self.currentPlayer = _currentPlayer.nextPlayer;
119 [self willChangeValueForKey: @"currentTurn"];
121 [self didChangeValueForKey: @"currentTurn"];
124 NSLog(@"Current player is %@",_currentPlayer);
130 NSLog(@"--- End of turn (move was '%@')", _currentMove);
131 if( self.isLatestTurn ) {
132 [self willChangeValueForKey: @"maxTurn"];
133 [_moves addObject: [[_currentMove copy] autorelease]];
134 [_currentMove setString: @""];
135 [self didChangeValueForKey: @"maxTurn"];
138 Player *winner = [self checkForWinner];
140 NSLog(@"*** The %@ Ends! The winner is %@ ! ***", self.class, winner);
141 [self _rememberState];
142 self.winner = winner;
153 - (unsigned) currentTurn
158 - (void) setCurrentTurn: (unsigned)turn
160 NSParameterAssert(turn<=self.maxTurn);
161 if( turn != _currentTurn ) {
162 if( turn==_currentTurn+1 ) {
163 [self applyMoveString: [_moves objectAtIndex: _currentTurn]];
165 [CATransaction begin];
166 [CATransaction setValue:(id)kCFBooleanTrue
167 forKey:kCATransactionDisableActions];
168 self.stateString = [_states objectAtIndex: turn];
169 [CATransaction commit];
172 self.currentPlayer = [_players objectAtIndex: (turn % _players.count)];
177 - (BOOL) isLatestTurn
179 return _currentTurn == MAX(_states.count,1)-1;
183 - (BOOL) animateMoveFrom: (BitHolder*)src to: (BitHolder*)dst
185 if( src==nil || dst==nil || dst==src )
187 Bit *bit = [src canDragBit: src.bit];
188 if( ! bit || ! [dst canDropBit: bit atPoint: GetCGRectCenter(dst.bounds)]
189 || ! [self canBit: bit moveFrom: src to: dst] )
192 ChangeSuperlayer(bit, _board.superlayer, -1);
194 dst.highlighted = YES;
195 [bit performSelector: @selector(setPickedUp:) withObject:nil afterDelay: 0.15];
196 CGPoint endPosition = [dst convertPoint: GetCGRectCenter(dst.bounds) toLayer: bit.superlayer];
197 [bit animateAndBlock: @"position"
198 from: [NSValue valueWithPoint: NSPointFromCGPoint(bit.position)]
199 to: [NSValue valueWithPoint: NSPointFromCGPoint(endPosition)]
202 dst.highlighted = NO;
205 [src draggedBit: bit to: dst];
206 [self bit: bit movedFrom: src to: dst];
213 #pragma mark GAMEPLAY METHODS TO BE OVERRIDDEN:
216 + (BOOL) landscapeOriented
222 - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)src
227 - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)src to: (id<BitHolder>)dst
232 - (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)src to: (id<BitHolder>)dst
237 - (Bit*) bitToPlaceInHolder: (id<BitHolder>)holder
243 - (BOOL) clickedBit: (Bit*)bit
248 - (Player*) checkForWinner
254 - (NSString*) stateString {return @"";}
255 - (void) setStateString: (NSString*)s { }
257 - (BOOL) applyMoveString: (NSString*)move {return NO;}
264 @implementation Player
267 - (id) initWithGame: (Game*)game
277 @synthesize game=_game, name=_name;
279 - (BOOL) isCurrent {return self == _game.currentPlayer;}
280 - (BOOL) isFriendly {return self == _game.currentPlayer;} // could be overridden for games with partners
281 - (BOOL) isUnfriendly {return ! self.friendly;}
285 return [_game.players indexOfObjectIdenticalTo: self];
288 - (Player*) nextPlayer
290 return [_game.players objectAtIndex: (self.index+1) % _game.players.count];
293 - (Player*) previousPlayer
295 return [_game.players objectAtIndex: (self.index-1) % _game.players.count];
298 - (NSString*) description
300 return [NSString stringWithFormat: @"%@[%@]", self.class,self.name];
308 @implementation CALayer (Game)
312 // The Game object stores a pointer to itself as the value of the "Game" property
313 // of its root layer. (CALayers can have arbitrary KV properties stored into them.)
314 for( CALayer *layer = self; layer; layer=layer.superlayer ) {
315 Game *game = [layer valueForKey: @"Game"];
319 NSAssert1(NO,@"Couldn't look up Game from %@",self);