Source/GoGame.m
author Jens Alfke <jens@mooseyard.com>
Sun Feb 06 16:31:03 2011 -0800 (2011-02-06)
changeset 29 0b1c315ffc64
parent 24 db8640a38faf
permissions -rw-r--r--
Minor compiler-compatibility fixes.
     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 "GoGame.h"
    24 #import "Grid.h"
    25 #import "Piece.h"
    26 #import "Dispenser.h"
    27 #import "Stack.h"
    28 #import "QuartzUtils.h"
    29 #import "GGBUtils.h"
    30 
    31 
    32 @implementation GoGame
    33 
    34 
    35 + (int) dimensions {return 19;}
    36 
    37 + (const GridCoord*) spotCoords
    38 {
    39     static GridCoord const sSpots[10]={ { 3,3}, { 3,9}, { 3,15},
    40                                         { 9,3}, { 9,9}, { 9,15},
    41                                         {15,3}, {15,9}, {15,15}, 
    42                                         {(unsigned)NSNotFound,(unsigned)NSNotFound} };
    43     return sSpots;
    44 }
    45 
    46 - (id) init
    47 {
    48     self = [super init];
    49     if (self != nil) {
    50         [self setNumberOfPlayers: 2];
    51         [(Player*)[_players objectAtIndex: 0] setName: @"Black"];
    52         [(Player*)[_players objectAtIndex: 1] setName: @"White"];
    53     }
    54     return self;
    55 }
    56         
    57 - (void) setUpBoard
    58 {
    59     int dimensions = [[self class] dimensions];
    60     CGRect tableBounds = _table.bounds;
    61     CGSize size = tableBounds.size;
    62     CGFloat boardSide = MIN(size.width* dimensions/(CGFloat)(dimensions+2),size.height);
    63     RectGrid *board = [[RectGrid alloc] initWithRows: dimensions columns: dimensions 
    64                                               frame: CGRectMake(floor((size.width-boardSide)/2),
    65                                                                 floor((size.height-boardSide)/2),
    66                                                                 boardSide,boardSide)];
    67     _board = board;
    68     /*
    69     grid.backgroundColor = GetCGPatternNamed(@"Wood.jpg");
    70     grid.borderColor = kTranslucentLightGrayColor;
    71     grid.borderWidth = 2;
    72     */
    73     board.lineColor = kTranslucentGrayColor;
    74     board.cellClass = [GoSquare class];
    75     board.usesDiagonals = board.allowsMoves = board.allowsCaptures = NO;
    76     [board addAllCells];
    77     const GridCoord *spots = [[self class] spotCoords];
    78     for( int i=0; spots[i].row!=(unsigned)NSNotFound; i++ )
    79         ((GoSquare*)[board cellAtRow: spots[i].row column: spots[i].col]).dotted = YES;
    80     [_table addSublayer: board];
    81     [board release];
    82     
    83     CGRect gridFrame = board.frame;
    84     CGFloat pieceSize = (int)board.spacing.width & ~1;  // make sure it's even
    85     CGFloat captureMinY = CGRectGetMinY(tableBounds) + pieceSize/2,
    86             captureHeight = size.height - pieceSize;
    87     _captured[0] = [[Stack alloc] initWithStartPos: CGPointMake(pieceSize/2,0)
    88                                            spacing: CGSizeMake(0,pieceSize)
    89                                       wrapInterval: floor(captureHeight/pieceSize)
    90                                        wrapSpacing: CGSizeMake(pieceSize,0)];
    91     _captured[0].frame = CGRectMake(CGRectGetMinX(tableBounds), 
    92                                     captureMinY,
    93                                     CGRectGetMinX(gridFrame)-CGRectGetMinX(tableBounds),
    94                                     captureHeight);
    95     _captured[0].zPosition = kPieceZ+1;
    96     [_table addSublayer: _captured[0]];
    97     [_captured[0] release];
    98     
    99     _captured[1] = [[Stack alloc] initWithStartPos: CGPointMake(pieceSize/2,captureHeight)
   100                                            spacing: CGSizeMake(0,-pieceSize)
   101                                       wrapInterval: floor(captureHeight/pieceSize)
   102                                        wrapSpacing: CGSizeMake(-pieceSize,0)];
   103     _captured[1].frame = CGRectMake(CGRectGetMaxX(gridFrame), 
   104                                     captureMinY,
   105                                     CGRectGetMaxX(tableBounds)-CGRectGetMaxX(gridFrame),
   106                                     captureHeight);
   107     _captured[1].startPos = CGPointMake(CGRectGetMaxX(_captured[1].bounds)-pieceSize/2, captureHeight);
   108     _captured[1].zPosition = kPieceZ+1;
   109     [_table addSublayer: _captured[1]];
   110     [_captured[1] release];
   111 
   112     PreloadSound(@"Pop");
   113 }
   114 
   115 - (CGImageRef) iconForPlayer: (int)playerNum
   116 {
   117     return GetCGImageNamed( playerNum ?@"ball-white.png" :@"ball-black.png" );
   118 }
   119 
   120 - (Piece*) pieceForPlayer: (int)index
   121 {
   122     NSString *imageName = index ?@"ball-white.png" :@"ball-black.png";
   123     CGFloat pieceSize = (int)(_board.spacing.width * 1.0) & ~1;  // make sure it's even
   124     Piece *stone = [[Piece alloc] initWithImageNamed: imageName scale: pieceSize];
   125     stone.owner = [self.players objectAtIndex: index];
   126     return [stone autorelease];
   127 }
   128 
   129 - (Bit*) bitToPlaceInHolder: (id<BitHolder>)holder
   130 {
   131     if( holder.bit != nil || ! [holder isKindOfClass: [GoSquare class]] )
   132         return nil;
   133     else
   134         return [self pieceForPlayer: self.currentPlayer.index];
   135 }
   136 
   137 
   138 - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder
   139 {
   140     return (srcHolder==nil);
   141 }
   142 
   143 
   144 - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   145 {
   146     if( srcHolder!=nil || ! [dstHolder isKindOfClass: [Square class]] )
   147         return NO;
   148     Square *dst=(Square*)dstHolder;
   149     
   150     // There should be a check here for a "ko" (repeated position) ... exercise for the reader!
   151     
   152     // Check for suicidal move. First an easy check for an empty adjacent space:
   153     NSArray *neighbors = dst.neighbors;
   154     for( GridCell *c in neighbors )
   155         if( c.empty )
   156             return YES;                     // there's an empty space
   157     // If the piece is surrounded, check the neighboring groups' liberties:
   158     for( GridCell *c in neighbors ) {
   159         int nLiberties;
   160         [c getGroup: &nLiberties];
   161         if( c.bit.unfriendly ) {
   162             if( nLiberties <= 1 )
   163                 return YES;             // the move captures, so it's not suicidal
   164         } else {
   165             if( nLiberties > 1 )
   166                 return YES;             // the stone joins a group with other liberties
   167         }
   168     }
   169     return NO;
   170 }
   171 
   172 
   173 - (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   174 {
   175     Square *dst=(Square*)dstHolder;
   176     int curIndex = self.currentPlayer.index;
   177     // Check for captured enemy groups:
   178     BOOL captured = NO;
   179     for( GridCell *c in dst.neighbors )
   180         if( c.bit.unfriendly ) {
   181             int nLiberties;
   182             NSSet *group = [c getGroup: &nLiberties];
   183             if( nLiberties == 0 ) {
   184                 captured = YES;
   185                 for( GridCell *capture in group )
   186                     [_captured[curIndex] addBit: capture.bit];  // Moves piece to POW camp!
   187             }
   188         }
   189     if( captured )
   190         PlaySound(@"Pop");
   191     
   192     [self.currentTurn addToMove: dst.name];
   193     [self endTurn];
   194 }
   195 
   196 
   197 // This sample code makes no attempt to detect the end of the game, or count score,
   198 // both of which are rather complex to decide in Go.
   199 
   200 
   201 #pragma mark -
   202 #pragma mark STATE:
   203 
   204 
   205 - (NSString*) stateString
   206 {
   207     int n = _board.rows;
   208     unichar state[n*n];
   209     for( int y=0; y<n; y++ )
   210         for( int x=0; x<n; x++ ) {
   211             Bit *bit = [_board cellAtRow: y column: x].bit;
   212             unichar ch;
   213             if( bit==nil )
   214                 ch = '-';
   215             else
   216                 ch = '1' + bit.owner.index;
   217             state[y*n+x] = ch;
   218         }
   219     NSMutableString *stateString = [NSMutableString stringWithCharacters: state length: n*n];
   220     
   221     NSUInteger cap0=_captured[0].numberOfBits, cap1=_captured[1].numberOfBits;
   222     if( cap0 || cap1 )
   223         [stateString appendFormat: @",%i,%i", cap0,cap1];
   224     return stateString;
   225 }
   226 
   227 - (void) setStateString: (NSString*)state
   228 {
   229     //NSLog(@"Go: setStateString: '%@'",state);
   230     NSArray *components = [state componentsSeparatedByString: @","];
   231     state = [components objectAtIndex: 0];
   232     int n = _board.rows;
   233     for( int y=0; y<n; y++ )
   234         for( int x=0; x<n; x++ ) {
   235             int i = y*n+x;
   236             Piece *piece = nil;
   237             if( i < state.length ) {
   238                 int index = [state characterAtIndex: i] - '1';
   239                 if( index==0 || index==1 )
   240                     piece = [self pieceForPlayer: index];
   241             }
   242             [_board cellAtRow: y column: x].bit = piece;
   243         }
   244     
   245     if( components.count < 3 )
   246         components = nil;
   247     for( int player=0; player<=1; player++ ) {
   248         NSUInteger nCaptured = [[components objectAtIndex: 1+player] intValue];
   249         NSUInteger curNCaptured = _captured[player].numberOfBits;
   250         if( nCaptured < curNCaptured )
   251            _captured[player].numberOfBits = nCaptured;
   252         else
   253             for( int i=curNCaptured; i<nCaptured; i++ )
   254                 [_captured[player] addBit: [self pieceForPlayer: 1-player]];
   255     }
   256 }
   257 
   258 
   259 - (BOOL) applyMoveString: (NSString*)move
   260 {
   261     //NSLog(@"Go: applyMoveString: '%@'",move);
   262     return [self animatePlacementIn: [_board cellWithName: move]];
   263 }
   264 
   265 
   266 @end
   267 
   268 
   269 @implementation Go9Game
   270 + (NSString*) displayName   {return @"Go (9x9)";}
   271 + (int) dimensions          {return 9;}
   272 + (const GridCoord*) spotCoords
   273 {
   274     static GridCoord const sSpots[6]= { {2,2}, {2,6}, {4,4}, {6,2}, {6,6}, 
   275                                         {(unsigned)NSNotFound,(unsigned)NSNotFound} };
   276     return sSpots;
   277 }
   278 @end
   279 
   280 
   281 @implementation Go13Game
   282 + (NSString*) displayName   {return @"Go (13x13)";}
   283 + (int) dimensions          {return 13;}
   284 + (const GridCoord*) spotCoords
   285 {
   286     static GridCoord const sSpots[6] = { { 2,2}, { 2,10}, {6,6},
   287                                          {10,2}, {10,10},
   288                                          {(unsigned)NSNotFound,(unsigned)NSNotFound} };
   289     return sSpots;
   290 }
   291 @end