Source/GoGame.m
author Jens Alfke <jens@mooseyard.com>
Mon Mar 10 17:32:04 2008 -0700 (2008-03-10)
changeset 2 7b0441db81e5
parent 0 e9f7ba4718e1
child 3 40d225cf9c43
permissions -rw-r--r--
Oops, needed to fix an #include
     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 - (void) x_createDispenser: (NSString*)imageName position: (CGPoint)position forPlayer: (unsigned)playerNo
    36 {
    37     CGFloat pieceSize = (int)(_grid.spacing.width * 0.9) & ~1;  // make sure it's even
    38     Piece *stone = [[Piece alloc] initWithImageNamed: imageName scale: pieceSize];
    39     stone.owner = [self.players objectAtIndex: playerNo];
    40     CGRect frame = {position, {1.5*pieceSize,1.5*pieceSize}};
    41     Dispenser *disp = [[Dispenser alloc] initWithPrototype: stone quantity: INT_MAX frame:frame];
    42     [_board addSublayer: disp];
    43     [disp release];
    44     [stone release];
    45 }    
    46 
    47 
    48 - (id) initWithBoard: (GGBLayer*)board
    49 {
    50     self = [super initWithBoard: board];
    51     if (self != nil) {
    52         [self setNumberOfPlayers: 2];
    53         
    54         CGSize size = board.bounds.size;
    55         RectGrid *grid = [[RectGrid alloc] initWithRows: 9 columns: 9 
    56                                                   frame: CGRectMake((size.width-size.height+16)/2,8,
    57                                                                     size.height-16,size.height-16)];
    58         _grid = grid;
    59         grid.backgroundColor = GetCGPatternNamed(@"Wood.jpg");
    60         grid.borderColor = kTranslucentLightGrayColor;
    61         grid.borderWidth = 2;
    62         grid.lineColor = kTranslucentGrayColor;
    63         grid.cellClass = [GoSquare class];
    64         [grid addAllCells];
    65         ((GoSquare*)[grid cellAtRow: 2 column: 2]).dotted = YES;
    66         ((GoSquare*)[grid cellAtRow: 6 column: 6]).dotted = YES;
    67         ((GoSquare*)[grid cellAtRow: 2 column: 6]).dotted = YES;
    68         ((GoSquare*)[grid cellAtRow: 6 column: 2]).dotted = YES;
    69         grid.usesDiagonals = grid.allowsMoves = grid.allowsCaptures = NO;
    70         [board addSublayer: grid];
    71         [grid release];
    72         
    73         CGRect gridFrame = grid.frame;
    74         CGFloat pieceSize = (int)grid.spacing.width & ~1;  // make sure it's even
    75         [self x_createDispenser: @"Red Ball.png"
    76                       position: CGPointMake(CGRectGetMinX(gridFrame)-2*pieceSize, 
    77                                             CGRectGetMinY(gridFrame))
    78                      forPlayer: 0];
    79         [self x_createDispenser: @"White Ball.png"
    80                       position: CGPointMake(CGRectGetMaxX(gridFrame)+0.5*pieceSize,
    81                                             CGRectGetMaxY(gridFrame)-1.5*pieceSize)
    82                      forPlayer: 1];
    83         
    84         CGFloat captureHeight = gridFrame.size.height-4*pieceSize;
    85         _captured[0] = [[Stack alloc] initWithStartPos: CGPointMake(2*pieceSize,0)
    86                                                spacing: CGSizeMake(0,pieceSize)
    87                                           wrapInterval: floor(captureHeight/pieceSize)
    88                                            wrapSpacing: CGSizeMake(-pieceSize,0)];
    89         _captured[0].frame = CGRectMake(CGRectGetMinX(gridFrame)-3*pieceSize, 
    90                                           CGRectGetMinY(gridFrame)+3*pieceSize,
    91                                           2*pieceSize, captureHeight);
    92         _captured[0].zPosition = kPieceZ+1;
    93         [board addSublayer: _captured[0]];
    94         [_captured[0] release];
    95         
    96         _captured[1] = [[Stack alloc] initWithStartPos: CGPointMake(0,captureHeight)
    97                                                spacing: CGSizeMake(0,-pieceSize)
    98                                           wrapInterval: floor(captureHeight/pieceSize)
    99                                            wrapSpacing: CGSizeMake(pieceSize,0)];
   100         _captured[1].frame = CGRectMake(CGRectGetMaxX(gridFrame)+pieceSize, 
   101                                           CGRectGetMinY(gridFrame)+pieceSize,
   102                                           2*pieceSize, captureHeight);
   103         _captured[1].zPosition = kPieceZ+1;
   104         [board addSublayer: _captured[1]];
   105         [_captured[1] release];
   106 
   107         [self nextPlayer];
   108 }
   109     return self;
   110 }
   111 
   112 
   113 - (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   114 {
   115     Square *dst=(Square*)dstHolder;
   116     
   117     // There should be a check here for a "ko" (repeated position) ... exercise for the reader!
   118     
   119     // Check for suicidal move. First an easy check for an empty adjacent space:
   120     NSArray *neighbors = dst.neighbors;
   121     for( GridCell *c in neighbors )
   122         if( c.empty )
   123             return YES;                     // there's an empty space
   124     // If the piece is surrounded, check the neighboring groups' liberties:
   125     for( GridCell *c in neighbors ) {
   126         int nLiberties;
   127         [c getGroup: &nLiberties];
   128         if( c.bit.unfriendly ) {
   129             if( nLiberties <= 1 )
   130                 return YES;             // the move captures, so it's not suicidal
   131         } else {
   132             if( nLiberties > 1 )
   133                 return YES;             // the stone joins a group with other liberties
   134         }
   135     }
   136     return NO;
   137 }
   138 
   139 
   140 - (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   141 {
   142     Square *dst=(Square*)dstHolder;
   143     int curIndex = _currentPlayer.index;
   144     // Check for captured enemy groups:
   145     BOOL captured = NO;
   146     for( GridCell *c in dst.neighbors )
   147         if( c.bit.unfriendly ) {
   148             int nLiberties;
   149             NSSet *group = [c getGroup: &nLiberties];
   150             if( nLiberties == 0 ) {
   151                 captured = YES;
   152                 for( GridCell *capture in group )
   153                     [_captured[curIndex] addBit: capture.bit];  // Moves piece to POW camp!
   154             }
   155         }
   156     if( captured )
   157         PlaySound(@"Pop");
   158         
   159     [self nextPlayer];
   160 }
   161 
   162 
   163 // This sample code makes no attempt to detect the end of the game, or count score,
   164 // both of which are rather complex to decide in Go.
   165 
   166 
   167 @end