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