Source/TicTacToeGame.m
author Jens Alfke <jens@mooseyard.com>
Sat Jul 05 17:46:43 2008 -0700 (2008-07-05)
changeset 11 436cbdf56810
parent 8 45c82a071aca
child 16 28392c9a969f
permissions -rw-r--r--
* Improved drag-and-drop (supports CandyBar)
* Fixed DiscPiece
* Inheritable layer styles
etc.
     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.
     4 
     5     Redistribution and use in source and binary forms, with or without modification, are permitted
     6     provided that the following conditions are met:
     7 
     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.
    13 
    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.
    22 */
    23 #import "TicTacToeGame.h"
    24 #import "Grid.h"
    25 #import "Dispenser.h"
    26 #import "Piece.h"
    27 #import "QuartzUtils.h"
    28 
    29 
    30 @implementation TicTacToeGame
    31 
    32 - (Piece*) pieceForPlayer: (int)playerNumber
    33 {
    34     Piece *p = [[Piece alloc] initWithImageNamed: (playerNumber ? @"O.tiff" :@"X.tiff")
    35                                            scale: 80];
    36     p.owner = [self.players objectAtIndex: playerNumber];
    37     p.name = (playerNumber ?@"O" :@"X");
    38     return [p autorelease];
    39 }
    40 
    41 - (id) init
    42 {
    43     self = [super init];
    44     if (self != nil) {
    45         [self setNumberOfPlayers: 2];
    46     }
    47     return self;
    48 }
    49         
    50 - (void) setUpBoard
    51 {
    52     // Create a 3x3 grid:
    53     CGFloat center = floor(CGRectGetMidX(_board.bounds));
    54     [_grid release];
    55     _grid = [[RectGrid alloc] initWithRows: 3 columns: 3 frame: CGRectMake(center-150,0, 300,300)];
    56     [_grid addAllCells];
    57     _grid.allowsMoves = _grid.allowsCaptures = NO;
    58     _grid.cellColor = CreateGray(1.0, 0.25);
    59     _grid.lineColor = kTranslucentLightGrayColor;
    60     [_board addSublayer: _grid];
    61     
    62     // Create piece dispensers for the two players:
    63     for( int playerNumber=0; playerNumber<=1; playerNumber++ ) {
    64         Piece *p = [self pieceForPlayer: playerNumber];
    65         CGFloat x = floor(CGRectGetMidX(_board.bounds));
    66 #if TARGET_OS_IPHONE
    67         x = x - 80 + 160*playerNumber;
    68         CGFloat y = 360;
    69 #else
    70         x += (playerNumber==0 ?-230 :230);
    71         CGFloat y = 175;
    72 #endif
    73         [_dispenser[playerNumber] release];
    74         _dispenser[playerNumber] = [[Dispenser alloc] initWithPrototype: p quantity: 0
    75                                                                   frame: CGRectMake(x-45,y-45, 90,90)];
    76         [_board addSublayer: _dispenser[playerNumber]];
    77     }            
    78 }
    79 
    80 
    81 - (NSString*) stateString
    82 {
    83     unichar str[10];
    84     for( int i=0; i<9; i++ ) {
    85         NSString *ident = [_grid cellAtRow: i/3 column: i%3].bit.name;
    86         if( ident==nil )
    87             str[i] = '-';
    88         else 
    89             str[i] = [ident characterAtIndex: 0];
    90     }
    91     return [NSString stringWithCharacters: str length: 9];
    92 }
    93 
    94 - (void) setStateString: (NSString*)stateString
    95 {
    96     for( int i=0; i<9; i++ ) {
    97         Piece *piece = nil;
    98         if( i < stateString.length )
    99             switch( [stateString characterAtIndex: i] ) {
   100                 case 'X': case 'x': piece = [self pieceForPlayer: 0]; break;
   101                 case 'O': case 'o': piece = [self pieceForPlayer: 1]; break;
   102                 default:            break;
   103             }
   104         [_grid cellAtRow: i/3 column: i%3].bit = piece;
   105     }
   106 }
   107 
   108 
   109 - (Bit*) bitToPlaceInHolder: (id<BitHolder>)holder
   110 {
   111     if( holder.bit==nil && [holder isKindOfClass: [Square class]] )
   112         return _dispenser[self.currentPlayer.index].bit;
   113     else
   114         return nil;
   115 }
   116 
   117 
   118 - (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)src to: (id<BitHolder>)dst
   119 {
   120     Square *square = (Square*)dst;
   121     int squareIndex = 3*square.row + square.column;
   122     [self.currentTurn addToMove: [NSString stringWithFormat: @"%@%i", bit.name, squareIndex]];
   123     [super bit: bit movedFrom: src to: dst];
   124 }
   125 
   126 /* FIX: Need to restore this somehow, now that -nextPlayer is gone
   127 - (void) nextPlayer
   128 {
   129     [super nextPlayer];
   130     // Give the next player another piece to put down:
   131     _dispenser[self.currentPlayer.index].quantity = 1;
   132 }
   133  */
   134 
   135 static Player* ownerAt( Grid *grid, int index )
   136 {
   137     return [grid cellAtRow: index/3 column: index%3].bit.owner;
   138 }
   139 
   140 /** Should return the winning player, if the current position is a win. */
   141 - (Player*) checkForWinner
   142 {
   143     static const int kWinningTriples[8][3] =  { {0,1,2}, {3,4,5}, {6,7,8},  // rows
   144                                                 {0,3,6}, {1,4,7}, {2,5,8},  // cols
   145                                                 {0,4,8}, {2,4,6} };         // diagonals
   146     for( int i=0; i<8; i++ ) {
   147         const int *triple = kWinningTriples[i];
   148         Player *p = ownerAt(_grid,triple[0]);
   149         if( p && p == ownerAt(_grid,triple[1]) && p == ownerAt(_grid,triple[2]) )
   150             return p;
   151     }
   152     return nil;
   153 }
   154 
   155 @end