Source/GoGame.m
changeset 0 e9f7ba4718e1
child 1 3eb7be1dd7b6
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/Source/GoGame.m	Fri Mar 07 11:43:02 2008 -0800
     1.3 @@ -0,0 +1,166 @@
     1.4 +/*  This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
     1.5 +    http://developer.apple.com/samplecode/GeekGameBoard/
     1.6 +    Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
     1.7 +
     1.8 +    Redistribution and use in source and binary forms, with or without modification, are permitted
     1.9 +    provided that the following conditions are met:
    1.10 +
    1.11 +    * Redistributions of source code must retain the above copyright notice, this list of conditions
    1.12 +      and the following disclaimer.
    1.13 +    * Redistributions in binary form must reproduce the above copyright notice, this list of
    1.14 +      conditions and the following disclaimer in the documentation and/or other materials provided
    1.15 +      with the distribution.
    1.16 +
    1.17 +    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
    1.18 +    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
    1.19 +    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
    1.20 +    BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    1.21 +    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
    1.22 +    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
    1.23 +    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
    1.24 +    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.25 +*/
    1.26 +#import "GoGame.h"
    1.27 +#import "Grid.h"
    1.28 +#import "Piece.h"
    1.29 +#import "Dispenser.h"
    1.30 +#import "Stack.h"
    1.31 +#import "QuartzUtils.h"
    1.32 +
    1.33 +
    1.34 +@implementation GoGame
    1.35 +
    1.36 +
    1.37 +- (void) x_createDispenser: (NSString*)imageName position: (CGPoint)position forPlayer: (unsigned)playerNo
    1.38 +{
    1.39 +    CGFloat pieceSize = (int)(_grid.spacing.width * 0.9) & ~1;  // make sure it's even
    1.40 +    Piece *stone = [[Piece alloc] initWithImageNamed: imageName scale: pieceSize];
    1.41 +    stone.owner = [self.players objectAtIndex: playerNo];
    1.42 +    CGRect frame = {position, {1.5*pieceSize,1.5*pieceSize}};
    1.43 +    Dispenser *disp = [[Dispenser alloc] initWithPrototype: stone quantity: INT_MAX frame:frame];
    1.44 +    [_board addSublayer: disp];
    1.45 +    [disp release];
    1.46 +    [stone release];
    1.47 +}    
    1.48 +
    1.49 +
    1.50 +- (id) initWithBoard: (CALayer*)board
    1.51 +{
    1.52 +    self = [super initWithBoard: board];
    1.53 +    if (self != nil) {
    1.54 +        [self setNumberOfPlayers: 2];
    1.55 +        
    1.56 +        CGSize size = board.bounds.size;
    1.57 +        RectGrid *grid = [[RectGrid alloc] initWithRows: 9 columns: 9 
    1.58 +                                                  frame: CGRectMake((size.width-size.height+16)/2,8,
    1.59 +                                                                    size.height-16,size.height-16)];
    1.60 +        _grid = grid;
    1.61 +        grid.backgroundColor = GetCGPatternNamed(@"Wood.jpg");
    1.62 +        grid.borderColor = kTranslucentLightGrayColor;
    1.63 +        grid.borderWidth = 2;
    1.64 +        grid.lineColor = kTranslucentGrayColor;
    1.65 +        grid.cellClass = [GoSquare class];
    1.66 +        [grid addAllCells];
    1.67 +        ((GoSquare*)[grid cellAtRow: 2 column: 2]).dotted = YES;
    1.68 +        ((GoSquare*)[grid cellAtRow: 6 column: 6]).dotted = YES;
    1.69 +        ((GoSquare*)[grid cellAtRow: 2 column: 6]).dotted = YES;
    1.70 +        ((GoSquare*)[grid cellAtRow: 6 column: 2]).dotted = YES;
    1.71 +        grid.usesDiagonals = grid.allowsMoves = grid.allowsCaptures = NO;
    1.72 +        [board addSublayer: grid];
    1.73 +        [grid release];
    1.74 +        
    1.75 +        CGRect gridFrame = grid.frame;
    1.76 +        CGFloat pieceSize = (int)grid.spacing.width & ~1;  // make sure it's even
    1.77 +        [self x_createDispenser: @"Red Ball.png"
    1.78 +                      position: CGPointMake(CGRectGetMinX(gridFrame)-2*pieceSize, 
    1.79 +                                            CGRectGetMinY(gridFrame))
    1.80 +                     forPlayer: 0];
    1.81 +        [self x_createDispenser: @"White Ball.png"
    1.82 +                      position: CGPointMake(CGRectGetMaxX(gridFrame)+0.5*pieceSize,
    1.83 +                                            CGRectGetMaxY(gridFrame)-1.5*pieceSize)
    1.84 +                     forPlayer: 1];
    1.85 +        
    1.86 +        CGFloat captureHeight = gridFrame.size.height-4*pieceSize;
    1.87 +        _captured[0] = [[Stack alloc] initWithStartPos: CGPointMake(2*pieceSize,0)
    1.88 +                                               spacing: CGSizeMake(0,pieceSize)
    1.89 +                                          wrapInterval: floor(captureHeight/pieceSize)
    1.90 +                                           wrapSpacing: CGSizeMake(-pieceSize,0)];
    1.91 +        _captured[0].frame = CGRectMake(CGRectGetMinX(gridFrame)-3*pieceSize, 
    1.92 +                                          CGRectGetMinY(gridFrame)+3*pieceSize,
    1.93 +                                          2*pieceSize, captureHeight);
    1.94 +        _captured[0].zPosition = kPieceZ+1;
    1.95 +        [board addSublayer: _captured[0]];
    1.96 +        [_captured[0] release];
    1.97 +        
    1.98 +        _captured[1] = [[Stack alloc] initWithStartPos: CGPointMake(0,captureHeight)
    1.99 +                                               spacing: CGSizeMake(0,-pieceSize)
   1.100 +                                          wrapInterval: floor(captureHeight/pieceSize)
   1.101 +                                           wrapSpacing: CGSizeMake(pieceSize,0)];
   1.102 +        _captured[1].frame = CGRectMake(CGRectGetMaxX(gridFrame)+pieceSize, 
   1.103 +                                          CGRectGetMinY(gridFrame)+pieceSize,
   1.104 +                                          2*pieceSize, captureHeight);
   1.105 +        _captured[1].zPosition = kPieceZ+1;
   1.106 +        [board addSublayer: _captured[1]];
   1.107 +        [_captured[1] release];
   1.108 +
   1.109 +        [self nextPlayer];
   1.110 +}
   1.111 +    return self;
   1.112 +}
   1.113 +
   1.114 +
   1.115 +- (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   1.116 +{
   1.117 +    Square *dst=(Square*)dstHolder;
   1.118 +    
   1.119 +    // There should be a check here for a "ko" (repeated position) ... exercise for the reader!
   1.120 +    
   1.121 +    // Check for suicidal move. First an easy check for an empty adjacent space:
   1.122 +    NSArray *neighbors = dst.neighbors;
   1.123 +    for( GridCell *c in neighbors )
   1.124 +        if( c.empty )
   1.125 +            return YES;                     // there's an empty space
   1.126 +    // If the piece is surrounded, check the neighboring groups' liberties:
   1.127 +    for( GridCell *c in neighbors ) {
   1.128 +        int nLiberties;
   1.129 +        [c getGroup: &nLiberties];
   1.130 +        if( c.bit.unfriendly ) {
   1.131 +            if( nLiberties <= 1 )
   1.132 +                return YES;             // the move captures, so it's not suicidal
   1.133 +        } else {
   1.134 +            if( nLiberties > 1 )
   1.135 +                return YES;             // the stone joins a group with other liberties
   1.136 +        }
   1.137 +    }
   1.138 +    return NO;
   1.139 +}
   1.140 +
   1.141 +
   1.142 +- (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   1.143 +{
   1.144 +    Square *dst=(Square*)dstHolder;
   1.145 +    int curIndex = _currentPlayer.index;
   1.146 +    // Check for captured enemy groups:
   1.147 +    BOOL captured = NO;
   1.148 +    for( GridCell *c in dst.neighbors )
   1.149 +        if( c.bit.unfriendly ) {
   1.150 +            int nLiberties;
   1.151 +            NSSet *group = [c getGroup: &nLiberties];
   1.152 +            if( nLiberties == 0 ) {
   1.153 +                captured = YES;
   1.154 +                for( GridCell *capture in group )
   1.155 +                    [_captured[curIndex] addBit: capture.bit];  // Moves piece to POW camp!
   1.156 +            }
   1.157 +        }
   1.158 +    if( captured )
   1.159 +        [[NSSound soundNamed: @"Pop"] play];
   1.160 +        
   1.161 +    [self nextPlayer];
   1.162 +}
   1.163 +
   1.164 +
   1.165 +// This sample code makes no attempt to detect the end of the game, or count score,
   1.166 +// both of which are rather complex to decide in Go.
   1.167 +
   1.168 +
   1.169 +@end