Source/Game.m
changeset 11 436cbdf56810
parent 9 a59acc683080
child 14 4585c74d809c
     1.1 --- a/Source/Game.m	Thu May 29 15:04:06 2008 -0700
     1.2 +++ b/Source/Game.m	Sat Jul 05 17:46:43 2008 -0700
     1.3 @@ -20,71 +20,60 @@
     1.4      CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
     1.5      THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     1.6  */
     1.7 -#import "Game.h"
     1.8 -#import "Bit.h"
     1.9 -#import "BitHolder.h"
    1.10 +#import "Game+Protected.h"
    1.11  #import "QuartzUtils.h"
    1.12 +#import "GGBUtils.h"
    1.13  
    1.14  
    1.15  @interface Game ()
    1.16  @property (copy) NSArray *players;
    1.17 -@property (assign) Player *currentPlayer, *winner;
    1.18 +@property (assign) Player *winner;
    1.19 +- (void) _startTurn;
    1.20  @end
    1.21  
    1.22  
    1.23  @implementation Game
    1.24  
    1.25  
    1.26 -+ (NSString*) identifier
    1.27 +- (id) init
    1.28  {
    1.29 -    NSString* name = [self description];
    1.30 -    if( [name hasSuffix: @"Game"] )
    1.31 -        name = [name substringToIndex: name.length-4];
    1.32 -    return name;
    1.33 -}
    1.34 -
    1.35 -
    1.36 -+ (NSString*) displayName
    1.37 -{
    1.38 -    return [self identifier];
    1.39 -}
    1.40 -
    1.41 -
    1.42 -- (id) initWithUniqueID: (NSString*)uuid
    1.43 -{
    1.44 -    NSParameterAssert(uuid);
    1.45      self = [super init];
    1.46      if (self != nil) {
    1.47 -        _uniqueID = [uuid copy];
    1.48 -        _board = [[GGBLayer alloc] init];
    1.49 -        // Store a pointer to myself as the value of the "Game" property
    1.50 -        // of my root layer. (CALayers can have arbitrary KV properties stored into them.)
    1.51 -        // This is used by the -[CALayer game] category method defined below, to find the Game.
    1.52 -        [_board setValue: self forKey: @"Game"];
    1.53 -
    1.54 -        _currentMove = [[NSMutableString alloc] init];
    1.55 +        // Don't create _turns till -initWithCoder or -setNumberOfPlayers:.
    1.56      }
    1.57      return self;
    1.58  }
    1.59  
    1.60 -- (id) init
    1.61 +
    1.62 +- (id) initWithCoder: (NSCoder*)decoder
    1.63  {
    1.64 -    CFUUIDRef uuidRef = CFUUIDCreate(NULL);
    1.65 -    NSString* uuid = (id) CFUUIDCreateString(NULL,uuidRef);
    1.66 -    self = [self initWithUniqueID: uuid];
    1.67 -    CFRelease(uuid);
    1.68 -    CFRelease(uuidRef);
    1.69 +    self = [self init];
    1.70 +    if( self ) {
    1.71 +        _players = [[decoder decodeObjectForKey: @"players"] mutableCopy];
    1.72 +        _winner =   [decoder decodeObjectForKey: @"winner"];
    1.73 +        _turns   = [[decoder decodeObjectForKey: @"turns"] mutableCopy];
    1.74 +        _extraValues = [[decoder decodeObjectForKey: @"extraValues"] mutableCopy];
    1.75 +        self.currentTurnNo = self.maxTurnNo;
    1.76 +    }
    1.77      return self;
    1.78  }
    1.79  
    1.80 -- (id) initWithBoard: (GGBLayer*)board
    1.81 +
    1.82 +- (void) encodeWithCoder: (NSCoder*)coder
    1.83 +{
    1.84 +    [coder encodeObject: _players forKey: @"players"];
    1.85 +    [coder encodeObject: _winner forKey: @"winner"];
    1.86 +    [coder encodeObject: _turns   forKey: @"turns"];
    1.87 +    [coder encodeObject: _extraValues forKey: @"extraValues"];
    1.88 +}
    1.89 +
    1.90 +
    1.91 +- (id) initNewGameWithBoard: (GGBLayer*)board
    1.92  {
    1.93      self = [self init];
    1.94 -    if (self != nil) {
    1.95 -        _states = [[NSMutableArray alloc] init];
    1.96 -        _moves = [[NSMutableArray alloc] init];
    1.97 -        _board = [board retain];
    1.98 -        [board setValue: self forKey: @"Game"];
    1.99 +    if( self ) {
   1.100 +        self.board = board;
   1.101 +        NSAssert1(_players && _turns, @"%@ failed to set numberOfPlayers",self);
   1.102      }
   1.103      return self;
   1.104  }
   1.105 @@ -94,15 +83,67 @@
   1.106  {
   1.107      [_board release];
   1.108      [_players release];
   1.109 -    [_currentMove release];
   1.110 -    [_states release];
   1.111 -    [_moves release];
   1.112 +    [_turns release];
   1.113 +    [_extraValues release];
   1.114      [super dealloc];
   1.115  }
   1.116  
   1.117  
   1.118 -@synthesize players=_players, currentPlayer=_currentPlayer, winner=_winner, 
   1.119 -            currentMove=_currentMove, states=_states, moves=_moves, uniqueID=_uniqueID;
   1.120 +@synthesize players=_players, winner=_winner, turns=_turns, requireConfirmation=_requireConfirmation;
   1.121 +
   1.122 +
   1.123 +- (id)valueForUndefinedKey:(NSString *)key
   1.124 +{
   1.125 +    return [_extraValues objectForKey: key];
   1.126 +}
   1.127 +
   1.128 +- (void)setValue:(id)value forUndefinedKey:(NSString *)key
   1.129 +{
   1.130 +    if( ! _extraValues )
   1.131 +        _extraValues = [[NSMutableDictionary alloc] init];
   1.132 +    if( value )
   1.133 +        [_extraValues setObject: value forKey: key];
   1.134 +    else
   1.135 +        [_extraValues removeObjectForKey: key];
   1.136 +}
   1.137 +
   1.138 +
   1.139 +#pragma mark -
   1.140 +#pragma mark BOARD:
   1.141 +
   1.142 +
   1.143 +- (void) setUpBoard
   1.144 +{
   1.145 +    NSAssert1(NO,@"%@ forgot to implement -setUpBoard",[self class]);
   1.146 +}
   1.147 +
   1.148 +- (GGBLayer*) board
   1.149 +{
   1.150 +    return _board;
   1.151 +}
   1.152 +
   1.153 +- (void) setBoard: (GGBLayer*)board
   1.154 +{
   1.155 +    setObj(&_board,board);
   1.156 +    // Store a pointer to myself as the value of the "Game" property
   1.157 +    // of my root layer. (CALayers can have arbitrary KV properties stored into them.)
   1.158 +    // This is used by the -[CALayer game] category method defined below, to find the Game.
   1.159 +    [_board setValue: self forKey: @"Game"];
   1.160 +    
   1.161 +    BeginDisableAnimations();
   1.162 +    
   1.163 +    // Tell the game to add the necessary bits to the board:
   1.164 +    [self setUpBoard];
   1.165 +    
   1.166 +    // Re-apply the current state to set up the pieces/cards:
   1.167 +    self.stateString = [[_turns objectAtIndex: _currentTurnNo] boardState];
   1.168 +    
   1.169 +    EndDisableAnimations();
   1.170 +}
   1.171 +
   1.172 +
   1.173 +#pragma mark -
   1.174 +#pragma mark PLAYERS:
   1.175  
   1.176  
   1.177  - (void) setNumberOfPlayers: (unsigned)n
   1.178 @@ -114,105 +155,184 @@
   1.179          [players addObject: player];
   1.180          [player release];
   1.181      }
   1.182 +    self.players = players;
   1.183      self.winner = nil;
   1.184 -    self.currentPlayer = nil;
   1.185 -    self.players = players;
   1.186 +    
   1.187 +    Turn *turn = [[Turn alloc] initStartOfGame: self];
   1.188 +    setObj(&_turns, [NSMutableArray arrayWithObject: turn]);
   1.189 +    [turn release];
   1.190 +    [self _startTurn];
   1.191  }
   1.192  
   1.193 -
   1.194 -- (void) addToMove: (NSString*)str;
   1.195 +- (Player*) remotePlayer
   1.196  {
   1.197 -    [_currentMove appendString: str];
   1.198 +    for( Player *player in _players )
   1.199 +        if( ! player.local )
   1.200 +            return player;
   1.201 +    return nil;
   1.202  }
   1.203  
   1.204 -
   1.205 -- (BOOL) _rememberState
   1.206 +- (BOOL) isLocal
   1.207  {
   1.208 -    if( self.isLatestTurn ) {
   1.209 -        [_states addObject: self.stateString];
   1.210 -        return YES;
   1.211 -    } else
   1.212 -        return NO;
   1.213 +    return self.remotePlayer == nil;
   1.214  }
   1.215  
   1.216 +- (Player*) currentPlayer
   1.217 +{
   1.218 +    return self.currentTurn.player;
   1.219 +}
   1.220  
   1.221 -- (void) nextPlayer
   1.222 ++ (NSArray*) keyPathsForValuesAffectingCurrentPlayer {return [NSArray arrayWithObject: @"currentTurn"];}
   1.223 +
   1.224 +
   1.225 +#pragma mark -
   1.226 +#pragma mark TURNS:
   1.227 +
   1.228 +
   1.229 +- (Turn*) currentTurn
   1.230  {
   1.231 -    BOOL latestTurn = [self _rememberState];
   1.232 -    if( ! _currentPlayer ) {
   1.233 -        NSLog(@"*** The %@ Begins! ***", self.class);
   1.234 -        self.currentPlayer = [_players objectAtIndex: 0];
   1.235 -    } else {
   1.236 -        self.currentPlayer = _currentPlayer.nextPlayer;
   1.237 -        if( latestTurn ) {
   1.238 -            [self willChangeValueForKey: @"currentTurn"];
   1.239 -            _currentTurn++;
   1.240 -            [self didChangeValueForKey: @"currentTurn"];
   1.241 -        }
   1.242 -    }
   1.243 -    NSLog(@"Current player is %@",_currentPlayer);
   1.244 +    return [_turns objectAtIndex: _currentTurnNo];
   1.245 +}
   1.246 +
   1.247 +- (Turn*) latestTurn
   1.248 +{
   1.249 +    return [_turns lastObject];
   1.250 +}
   1.251 +
   1.252 ++ (NSArray*) keyPathsForValuesAffectingCurrentTurn {return [NSArray arrayWithObject: @"currentTurnNo"];}
   1.253 ++ (NSArray*) keyPathsForValuesAffectingLatestTurn  {return [NSArray arrayWithObject: @"turns"];}
   1.254 +
   1.255 +
   1.256 +- (void) _startTurn
   1.257 +{
   1.258 +    Turn *lastTurn = [_turns lastObject];
   1.259 +    NSAssert(lastTurn.status==kTurnFinished,@"Can't _startTurn till previous turn is finished");
   1.260 +    Turn *newTurn = [[Turn alloc] initWithPlayer: lastTurn.nextPlayer];
   1.261 +    
   1.262 +    [self willChangeValueForKey: @"turns"];
   1.263 +    [_turns addObject: newTurn];
   1.264 +    [self willChangeValueForKey: @"turns"];
   1.265 +    [newTurn release];
   1.266 +    self.currentTurnNo = _turns.count-1;
   1.267  }
   1.268  
   1.269  
   1.270  - (void) endTurn
   1.271  {
   1.272 -    NSLog(@"--- End of turn (move was '%@')", _currentMove);
   1.273 -    if( self.isLatestTurn ) {
   1.274 -        NSString *move = [[_currentMove copy] autorelease];
   1.275 -        [_currentMove setString: @""];
   1.276 -        [self willChangeValueForKey: @"maxTurn"];
   1.277 -        [_moves addObject: move];
   1.278 -        [self didChangeValueForKey: @"maxTurn"];
   1.279 -    }
   1.280 +    Turn *curTurn = self.currentTurn;
   1.281 +    if( curTurn.isLatestTurn && ! curTurn.replaying ) {
   1.282 +        curTurn.status = kTurnComplete;
   1.283 +        NSLog(@"--- End of %@", curTurn);
   1.284 +        
   1.285 +        Player *winner = [self checkForWinner];
   1.286 +        if( winner ) {
   1.287 +            NSLog(@"*** The %@ Ends! The winner is %@ ! ***", self.class, winner);
   1.288 +            self.winner = winner;
   1.289 +        }
   1.290 +        
   1.291 +        if( ! _requireConfirmation || !curTurn.player.local ) 
   1.292 +            [self confirmCurrentTurn];
   1.293  
   1.294 -    Player *winner = [self checkForWinner];
   1.295 -    if( winner ) {
   1.296 -        NSLog(@"*** The %@ Ends! The winner is %@ ! ***", self.class, winner);
   1.297 -        [self _rememberState];
   1.298 -        self.winner = winner;
   1.299 -    } else
   1.300 -        [self nextPlayer];
   1.301 -}
   1.302 -
   1.303 -
   1.304 -#pragma mark -
   1.305 -#pragma mark STORED TURNS:
   1.306 -
   1.307 -
   1.308 -- (unsigned) maxTurn
   1.309 -{
   1.310 -    return _moves.count;
   1.311 -}
   1.312 -
   1.313 -- (unsigned) currentTurn
   1.314 -{
   1.315 -    return _currentTurn;
   1.316 -}
   1.317 -
   1.318 -- (void) setCurrentTurn: (unsigned)turn
   1.319 -{
   1.320 -    NSParameterAssert(turn<=self.maxTurn);
   1.321 -    if( turn != _currentTurn ) {
   1.322 -        if( turn==_currentTurn+1 ) {
   1.323 -            [self applyMoveString: [_moves objectAtIndex: _currentTurn]];
   1.324 -        } else {
   1.325 -            BeginDisableAnimations();
   1.326 -            self.stateString = [_states objectAtIndex: turn];
   1.327 -            EndDisableAnimations();
   1.328 -        }
   1.329 -        _currentTurn = turn;
   1.330 -        self.currentPlayer = [_players objectAtIndex: (turn % _players.count)];
   1.331 +        [[NSNotificationCenter defaultCenter] postNotificationName: kTurnCompleteNotification
   1.332 +                                                            object: curTurn];
   1.333      }
   1.334  }
   1.335  
   1.336 +- (void) cancelCurrentTurn
   1.337 +{
   1.338 +    Turn *curTurn = self.currentTurn;
   1.339 +    if( curTurn.status > kTurnEmpty && curTurn.status < kTurnFinished ) {
   1.340 +        if( _winner )
   1.341 +            self.winner = nil;
   1.342 +        if( _board )
   1.343 +            self.stateString = curTurn.previousTurn.boardState;
   1.344 +        curTurn.status = kTurnEmpty;
   1.345 +    }
   1.346 +}
   1.347 +
   1.348 +- (void) confirmCurrentTurn
   1.349 +{
   1.350 +    Turn *curTurn = self.currentTurn;
   1.351 +    if( curTurn.status == kTurnComplete ) {
   1.352 +        curTurn.status = kTurnFinished;
   1.353 +        if( ! _winner )
   1.354 +            [self _startTurn];
   1.355 +    }
   1.356 +}
   1.357 +
   1.358  
   1.359  - (BOOL) isLatestTurn
   1.360  {
   1.361 -    return _currentTurn == MAX(_states.count,1)-1;
   1.362 +    return _currentTurnNo == _turns.count-1;
   1.363  }
   1.364  
   1.365 +- (unsigned) maxTurnNo
   1.366 +{
   1.367 +    return _turns.count-1;
   1.368 +}
   1.369  
   1.370 -- (BOOL) animateMoveFrom: (BitHolder*)src to: (BitHolder*)dst
   1.371 ++ (NSArray*) keyPathsForValuesAffectingIsLatestTurn {return [NSArray arrayWithObjects: @"currentTurnNo",@"turns",nil];}
   1.372 ++ (NSArray*) keyPathsForValuesAffectingMaxTurnNo    {return [NSArray arrayWithObjects: @"turns",nil];}
   1.373 +
   1.374 +- (unsigned) currentTurnNo
   1.375 +{
   1.376 +    return _currentTurnNo;
   1.377 +}
   1.378 +
   1.379 +
   1.380 +#pragma mark -
   1.381 +#pragma mark REPLAYING TURNS:
   1.382 +
   1.383 +
   1.384 +- (void) setCurrentTurnNo: (unsigned)turnNo
   1.385 +{
   1.386 +    NSParameterAssert(turnNo<=self.maxTurnNo);
   1.387 +    unsigned oldTurnNo = _currentTurnNo;
   1.388 +    if( turnNo != oldTurnNo ) {
   1.389 +        if( _board ) {
   1.390 +            Turn *turn = [_turns objectAtIndex: turnNo];
   1.391 +            NSString *state;
   1.392 +            if( turn.status == kTurnEmpty )
   1.393 +                state = turn.previousTurn.boardState;
   1.394 +            else
   1.395 +                state = turn.boardState;
   1.396 +            NSAssert1(state,@"empty boardState at turn #%i",turnNo);
   1.397 +            _currentTurnNo = turnNo;
   1.398 +            if( turnNo==oldTurnNo+1 ) {
   1.399 +                NSString *move = turn.move;
   1.400 +                if( move ) {
   1.401 +                    NSLog(@"Reapplying move '%@'",move);
   1.402 +                    turn.replaying = YES;
   1.403 +                    @try{
   1.404 +                        if( ! [self applyMoveString: move] ) {
   1.405 +                            _currentTurnNo = oldTurnNo;
   1.406 +                            NSBeep();
   1.407 +                            NSLog(@"WARNING: %@ failed to apply stored move '%@'!", self,move);
   1.408 +                            return;
   1.409 +                        }
   1.410 +                    }@finally{
   1.411 +                        turn.replaying = NO;
   1.412 +                    }
   1.413 +                }
   1.414 +            } else {
   1.415 +                NSLog(@"Reapplying state '%@'",state);
   1.416 +                BeginDisableAnimations();
   1.417 +                self.stateString = state;
   1.418 +                EndDisableAnimations();
   1.419 +            }
   1.420 +            if( ! [self.stateString isEqual: state] ) {
   1.421 +                _currentTurnNo = oldTurnNo;
   1.422 +                NSBeep();
   1.423 +                NSLog(@"WARNING: %@ failed to apply stored state '%@'!", self,state);
   1.424 +                return;
   1.425 +            }
   1.426 +        } else
   1.427 +            _currentTurnNo = turnNo;
   1.428 +    }
   1.429 +}
   1.430 +
   1.431 +
   1.432 +- (BOOL) animateMoveFrom: (CALayer<BitHolder>*)src to: (CALayer<BitHolder>*)dst
   1.433  {
   1.434      if( src==nil || dst==nil || dst==src )
   1.435          return NO;
   1.436 @@ -241,7 +361,35 @@
   1.437      
   1.438      [src draggedBit: bit to: dst];
   1.439      [self bit: bit movedFrom: src to: dst];
   1.440 -    src = dst;
   1.441 +    return YES;
   1.442 +}
   1.443 +
   1.444 +
   1.445 +- (BOOL) animatePlacementIn: (CALayer<BitHolder>*)dst
   1.446 +{
   1.447 +    if( dst == nil )
   1.448 +        return NO;
   1.449 +    Bit *bit = [self bitToPlaceInHolder: dst];
   1.450 +    if( ! bit )
   1.451 +        return NO;
   1.452 +    
   1.453 +    CALayer<BitHolder>* oldHolder = (CALayer<BitHolder>*) bit.holder;
   1.454 +    if( oldHolder ) {
   1.455 +        if( oldHolder != dst ) 
   1.456 +            return [self animateMoveFrom: oldHolder to: dst];
   1.457 +    } else
   1.458 +        bit.position = [dst convertPoint: GetCGRectCenter(dst.bounds) toLayer: _board.superlayer];
   1.459 +    ChangeSuperlayer(bit, _board.superlayer, -1);
   1.460 +    bit.pickedUp = YES;
   1.461 +    dst.highlighted = YES;
   1.462 +    
   1.463 +    DelayFor(0.2);
   1.464 +    
   1.465 +    dst.bit = bit;
   1.466 +    dst.highlighted = NO;
   1.467 +    bit.pickedUp = NO;
   1.468 +    
   1.469 +    [self bit: bit movedFrom: nil to: dst];
   1.470      return YES;
   1.471  }
   1.472       
   1.473 @@ -250,12 +398,37 @@
   1.474  #pragma mark GAMEPLAY METHODS TO BE OVERRIDDEN:
   1.475  
   1.476  
   1.477 ++ (NSString*) identifier
   1.478 +{
   1.479 +    NSString* name = [self description];
   1.480 +    if( [name hasSuffix: @"Game"] )
   1.481 +        name = [name substringToIndex: name.length-4];
   1.482 +    return name;
   1.483 +}
   1.484 +
   1.485 ++ (NSString*) displayName
   1.486 +{
   1.487 +    return [self identifier];
   1.488 +}
   1.489 +
   1.490  + (BOOL) landscapeOriented
   1.491  {
   1.492      return NO;
   1.493  }
   1.494  
   1.495  
   1.496 +- (NSString*) initialStateString
   1.497 +{
   1.498 +    return @"";
   1.499 +}
   1.500 +
   1.501 +
   1.502 +- (CGImageRef) iconForPlayer: (int)playerIndex
   1.503 +{
   1.504 +    return nil;
   1.505 +}
   1.506 +
   1.507 +
   1.508  - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)src
   1.509  {
   1.510      return YES;
   1.511 @@ -287,83 +460,20 @@
   1.512      return nil;
   1.513  }
   1.514  
   1.515 -
   1.516 +/* These are abstract
   1.517 + 
   1.518  - (NSString*) stateString                   {return @"";}
   1.519  - (void) setStateString: (NSString*)s       { }
   1.520  
   1.521  - (BOOL) applyMoveString: (NSString*)move   {return NO;}
   1.522 +*/
   1.523  
   1.524  @end
   1.525  
   1.526  
   1.527  
   1.528  
   1.529 -@implementation Player
   1.530 -
   1.531 -
   1.532 -- (id) initWithGame: (Game*)game
   1.533 -{
   1.534 -    self = [super init];
   1.535 -    if (self != nil) {
   1.536 -        _game = game;
   1.537 -    }
   1.538 -    return self;
   1.539 -}
   1.540 -
   1.541 -- (id) initWithCoder: (NSCoder*)decoder
   1.542 -{
   1.543 -    self = [self init];
   1.544 -    if( self ) {
   1.545 -        _game =  [decoder decodeObjectForKey: @"game"];
   1.546 -        _name = [[decoder decodeObjectForKey: @"name"] copy];
   1.547 -    }
   1.548 -    return self;
   1.549 -}
   1.550 -
   1.551 -- (void) encodeWithCoder: (NSCoder*)coder
   1.552 -{
   1.553 -    [coder encodeObject: _game forKey: @"game"];
   1.554 -    [coder encodeObject: _name forKey: @"name"];
   1.555 -}
   1.556 -
   1.557 -- (void) dealloc
   1.558 -{
   1.559 -    [_name release];
   1.560 -    [super dealloc];
   1.561 -}
   1.562 -
   1.563 -
   1.564 -@synthesize game=_game, name=_name;
   1.565 -
   1.566 -- (BOOL) isCurrent      {return self == _game.currentPlayer;}
   1.567 -- (BOOL) isFriendly     {return self == _game.currentPlayer;}   // could be overridden for games with partners
   1.568 -- (BOOL) isUnfriendly   {return ! self.friendly;}
   1.569 -
   1.570 -- (int) index
   1.571 -{
   1.572 -    return [_game.players indexOfObjectIdenticalTo: self];
   1.573 -}
   1.574 -
   1.575 -- (Player*) nextPlayer
   1.576 -{
   1.577 -    return [_game.players objectAtIndex: (self.index+1) % _game.players.count];
   1.578 -}
   1.579 -
   1.580 -- (Player*) previousPlayer
   1.581 -{
   1.582 -    return [_game.players objectAtIndex: (self.index-1) % _game.players.count];
   1.583 -}
   1.584 -
   1.585 -- (NSString*) description
   1.586 -{
   1.587 -    return [NSString stringWithFormat: @"%@[%@]", self.class,self.name];
   1.588 -}
   1.589 -
   1.590 -@end
   1.591 -
   1.592 -
   1.593 -
   1.594 -
   1.595 +#pragma mark -
   1.596  @implementation CALayer (Game)
   1.597  
   1.598  - (Game*) game