Tweaks and fixes, including renaming Game's "board" property/ivar to "table", which is less confusing.
Released as part of Your Move 1.0a2.
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.
5 Redistribution and use in source and binary forms, with or without modification, are permitted
6 provided that the following conditions are met:
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.
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.
27 @implementation HexGrid
30 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
31 spacing: (CGSize)spacing
32 position: (CGPoint)pos
34 // Ignore given spacing.height; set it to make the hexagons regular.
35 CGFloat capHeight = spacing.height / 2 * tan(M_PI/6);
36 CGFloat side = spacing.height / 2 / cos(M_PI/6);
37 spacing.height = side + capHeight;
39 self = [super initWithRows: nRows columns: nColumns
43 _capHeight = capHeight;
45 self.bounds = CGRectMake(-1, -1,
46 (nColumns+0.5)*spacing.width + 2,
47 nRows*spacing.height+capHeight + 2);
48 _cellClass = [Hex class];
54 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
57 // Compute the horizontal spacing:
58 CGFloat s = floor(MIN( (frame.size.width -2.0)/nColumns,
59 (frame.size.height-2.0)/(nRows+0.5*tan(M_PI/6)) / (0.5*(tan(M_PI/6)+1/cos(M_PI/6))) ));
60 self = [self initWithRows: nRows columns: nColumns
61 spacing: CGSizeMake(s,s)
62 position: frame.origin];
65 CGRect curFrame = self.frame;
66 curFrame.origin.x = round( curFrame.origin.x + (frame.size.width - curFrame.size.width )/2.0f );
67 curFrame.origin.y = round( curFrame.origin.y + (frame.size.height- curFrame.size.height)/2.0f );
68 self.frame = curFrame;
76 CGPathRelease(_cellPath);
81 - (void) addCellsInHexagon
83 int size = _nRows - !(_nRows & 1); // make it odd
84 for( int row=0; row<_nRows; row++ ) {
85 int n; // # of hexes remaining in this row
87 n = size - abs(row-size/2);
90 int c0 = floor(((int)_nRows+1-n -(row&1))/2.0); // col of 1st remaining hex
92 for( int col=0; col<_nColumns; col++ )
93 if( col>=c0 && col<c0+n )
94 [self addCellAtRow: row column: col];
99 - (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col
100 suggestedFrame: (CGRect)frame
102 // Overridden to stagger the odd-numbered rows
103 BOOL stagger = (row & 1) != 0;
104 if( _reversed && (_nRows & 1) )
107 frame.origin.x += _spacing.width/2;
108 frame.size.height += _capHeight;
109 return [super createCellAtRow: row column: col suggestedFrame: frame];
113 // Returns a hexagonal CG path defining a cell's outline. Used by cells when drawing & hit-testing.
114 - (CGPathRef) cellPath
117 CGFloat x1 = _spacing.width/2;
118 CGFloat x2 = _spacing.width;
119 CGFloat y1 = _capHeight;
120 CGFloat y2 = y1 + _side;
121 CGFloat y3 = y2 + _capHeight;
122 CGPoint p[6] = { {0,y1}, {x1,0}, {x2,y1}, {x2,y2}, {x1,y3}, {0,y2} };
124 CGMutablePathRef path = CGPathCreateMutable();
125 CGPathAddLines(path, NULL, p, 6);
126 CGPathCloseSubpath(path);
144 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
146 CGContextSaveGState(ctx);
147 CGPoint pos = self.position;
148 CGContextTranslateCTM(ctx, pos.x, pos.y);
149 CGContextBeginPath(ctx);
150 CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
151 CGContextDrawPath(ctx, (fill ?kCGPathFill :kCGPathStroke));
153 if( !fill && self.highlighted ) {
154 // Highlight by drawing my outline in the highlight color:
155 CGContextSetStrokeColorWithColor(ctx, self.borderColor);
156 CGContextSetLineWidth(ctx,6);
157 CGContextBeginPath(ctx);
158 CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
159 CGContextDrawPath(ctx, kCGPathStroke);
161 CGContextRestoreGState(ctx);
165 - (BOOL)containsPoint:(CGPoint)p
167 return [super containsPoint: p]
168 && CGPathContainsPoint( ((HexGrid*)_grid).cellPath, NULL, p, NO );
172 - (void) setHighlighted: (BOOL)highlighted
174 if( highlighted != self.highlighted ) {
175 [super setHighlighted: highlighted];
176 [_grid setNeedsDisplay]; // So I'll be asked to redraw myself
182 #pragma mark NEIGHBORS:
185 - (NSArray*) neighbors
187 NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 6];
188 Hex* n[6] = {self.nw, self.ne, self.w, self.e, self.sw, self.se};
189 for( int i=0; i<6; i++ )
191 [neighbors addObject: n[i]];
195 - (Hex*) nw {return (Hex*)[_grid cellAtRow: _row+1 column: _column - ((_row+1)&1)];}
196 - (Hex*) ne {return (Hex*)[_grid cellAtRow: _row+1 column: _column + (_row&1)];}
197 - (Hex*) e {return (Hex*)[_grid cellAtRow: _row column: _column + 1];}
198 - (Hex*) se {return (Hex*)[_grid cellAtRow: _row-1 column: _column + (_row&1)];}
199 - (Hex*) sw {return (Hex*)[_grid cellAtRow: _row-1 column: _column - ((_row-1)&1)];}
200 - (Hex*) w {return (Hex*)[_grid cellAtRow: _row column: _column - 1];}
202 // Directions relative to the current player:
203 - (Hex*) fl {return self.fwdIsN ?self.nw :self.se;}
204 - (Hex*) fr {return self.fwdIsN ?self.ne :self.sw;}
205 - (Hex*) r {return self.fwdIsN ?self.e :self.w;}
206 - (Hex*) br {return self.fwdIsN ?self.se :self.nw;}
207 - (Hex*) bl {return self.fwdIsN ?self.sw :self.ne;}
208 - (Hex*) l {return self.fwdIsN ?self.w :self.e;}