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@1: - (id) initWithBoard: (GGBLayer*)board jens@0: { jens@0: self = [super initWithBoard: board]; jens@0: if (self != nil) { jens@0: [self setNumberOfPlayers: 2]; jens@3: [(Player*)[_players objectAtIndex: 0] setName: @"Red"]; jens@3: [(Player*)[_players objectAtIndex: 1] setName: @"White"]; jens@0: jens@0: CGSize size = board.bounds.size; jens@3: CGFloat boardSide = MIN(size.width,size.height); jens@0: RectGrid *grid = [[RectGrid alloc] initWithRows: 9 columns: 9 jens@3: frame: CGRectMake(floor((size.width-boardSide)/2), jens@3: floor((size.height-boardSide)/2), jens@3: boardSide,boardSide)]; 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: 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@7: PreloadSound(@"Pop"); jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: jens@3: - (Bit*) bitToPlaceInHolder: (id)holder jens@3: { jens@3: if( holder.bit != nil || ! [holder isKindOfClass: [GoSquare class]] ) jens@3: return nil; jens@3: NSString *imageName = self.currentPlayer.index ?@"White Ball.png" :@"Red Ball.png"; jens@3: CGFloat pieceSize = (int)(_grid.spacing.width * 0.9) & ~1; // make sure it's even jens@3: Piece *stone = [[Piece alloc] initWithImageNamed: imageName scale: pieceSize]; jens@3: stone.owner = self.currentPlayer; jens@3: return [stone autorelease]; jens@3: } jens@3: jens@3: jens@7: - (BOOL) canBit: (Bit*)bit moveFrom: (id)srcHolder jens@7: { jens@7: return (srcHolder==nil); jens@7: } jens@7: jens@7: jens@0: - (BOOL) canBit: (Bit*)bit moveFrom: (id)srcHolder to: (id)dstHolder jens@0: { jens@7: if( srcHolder!=nil || ! [dstHolder isKindOfClass: [Square class]] ) jens@6: return NO; 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