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@10: + (int) dimensions {return 19;} jens@10: jens@24: + (const GridCoord*) spotCoords jens@24: { jens@24: static GridCoord const sSpots[10]={ { 3,3}, { 3,9}, { 3,15}, jens@24: { 9,3}, { 9,9}, { 9,15}, snej@27: {15,3}, {15,9}, {15,15}, snej@27: {(unsigned)NSNotFound,(unsigned)NSNotFound} }; jens@24: return sSpots; jens@24: } jens@24: jens@10: - (id) init jens@0: { jens@10: self = [super init]; jens@0: if (self != nil) { jens@0: [self setNumberOfPlayers: 2]; jens@21: [(Player*)[_players objectAtIndex: 0] setName: @"Black"]; jens@3: [(Player*)[_players objectAtIndex: 1] setName: @"White"]; jens@10: } jens@0: return self; jens@0: } jens@10: jens@10: - (void) setUpBoard jens@10: { jens@10: int dimensions = [[self class] dimensions]; jens@21: CGRect tableBounds = _table.bounds; jens@21: CGSize size = tableBounds.size; jens@21: CGFloat boardSide = MIN(size.width* dimensions/(CGFloat)(dimensions+2),size.height); jens@16: RectGrid *board = [[RectGrid alloc] initWithRows: dimensions columns: dimensions jens@10: frame: CGRectMake(floor((size.width-boardSide)/2), jens@10: floor((size.height-boardSide)/2), jens@10: boardSide,boardSide)]; jens@16: _board = board; jens@10: /* jens@10: grid.backgroundColor = GetCGPatternNamed(@"Wood.jpg"); jens@10: grid.borderColor = kTranslucentLightGrayColor; jens@10: grid.borderWidth = 2; jens@10: */ jens@16: board.lineColor = kTranslucentGrayColor; jens@16: board.cellClass = [GoSquare class]; jens@24: board.usesDiagonals = board.allowsMoves = board.allowsCaptures = NO; jens@16: [board addAllCells]; jens@24: const GridCoord *spots = [[self class] spotCoords]; snej@27: for( int i=0; spots[i].row!=(unsigned)NSNotFound; i++ ) jens@24: ((GoSquare*)[board cellAtRow: spots[i].row column: spots[i].col]).dotted = YES; jens@16: [_table addSublayer: board]; jens@16: [board release]; jens@10: jens@16: CGRect gridFrame = board.frame; jens@16: CGFloat pieceSize = (int)board.spacing.width & ~1; // make sure it's even jens@21: CGFloat captureMinY = CGRectGetMinY(tableBounds) + pieceSize/2, jens@21: captureHeight = size.height - pieceSize; jens@21: _captured[0] = [[Stack alloc] initWithStartPos: CGPointMake(pieceSize/2,0) jens@10: spacing: CGSizeMake(0,pieceSize) jens@10: wrapInterval: floor(captureHeight/pieceSize) jens@21: wrapSpacing: CGSizeMake(pieceSize,0)]; jens@21: _captured[0].frame = CGRectMake(CGRectGetMinX(tableBounds), jens@21: captureMinY, jens@21: CGRectGetMinX(gridFrame)-CGRectGetMinX(tableBounds), jens@21: captureHeight); jens@10: _captured[0].zPosition = kPieceZ+1; jens@16: [_table addSublayer: _captured[0]]; jens@10: [_captured[0] release]; jens@10: jens@21: _captured[1] = [[Stack alloc] initWithStartPos: CGPointMake(pieceSize/2,captureHeight) jens@10: spacing: CGSizeMake(0,-pieceSize) jens@10: wrapInterval: floor(captureHeight/pieceSize) jens@21: wrapSpacing: CGSizeMake(-pieceSize,0)]; jens@21: _captured[1].frame = CGRectMake(CGRectGetMaxX(gridFrame), jens@21: captureMinY, jens@21: CGRectGetMaxX(tableBounds)-CGRectGetMaxX(gridFrame), jens@21: captureHeight); jens@21: _captured[1].startPos = CGPointMake(CGRectGetMaxX(_captured[1].bounds)-pieceSize/2, captureHeight); jens@10: _captured[1].zPosition = kPieceZ+1; jens@16: [_table addSublayer: _captured[1]]; jens@10: [_captured[1] release]; jens@0: jens@10: PreloadSound(@"Pop"); jens@10: } jens@10: jens@10: - (CGImageRef) iconForPlayer: (int)playerNum jens@10: { jens@22: return GetCGImageNamed( playerNum ?@"ball-white.png" :@"ball-black.png" ); jens@10: } jens@10: jens@10: - (Piece*) pieceForPlayer: (int)index jens@10: { jens@22: NSString *imageName = index ?@"ball-white.png" :@"ball-black.png"; jens@22: CGFloat pieceSize = (int)(_board.spacing.width * 1.0) & ~1; // make sure it's even jens@10: Piece *stone = [[Piece alloc] initWithImageNamed: imageName scale: pieceSize]; jens@10: stone.owner = [self.players objectAtIndex: index]; jens@10: return [stone autorelease]; jens@10: } 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@10: else jens@10: return [self pieceForPlayer: self.currentPlayer.index]; 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@10: int curIndex = self.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@10: jens@10: [self.currentTurn addToMove: dst.name]; jens@10: [self endTurn]; 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@10: #pragma mark - jens@10: #pragma mark STATE: jens@10: jens@10: jens@10: - (NSString*) stateString jens@10: { jens@16: int n = _board.rows; jens@10: unichar state[n*n]; jens@10: for( int y=0; y