jens@0: /* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. jens@0: http://developer.apple.com/samplecode/GeekGameBoard/ jens@0: Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. jens@0: jens@0: Redistribution and use in source and binary forms, with or without modification, are permitted jens@0: provided that the following conditions are met: jens@0: jens@0: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@0: and the following disclaimer. jens@0: * Redistributions in binary form must reproduce the above copyright notice, this list of jens@0: conditions and the following disclaimer in the documentation and/or other materials provided jens@0: with the distribution. jens@0: jens@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@0: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@0: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@0: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@0: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@0: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@0: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@0: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@0: */ jens@0: #import "GoGame.h" jens@0: #import "Grid.h" jens@0: #import "Piece.h" jens@0: #import "Dispenser.h" jens@0: #import "Stack.h" jens@0: #import "QuartzUtils.h" jens@1: #import "GGBUtils.h" jens@0: jens@0: jens@0: @implementation GoGame jens@0: jens@0: jens@0: - (void) x_createDispenser: (NSString*)imageName position: (CGPoint)position forPlayer: (unsigned)playerNo jens@0: { jens@0: CGFloat pieceSize = (int)(_grid.spacing.width * 0.9) & ~1; // make sure it's even jens@0: Piece *stone = [[Piece alloc] initWithImageNamed: imageName scale: pieceSize]; jens@0: stone.owner = [self.players objectAtIndex: playerNo]; jens@0: CGRect frame = {position, {1.5*pieceSize,1.5*pieceSize}}; jens@0: Dispenser *disp = [[Dispenser alloc] initWithPrototype: stone quantity: INT_MAX frame:frame]; jens@0: [_board addSublayer: disp]; jens@0: [disp release]; jens@0: [stone release]; jens@0: } jens@0: jens@0: jens@1: - (id) initWithBoard: (GGBLayer*)board jens@0: { jens@0: self = [super initWithBoard: board]; jens@0: if (self != nil) { jens@0: [self setNumberOfPlayers: 2]; jens@0: jens@0: CGSize size = board.bounds.size; jens@0: RectGrid *grid = [[RectGrid alloc] initWithRows: 9 columns: 9 jens@0: frame: CGRectMake((size.width-size.height+16)/2,8, jens@0: size.height-16,size.height-16)]; jens@0: _grid = grid; jens@0: grid.backgroundColor = GetCGPatternNamed(@"Wood.jpg"); jens@0: grid.borderColor = kTranslucentLightGrayColor; jens@0: grid.borderWidth = 2; jens@0: grid.lineColor = kTranslucentGrayColor; jens@0: grid.cellClass = [GoSquare class]; jens@0: [grid addAllCells]; jens@0: ((GoSquare*)[grid cellAtRow: 2 column: 2]).dotted = YES; jens@0: ((GoSquare*)[grid cellAtRow: 6 column: 6]).dotted = YES; jens@0: ((GoSquare*)[grid cellAtRow: 2 column: 6]).dotted = YES; jens@0: ((GoSquare*)[grid cellAtRow: 6 column: 2]).dotted = YES; jens@0: grid.usesDiagonals = grid.allowsMoves = grid.allowsCaptures = NO; jens@0: [board addSublayer: grid]; jens@0: [grid release]; jens@0: jens@0: CGRect gridFrame = grid.frame; jens@0: CGFloat pieceSize = (int)grid.spacing.width & ~1; // make sure it's even jens@0: [self x_createDispenser: @"Red Ball.png" jens@0: position: CGPointMake(CGRectGetMinX(gridFrame)-2*pieceSize, jens@0: CGRectGetMinY(gridFrame)) jens@0: forPlayer: 0]; jens@0: [self x_createDispenser: @"White Ball.png" jens@0: position: CGPointMake(CGRectGetMaxX(gridFrame)+0.5*pieceSize, jens@0: CGRectGetMaxY(gridFrame)-1.5*pieceSize) jens@0: forPlayer: 1]; jens@0: jens@0: CGFloat captureHeight = gridFrame.size.height-4*pieceSize; jens@0: _captured[0] = [[Stack alloc] initWithStartPos: CGPointMake(2*pieceSize,0) jens@0: spacing: CGSizeMake(0,pieceSize) jens@0: wrapInterval: floor(captureHeight/pieceSize) jens@0: wrapSpacing: CGSizeMake(-pieceSize,0)]; jens@0: _captured[0].frame = CGRectMake(CGRectGetMinX(gridFrame)-3*pieceSize, jens@0: CGRectGetMinY(gridFrame)+3*pieceSize, jens@0: 2*pieceSize, captureHeight); jens@0: _captured[0].zPosition = kPieceZ+1; jens@0: [board addSublayer: _captured[0]]; jens@0: [_captured[0] release]; jens@0: jens@0: _captured[1] = [[Stack alloc] initWithStartPos: CGPointMake(0,captureHeight) jens@0: spacing: CGSizeMake(0,-pieceSize) jens@0: wrapInterval: floor(captureHeight/pieceSize) jens@0: wrapSpacing: CGSizeMake(pieceSize,0)]; jens@0: _captured[1].frame = CGRectMake(CGRectGetMaxX(gridFrame)+pieceSize, jens@0: CGRectGetMinY(gridFrame)+pieceSize, jens@0: 2*pieceSize, captureHeight); jens@0: _captured[1].zPosition = kPieceZ+1; jens@0: [board addSublayer: _captured[1]]; jens@0: [_captured[1] release]; jens@0: jens@0: [self nextPlayer]; jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: jens@0: - (BOOL) canBit: (Bit*)bit moveFrom: (id)srcHolder to: (id)dstHolder jens@0: { jens@0: Square *dst=(Square*)dstHolder; jens@0: jens@0: // There should be a check here for a "ko" (repeated position) ... exercise for the reader! jens@0: jens@0: // Check for suicidal move. First an easy check for an empty adjacent space: jens@0: NSArray *neighbors = dst.neighbors; jens@0: for( GridCell *c in neighbors ) jens@0: if( c.empty ) jens@0: return YES; // there's an empty space jens@0: // If the piece is surrounded, check the neighboring groups' liberties: jens@0: for( GridCell *c in neighbors ) { jens@0: int nLiberties; jens@0: [c getGroup: &nLiberties]; jens@0: if( c.bit.unfriendly ) { jens@0: if( nLiberties <= 1 ) jens@0: return YES; // the move captures, so it's not suicidal jens@0: } else { jens@0: if( nLiberties > 1 ) jens@0: return YES; // the stone joins a group with other liberties jens@0: } jens@0: } jens@0: return NO; jens@0: } jens@0: jens@0: jens@0: - (void) bit: (Bit*)bit movedFrom: (id)srcHolder to: (id)dstHolder jens@0: { jens@0: Square *dst=(Square*)dstHolder; jens@0: int curIndex = _currentPlayer.index; jens@0: // Check for captured enemy groups: jens@0: BOOL captured = NO; jens@0: for( GridCell *c in dst.neighbors ) jens@0: if( c.bit.unfriendly ) { jens@0: int nLiberties; jens@0: NSSet *group = [c getGroup: &nLiberties]; jens@0: if( nLiberties == 0 ) { jens@0: captured = YES; jens@0: for( GridCell *capture in group ) jens@0: [_captured[curIndex] addBit: capture.bit]; // Moves piece to POW camp! jens@0: } jens@0: } jens@0: if( captured ) jens@1: PlaySound(@"Pop"); jens@0: jens@0: [self nextPlayer]; jens@0: } jens@0: jens@0: jens@0: // This sample code makes no attempt to detect the end of the game, or count score, jens@0: // both of which are rather complex to decide in Go. jens@0: jens@0: jens@0: @end