Source/HexGrid.m
author Jens Alfke <jens@mooseyard.com>
Sun Mar 16 15:06:47 2008 -0700 (2008-03-16)
changeset 7 428a194e3e59
child 10 6c78cc6bd7a6
permissions -rw-r--r--
Game class now tracks board state and moves, as strings, and can step through its history.
Fixed another bug in Go (you could drag your captured stones back to the board!)
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 "HexGrid.h"
jens@0
    24
#import "Game.h"
jens@0
    25
jens@0
    26
jens@0
    27
@implementation HexGrid
jens@0
    28
jens@0
    29
jens@0
    30
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
jens@0
    31
            spacing: (CGSize)spacing
jens@0
    32
           position: (CGPoint)pos
jens@0
    33
{
jens@0
    34
    // Ignore given spacing.height; set it to make the hexagons regular.
jens@0
    35
    CGFloat capHeight = spacing.height / 2 * tan(M_PI/6);
jens@0
    36
    CGFloat side = spacing.height / 2 / cos(M_PI/6);
jens@0
    37
    spacing.height = side + capHeight;
jens@0
    38
    
jens@0
    39
    self = [super initWithRows: nRows columns: nColumns
jens@0
    40
                       spacing: spacing
jens@0
    41
                      position: pos];
jens@0
    42
    if( self ) {
jens@0
    43
        _capHeight = capHeight;
jens@0
    44
        _side = side;
jens@0
    45
        self.bounds = CGRectMake(-1, -1, 
jens@0
    46
                                 (nColumns+0.5)*spacing.width + 2,
jens@0
    47
                                 nRows*spacing.height+capHeight + 2);
jens@0
    48
        _cellClass = [Hex class];
jens@0
    49
    }
jens@0
    50
    return self;
jens@0
    51
}
jens@0
    52
jens@0
    53
jens@0
    54
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
jens@0
    55
              frame: (CGRect)frame;
jens@0
    56
{
jens@0
    57
    // Compute the horizontal spacing:
jens@0
    58
    CGFloat s = floor(MIN( (frame.size.width -2.0)/nColumns,
jens@0
    59
                         (frame.size.height-2.0)/(nRows+0.5*tan(M_PI/6)) / (0.5*(tan(M_PI/6)+1/cos(M_PI/6))) ));
jens@0
    60
    return [self initWithRows: nRows columns: nColumns
jens@0
    61
                      spacing: CGSizeMake(s,s)
jens@0
    62
                     position: frame.origin];
jens@0
    63
}
jens@0
    64
    
jens@0
    65
    
jens@0
    66
- (void) dealloc
jens@0
    67
{
jens@0
    68
    CGPathRelease(_cellPath);
jens@0
    69
    [super dealloc];
jens@0
    70
}
jens@0
    71
jens@0
    72
jens@0
    73
- (void) addCellsInHexagon
jens@0
    74
{
jens@0
    75
    int size = _nRows - !(_nRows & 1);      // make it odd
jens@0
    76
    for( int row=0; row<_nRows; row++ ) {
jens@0
    77
        int n;                              // # of hexes remaining in this row
jens@0
    78
        if( row < size )
jens@0
    79
            n = size - abs(row-size/2);
jens@0
    80
        else
jens@0
    81
            n = 0;
jens@0
    82
        int c0 = floor(((int)_nRows+1-n -(row&1))/2.0);       // col of 1st remaining hex
jens@0
    83
jens@0
    84
        for( int col=0; col<_nColumns; col++ )
jens@0
    85
            if( col>=c0 && col<c0+n )
jens@0
    86
                [self addCellAtRow: row column: col];
jens@0
    87
    }
jens@0
    88
}
jens@0
    89
jens@0
    90
jens@0
    91
- (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col 
jens@0
    92
               suggestedFrame: (CGRect)frame
jens@0
    93
{
jens@0
    94
    // Overridden to stagger the odd-numbered rows
jens@0
    95
    if( row & 1 )
jens@0
    96
        frame.origin.x += _spacing.width/2;
jens@0
    97
    frame.size.height += _capHeight;
jens@0
    98
    return [super createCellAtRow: row column: col suggestedFrame: frame];
jens@0
    99
}
jens@0
   100
jens@0
   101
jens@0
   102
// Returns a hexagonal CG path defining a cell's outline. Used by cells when drawing & hit-testing.
jens@0
   103
- (CGPathRef) cellPath
jens@0
   104
{
jens@0
   105
    if( ! _cellPath ) {
jens@0
   106
        CGFloat x1 = _spacing.width/2;
jens@0
   107
        CGFloat x2 = _spacing.width;
jens@0
   108
        CGFloat y1 = _capHeight;
jens@0
   109
        CGFloat y2 = y1 + _side;
jens@0
   110
        CGFloat y3 = y2 + _capHeight;
jens@0
   111
        CGPoint p[6] = { {0,y1}, {x1,0}, {x2,y1}, {x2,y2}, {x1,y3}, {0,y2} };
jens@0
   112
        
jens@0
   113
        CGMutablePathRef path = CGPathCreateMutable();
jens@0
   114
        CGPathAddLines(path, NULL, p, 6);
jens@0
   115
        CGPathCloseSubpath(path);
jens@0
   116
        _cellPath = path;
jens@0
   117
    }
jens@0
   118
    return _cellPath;
jens@0
   119
}
jens@0
   120
jens@0
   121
jens@0
   122
@end
jens@0
   123
jens@0
   124
jens@0
   125
jens@0
   126
jens@0
   127
jens@0
   128
#pragma mark -
jens@0
   129
jens@0
   130
@implementation Hex
jens@0
   131
jens@0
   132
jens@0
   133
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   134
{
jens@0
   135
    CGContextSaveGState(ctx);
jens@0
   136
    CGPoint pos = self.position;
jens@0
   137
    CGContextTranslateCTM(ctx, pos.x, pos.y);
jens@0
   138
    CGContextBeginPath(ctx);
jens@0
   139
    CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
jens@0
   140
    CGContextDrawPath(ctx, (fill ?kCGPathFill :kCGPathStroke));
jens@0
   141
    
jens@0
   142
    if( !fill && self.highlighted ) {
jens@0
   143
        // Highlight by drawing my outline in the highlight color:
jens@0
   144
        CGContextSetStrokeColorWithColor(ctx, self.borderColor);
jens@0
   145
        CGContextSetLineWidth(ctx,6);
jens@0
   146
        CGContextBeginPath(ctx);
jens@0
   147
        CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
jens@0
   148
        CGContextDrawPath(ctx, kCGPathStroke);
jens@0
   149
    }
jens@0
   150
    CGContextRestoreGState(ctx);
jens@0
   151
}
jens@0
   152
jens@0
   153
jens@0
   154
- (BOOL)containsPoint:(CGPoint)p
jens@0
   155
{
jens@0
   156
    return [super containsPoint: p]
jens@0
   157
        && CGPathContainsPoint( ((HexGrid*)_grid).cellPath, NULL, p, NO );
jens@0
   158
}
jens@0
   159
jens@0
   160
jens@0
   161
- (void) setHighlighted: (BOOL)highlighted
jens@0
   162
{
jens@0
   163
    if( highlighted != self.highlighted ) {
jens@0
   164
        [super setHighlighted: highlighted];
jens@0
   165
        [_grid setNeedsDisplay];        // So I'll be asked to redraw myself
jens@0
   166
    }
jens@0
   167
}
jens@0
   168
jens@0
   169
jens@0
   170
#pragma mark -
jens@0
   171
#pragma mark NEIGHBORS:
jens@0
   172
jens@0
   173
jens@0
   174
- (NSArray*) neighbors
jens@0
   175
{
jens@0
   176
    NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 6];
jens@0
   177
    Hex* n[6] = {self.nw, self.ne, self.w, self.e, self.sw, self.se};
jens@0
   178
    for( int i=0; i<6; i++ )
jens@0
   179
        if( n[i] )
jens@0
   180
            [neighbors addObject: n[i]];
jens@0
   181
    return neighbors;
jens@0
   182
}
jens@0
   183
jens@0
   184
- (Hex*) nw     {return (Hex*)[_grid cellAtRow: _row+1 column: _column - ((_row+1)&1)];}
jens@0
   185
- (Hex*) ne     {return (Hex*)[_grid cellAtRow: _row+1 column: _column + (_row&1)];}
jens@0
   186
- (Hex*) e      {return (Hex*)[_grid cellAtRow: _row   column: _column + 1];}
jens@0
   187
- (Hex*) se     {return (Hex*)[_grid cellAtRow: _row-1 column: _column + (_row&1)];}
jens@0
   188
- (Hex*) sw     {return (Hex*)[_grid cellAtRow: _row-1 column: _column - ((_row-1)&1)];}
jens@0
   189
- (Hex*) w      {return (Hex*)[_grid cellAtRow: _row   column: _column - 1];}
jens@0
   190
jens@0
   191
// Directions relative to the current player:
jens@0
   192
- (Hex*) fl     {return self.fwdIsN ?self.nw :self.se;}
jens@0
   193
- (Hex*) fr     {return self.fwdIsN ?self.ne :self.sw;}
jens@0
   194
- (Hex*) r      {return self.fwdIsN ?self.e  :self.w;}
jens@0
   195
- (Hex*) br     {return self.fwdIsN ?self.se :self.nw;}
jens@0
   196
- (Hex*) bl     {return self.fwdIsN ?self.sw :self.ne;}
jens@0
   197
- (Hex*) l      {return self.fwdIsN ?self.w  :self.e;}
jens@0
   198
jens@0
   199
jens@0
   200
@end