Source/GoGame.m
author Jens Alfke <jens@mooseyard.com>
Wed Mar 12 15:51:32 2008 -0700 (2008-03-12)
changeset 6 af9b2b929b03
parent 3 40d225cf9c43
child 7 428a194e3e59
permissions -rw-r--r--
Fixed: An exception in the Go game if you mouse down on the board but then drag to the captured-pieces area and release the mouse.
     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     if( ! [dstHolder isKindOfClass: [Square class]] )
   110         return NO;
   111     Square *dst=(Square*)dstHolder;
   112     
   113     // There should be a check here for a "ko" (repeated position) ... exercise for the reader!
   114     
   115     // Check for suicidal move. First an easy check for an empty adjacent space:
   116     NSArray *neighbors = dst.neighbors;
   117     for( GridCell *c in neighbors )
   118         if( c.empty )
   119             return YES;                     // there's an empty space
   120     // If the piece is surrounded, check the neighboring groups' liberties:
   121     for( GridCell *c in neighbors ) {
   122         int nLiberties;
   123         [c getGroup: &nLiberties];
   124         if( c.bit.unfriendly ) {
   125             if( nLiberties <= 1 )
   126                 return YES;             // the move captures, so it's not suicidal
   127         } else {
   128             if( nLiberties > 1 )
   129                 return YES;             // the stone joins a group with other liberties
   130         }
   131     }
   132     return NO;
   133 }
   134 
   135 
   136 - (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
   137 {
   138     Square *dst=(Square*)dstHolder;
   139     int curIndex = _currentPlayer.index;
   140     // Check for captured enemy groups:
   141     BOOL captured = NO;
   142     for( GridCell *c in dst.neighbors )
   143         if( c.bit.unfriendly ) {
   144             int nLiberties;
   145             NSSet *group = [c getGroup: &nLiberties];
   146             if( nLiberties == 0 ) {
   147                 captured = YES;
   148                 for( GridCell *capture in group )
   149                     [_captured[curIndex] addBit: capture.bit];  // Moves piece to POW camp!
   150             }
   151         }
   152     if( captured )
   153         PlaySound(@"Pop");
   154         
   155     [self nextPlayer];
   156 }
   157 
   158 
   159 // This sample code makes no attempt to detect the end of the game, or count score,
   160 // both of which are rather complex to decide in Go.
   161 
   162 
   163 @end