Source/GoGame.m
author Jens Alfke <jens@mooseyard.com>
Thu May 29 15:04:06 2008 -0700 (2008-05-29)
changeset 9 a59acc683080
parent 6 af9b2b929b03
child 10 6c78cc6bd7a6
permissions -rw-r--r--
Finally fixed the slow animation performance of board games; all it took was changing the board's z index from 1 to 0, somehow. Games working well now.
jens@0
     1
/*  This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
jens@0
     2
    http://developer.apple.com/samplecode/GeekGameBoard/
jens@0
     3
    Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
jens@0
     4
jens@0
     5
    Redistribution and use in source and binary forms, with or without modification, are permitted
jens@0
     6
    provided that the following conditions are met:
jens@0
     7
jens@0
     8
    * Redistributions of source code must retain the above copyright notice, this list of conditions
jens@0
     9
      and the following disclaimer.
jens@0
    10
    * Redistributions in binary form must reproduce the above copyright notice, this list of
jens@0
    11
      conditions and the following disclaimer in the documentation and/or other materials provided
jens@0
    12
      with the distribution.
jens@0
    13
jens@0
    14
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
jens@0
    15
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
jens@0
    16
    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
jens@0
    17
    BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
jens@0
    18
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
jens@0
    19
    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
jens@0
    20
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
jens@0
    21
    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
jens@0
    22
*/
jens@0
    23
#import "GoGame.h"
jens@0
    24
#import "Grid.h"
jens@0
    25
#import "Piece.h"
jens@0
    26
#import "Dispenser.h"
jens@0
    27
#import "Stack.h"
jens@0
    28
#import "QuartzUtils.h"
jens@1
    29
#import "GGBUtils.h"
jens@0
    30
jens@0
    31
jens@0
    32
@implementation GoGame
jens@0
    33
jens@0
    34
jens@1
    35
- (id) initWithBoard: (GGBLayer*)board
jens@0
    36
{
jens@0
    37
    self = [super initWithBoard: board];
jens@0
    38
    if (self != nil) {
jens@0
    39
        [self setNumberOfPlayers: 2];
jens@3
    40
        [(Player*)[_players objectAtIndex: 0] setName: @"Red"];
jens@3
    41
        [(Player*)[_players objectAtIndex: 1] setName: @"White"];
jens@0
    42
        
jens@0
    43
        CGSize size = board.bounds.size;
jens@3
    44
        CGFloat boardSide = MIN(size.width,size.height);
jens@0
    45
        RectGrid *grid = [[RectGrid alloc] initWithRows: 9 columns: 9 
jens@3
    46
                                                  frame: CGRectMake(floor((size.width-boardSide)/2),
jens@3
    47
                                                                    floor((size.height-boardSide)/2),
jens@3
    48
                                                                    boardSide,boardSide)];
jens@0
    49
        _grid = grid;
jens@0
    50
        grid.backgroundColor = GetCGPatternNamed(@"Wood.jpg");
jens@0
    51
        grid.borderColor = kTranslucentLightGrayColor;
jens@0
    52
        grid.borderWidth = 2;
jens@0
    53
        grid.lineColor = kTranslucentGrayColor;
jens@0
    54
        grid.cellClass = [GoSquare class];
jens@0
    55
        [grid addAllCells];
jens@0
    56
        ((GoSquare*)[grid cellAtRow: 2 column: 2]).dotted = YES;
jens@0
    57
        ((GoSquare*)[grid cellAtRow: 6 column: 6]).dotted = YES;
jens@0
    58
        ((GoSquare*)[grid cellAtRow: 2 column: 6]).dotted = YES;
jens@0
    59
        ((GoSquare*)[grid cellAtRow: 6 column: 2]).dotted = YES;
jens@0
    60
        grid.usesDiagonals = grid.allowsMoves = grid.allowsCaptures = NO;
jens@0
    61
        [board addSublayer: grid];
jens@0
    62
        [grid release];
jens@0
    63
        
jens@0
    64
        CGRect gridFrame = grid.frame;
jens@0
    65
        CGFloat pieceSize = (int)grid.spacing.width & ~1;  // make sure it's even
jens@0
    66
        CGFloat captureHeight = gridFrame.size.height-4*pieceSize;
jens@0
    67
        _captured[0] = [[Stack alloc] initWithStartPos: CGPointMake(2*pieceSize,0)
jens@0
    68
                                               spacing: CGSizeMake(0,pieceSize)
jens@0
    69
                                          wrapInterval: floor(captureHeight/pieceSize)
jens@0
    70
                                           wrapSpacing: CGSizeMake(-pieceSize,0)];
jens@0
    71
        _captured[0].frame = CGRectMake(CGRectGetMinX(gridFrame)-3*pieceSize, 
jens@0
    72
                                          CGRectGetMinY(gridFrame)+3*pieceSize,
jens@0
    73
                                          2*pieceSize, captureHeight);
jens@0
    74
        _captured[0].zPosition = kPieceZ+1;
jens@0
    75
        [board addSublayer: _captured[0]];
jens@0
    76
        [_captured[0] release];
jens@0
    77
        
jens@0
    78
        _captured[1] = [[Stack alloc] initWithStartPos: CGPointMake(0,captureHeight)
jens@0
    79
                                               spacing: CGSizeMake(0,-pieceSize)
jens@0
    80
                                          wrapInterval: floor(captureHeight/pieceSize)
jens@0
    81
                                           wrapSpacing: CGSizeMake(pieceSize,0)];
jens@0
    82
        _captured[1].frame = CGRectMake(CGRectGetMaxX(gridFrame)+pieceSize, 
jens@0
    83
                                          CGRectGetMinY(gridFrame)+pieceSize,
jens@0
    84
                                          2*pieceSize, captureHeight);
jens@0
    85
        _captured[1].zPosition = kPieceZ+1;
jens@0
    86
        [board addSublayer: _captured[1]];
jens@0
    87
        [_captured[1] release];
jens@0
    88
jens@0
    89
        [self nextPlayer];
jens@7
    90
        PreloadSound(@"Pop");
jens@0
    91
}
jens@0
    92
    return self;
jens@0
    93
}
jens@0
    94
jens@0
    95
jens@3
    96
- (Bit*) bitToPlaceInHolder: (id<BitHolder>)holder
jens@3
    97
{
jens@3
    98
    if( holder.bit != nil || ! [holder isKindOfClass: [GoSquare class]] )
jens@3
    99
        return nil;
jens@3
   100
    NSString *imageName = self.currentPlayer.index ?@"White Ball.png" :@"Red Ball.png";
jens@3
   101
    CGFloat pieceSize = (int)(_grid.spacing.width * 0.9) & ~1;  // make sure it's even
jens@3
   102
    Piece *stone = [[Piece alloc] initWithImageNamed: imageName scale: pieceSize];
jens@3
   103
    stone.owner = self.currentPlayer;
jens@3
   104
    return [stone autorelease];
jens@3
   105
}
jens@3
   106
jens@3
   107
jens@7
   108
- (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder
jens@7
   109
{
jens@7
   110
    return (srcHolder==nil);
jens@7
   111
}
jens@7
   112
jens@7
   113
jens@0
   114
- (BOOL) canBit: (Bit*)bit moveFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
jens@0
   115
{
jens@7
   116
    if( srcHolder!=nil || ! [dstHolder isKindOfClass: [Square class]] )
jens@6
   117
        return NO;
jens@0
   118
    Square *dst=(Square*)dstHolder;
jens@0
   119
    
jens@0
   120
    // There should be a check here for a "ko" (repeated position) ... exercise for the reader!
jens@0
   121
    
jens@0
   122
    // Check for suicidal move. First an easy check for an empty adjacent space:
jens@0
   123
    NSArray *neighbors = dst.neighbors;
jens@0
   124
    for( GridCell *c in neighbors )
jens@0
   125
        if( c.empty )
jens@0
   126
            return YES;                     // there's an empty space
jens@0
   127
    // If the piece is surrounded, check the neighboring groups' liberties:
jens@0
   128
    for( GridCell *c in neighbors ) {
jens@0
   129
        int nLiberties;
jens@0
   130
        [c getGroup: &nLiberties];
jens@0
   131
        if( c.bit.unfriendly ) {
jens@0
   132
            if( nLiberties <= 1 )
jens@0
   133
                return YES;             // the move captures, so it's not suicidal
jens@0
   134
        } else {
jens@0
   135
            if( nLiberties > 1 )
jens@0
   136
                return YES;             // the stone joins a group with other liberties
jens@0
   137
        }
jens@0
   138
    }
jens@0
   139
    return NO;
jens@0
   140
}
jens@0
   141
jens@0
   142
jens@0
   143
- (void) bit: (Bit*)bit movedFrom: (id<BitHolder>)srcHolder to: (id<BitHolder>)dstHolder
jens@0
   144
{
jens@0
   145
    Square *dst=(Square*)dstHolder;
jens@0
   146
    int curIndex = _currentPlayer.index;
jens@0
   147
    // Check for captured enemy groups:
jens@0
   148
    BOOL captured = NO;
jens@0
   149
    for( GridCell *c in dst.neighbors )
jens@0
   150
        if( c.bit.unfriendly ) {
jens@0
   151
            int nLiberties;
jens@0
   152
            NSSet *group = [c getGroup: &nLiberties];
jens@0
   153
            if( nLiberties == 0 ) {
jens@0
   154
                captured = YES;
jens@0
   155
                for( GridCell *capture in group )
jens@0
   156
                    [_captured[curIndex] addBit: capture.bit];  // Moves piece to POW camp!
jens@0
   157
            }
jens@0
   158
        }
jens@0
   159
    if( captured )
jens@1
   160
        PlaySound(@"Pop");
jens@0
   161
        
jens@0
   162
    [self nextPlayer];
jens@0
   163
}
jens@0
   164
jens@0
   165
jens@0
   166
// This sample code makes no attempt to detect the end of the game, or count score,
jens@0
   167
// both of which are rather complex to decide in Go.
jens@0
   168
jens@0
   169
jens@0
   170
@end