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 "Grid.h"
|
jens@0
|
24 |
#import "Bit.h"
|
jens@12
|
25 |
#import "Piece.h"
|
jens@12
|
26 |
#import "Game+Protected.h"
|
jens@10
|
27 |
#import "Player.h"
|
jens@0
|
28 |
#import "QuartzUtils.h"
|
jens@0
|
29 |
|
jens@0
|
30 |
|
jens@0
|
31 |
@implementation Grid
|
jens@0
|
32 |
|
jens@0
|
33 |
|
jens@0
|
34 |
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
|
jens@0
|
35 |
spacing: (CGSize)spacing
|
jens@0
|
36 |
position: (CGPoint)pos
|
jens@0
|
37 |
{
|
jens@0
|
38 |
NSParameterAssert(nRows>0 && nColumns>0);
|
jens@0
|
39 |
self = [super init];
|
jens@0
|
40 |
if( self ) {
|
jens@0
|
41 |
_nRows = nRows;
|
jens@0
|
42 |
_nColumns = nColumns;
|
jens@0
|
43 |
_spacing = spacing;
|
jens@0
|
44 |
_cellClass = [GridCell class];
|
jens@1
|
45 |
self.lineColor = kBlackColor;
|
jens@0
|
46 |
_allowsMoves = YES;
|
jens@0
|
47 |
_usesDiagonals = YES;
|
jens@0
|
48 |
|
jens@0
|
49 |
self.bounds = CGRectMake(-1, -1, nColumns*spacing.width+2, nRows*spacing.height+2);
|
jens@0
|
50 |
self.position = pos;
|
jens@0
|
51 |
self.anchorPoint = CGPointMake(0,0);
|
jens@0
|
52 |
self.zPosition = kBoardZ;
|
jens@0
|
53 |
self.needsDisplayOnBoundsChange = YES;
|
jens@0
|
54 |
|
jens@0
|
55 |
unsigned n = nRows*nColumns;
|
jens@0
|
56 |
_cells = [[NSMutableArray alloc] initWithCapacity: n];
|
jens@0
|
57 |
id null = [NSNull null];
|
jens@0
|
58 |
while( n-- > 0 )
|
jens@0
|
59 |
[_cells addObject: null];
|
jens@0
|
60 |
|
jens@0
|
61 |
[self setNeedsDisplay];
|
jens@0
|
62 |
}
|
jens@0
|
63 |
return self;
|
jens@0
|
64 |
}
|
jens@0
|
65 |
|
jens@0
|
66 |
|
jens@0
|
67 |
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
|
jens@0
|
68 |
frame: (CGRect)frame
|
jens@0
|
69 |
{
|
jens@0
|
70 |
CGFloat spacing = floor(MIN( (frame.size.width -2)/(CGFloat)nColumns,
|
jens@0
|
71 |
(frame.size.height-2)/(CGFloat)nRows) );
|
jens@3
|
72 |
CGSize size = CGSizeMake(nColumns*spacing+2, nRows*spacing+2);
|
jens@3
|
73 |
CGPoint position = frame.origin;
|
jens@3
|
74 |
position.x += round( (frame.size.width -size.width )/2.0 );
|
jens@3
|
75 |
position.y += round( (frame.size.height-size.height)/2.0 );
|
jens@3
|
76 |
|
jens@0
|
77 |
return [self initWithRows: nRows columns: nColumns
|
jens@0
|
78 |
spacing: CGSizeMake(spacing,spacing)
|
jens@3
|
79 |
position: position];
|
jens@0
|
80 |
}
|
jens@0
|
81 |
|
jens@0
|
82 |
|
jens@0
|
83 |
- (void) dealloc
|
jens@0
|
84 |
{
|
jens@0
|
85 |
CGColorRelease(_cellColor);
|
jens@0
|
86 |
CGColorRelease(_lineColor);
|
jens@0
|
87 |
[_cells release];
|
jens@0
|
88 |
[super dealloc];
|
jens@0
|
89 |
}
|
jens@0
|
90 |
|
jens@0
|
91 |
|
jens@0
|
92 |
static void setcolor( CGColorRef *var, CGColorRef color )
|
jens@0
|
93 |
{
|
jens@0
|
94 |
if( color != *var ) {
|
jens@0
|
95 |
CGColorRelease(*var);
|
jens@0
|
96 |
*var = CGColorRetain(color);
|
jens@0
|
97 |
}
|
jens@0
|
98 |
}
|
jens@0
|
99 |
|
jens@0
|
100 |
- (CGColorRef) cellColor {return _cellColor;}
|
jens@0
|
101 |
- (void) setCellColor: (CGColorRef)cellColor {setcolor(&_cellColor,cellColor);}
|
jens@0
|
102 |
|
jens@0
|
103 |
- (CGColorRef) lineColor {return _lineColor;}
|
jens@0
|
104 |
- (void) setLineColor: (CGColorRef)lineColor {setcolor(&_lineColor,lineColor);}
|
jens@0
|
105 |
|
jens@11
|
106 |
- (CGImageRef) backgroundImage {return _backgroundImage;}
|
jens@11
|
107 |
- (void) setBackgroundImage: (CGImageRef)image
|
jens@11
|
108 |
{
|
jens@11
|
109 |
if( image != _backgroundImage ) {
|
jens@11
|
110 |
CGImageRelease(_backgroundImage);
|
jens@11
|
111 |
_backgroundImage = CGImageRetain(image);
|
jens@11
|
112 |
}
|
jens@11
|
113 |
}
|
jens@11
|
114 |
|
jens@0
|
115 |
@synthesize cellClass=_cellClass, rows=_nRows, columns=_nColumns, spacing=_spacing,
|
jens@12
|
116 |
usesDiagonals=_usesDiagonals, allowsMoves=_allowsMoves, allowsCaptures=_allowsCaptures;
|
jens@0
|
117 |
|
jens@0
|
118 |
|
jens@0
|
119 |
#pragma mark -
|
jens@0
|
120 |
#pragma mark GEOMETRY:
|
jens@0
|
121 |
|
jens@0
|
122 |
|
jens@0
|
123 |
- (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col
|
jens@0
|
124 |
{
|
jens@0
|
125 |
if( row < _nRows && col < _nColumns ) {
|
jens@0
|
126 |
id cell = [_cells objectAtIndex: row*_nColumns+col];
|
jens@0
|
127 |
if( cell != [NSNull null] )
|
jens@0
|
128 |
return cell;
|
jens@0
|
129 |
}
|
jens@0
|
130 |
return nil;
|
jens@0
|
131 |
}
|
jens@0
|
132 |
|
jens@0
|
133 |
|
jens@0
|
134 |
/** Subclasses can override this, to change the cell's class or frame. */
|
jens@0
|
135 |
- (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col
|
jens@0
|
136 |
suggestedFrame: (CGRect)frame
|
jens@0
|
137 |
{
|
jens@7
|
138 |
GridCell *cell = [[_cellClass alloc] initWithGrid: self
|
jens@0
|
139 |
row: row column: col
|
jens@7
|
140 |
frame: frame];
|
jens@10
|
141 |
cell.name = [NSString stringWithFormat: @"%c%u", ('A'+row),(1+col)];
|
jens@7
|
142 |
return [cell autorelease];
|
jens@0
|
143 |
}
|
jens@0
|
144 |
|
jens@0
|
145 |
|
jens@0
|
146 |
- (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col
|
jens@0
|
147 |
{
|
jens@0
|
148 |
NSParameterAssert(row<_nRows);
|
jens@0
|
149 |
NSParameterAssert(col<_nColumns);
|
jens@0
|
150 |
unsigned index = row*_nColumns+col;
|
jens@0
|
151 |
GridCell *cell = [_cells objectAtIndex: index];
|
jens@0
|
152 |
if( (id)cell == [NSNull null] ) {
|
jens@0
|
153 |
CGRect frame = CGRectMake(col*_spacing.width, row*_spacing.height,
|
jens@0
|
154 |
_spacing.width,_spacing.height);
|
jens@0
|
155 |
cell = [self createCellAtRow: row column: col suggestedFrame: frame];
|
jens@0
|
156 |
if( cell ) {
|
jens@0
|
157 |
[_cells replaceObjectAtIndex: index withObject: cell];
|
jens@0
|
158 |
[self addSublayer: cell];
|
jens@0
|
159 |
[self setNeedsDisplay];
|
jens@0
|
160 |
}
|
jens@0
|
161 |
}
|
jens@0
|
162 |
return cell;
|
jens@0
|
163 |
}
|
jens@0
|
164 |
|
jens@0
|
165 |
|
jens@0
|
166 |
- (void) addAllCells
|
jens@0
|
167 |
{
|
jens@0
|
168 |
for( int row=_nRows-1; row>=0; row-- ) // makes 'upper' cells be in 'back'
|
jens@0
|
169 |
for( int col=0; col<_nColumns; col++ )
|
jens@0
|
170 |
[self addCellAtRow: row column: col];
|
jens@0
|
171 |
}
|
jens@0
|
172 |
|
jens@0
|
173 |
|
jens@0
|
174 |
- (void) removeCellAtRow: (unsigned)row column: (unsigned)col
|
jens@0
|
175 |
{
|
jens@0
|
176 |
NSParameterAssert(row<_nRows);
|
jens@0
|
177 |
NSParameterAssert(col<_nColumns);
|
jens@0
|
178 |
unsigned index = row*_nColumns+col;
|
jens@0
|
179 |
id cell = [_cells objectAtIndex: index];
|
jens@0
|
180 |
if( cell != [NSNull null] )
|
jens@0
|
181 |
[cell removeFromSuperlayer];
|
jens@0
|
182 |
[_cells replaceObjectAtIndex: index withObject: [NSNull null]];
|
jens@0
|
183 |
[self setNeedsDisplay];
|
jens@0
|
184 |
}
|
jens@0
|
185 |
|
jens@0
|
186 |
|
jens@12
|
187 |
- (NSArray*) cells
|
jens@12
|
188 |
{
|
jens@12
|
189 |
NSMutableArray *cells = [_cells mutableCopy];
|
jens@12
|
190 |
for( int i=cells.count-1; i>=0; i-- )
|
jens@12
|
191 |
if( [cells objectAtIndex: i] == [NSNull null] )
|
jens@12
|
192 |
[cells removeObjectAtIndex: i];
|
jens@12
|
193 |
return cells;
|
jens@12
|
194 |
}
|
jens@12
|
195 |
|
jens@12
|
196 |
|
jens@7
|
197 |
- (GridCell*) cellWithName: (NSString*)name
|
jens@7
|
198 |
{
|
jens@7
|
199 |
for( CALayer *layer in self.sublayers )
|
jens@7
|
200 |
if( [layer isKindOfClass: [GridCell class]] )
|
jens@7
|
201 |
if( [name isEqualToString: ((GridCell*)layer).name] )
|
jens@7
|
202 |
return (GridCell*)layer;
|
jens@7
|
203 |
return nil;
|
jens@7
|
204 |
}
|
jens@7
|
205 |
|
jens@7
|
206 |
|
jens@12
|
207 |
- (NSCountedSet*) countPiecesByPlayer
|
jens@12
|
208 |
{
|
jens@12
|
209 |
NSCountedSet *players = [NSCountedSet set];
|
jens@12
|
210 |
for( GridCell *cell in self.cells ) {
|
jens@12
|
211 |
Player *owner = cell.bit.owner;
|
jens@12
|
212 |
if( owner )
|
jens@12
|
213 |
[players addObject: owner];
|
jens@12
|
214 |
}
|
jens@12
|
215 |
return players;
|
jens@12
|
216 |
}
|
jens@12
|
217 |
|
jens@12
|
218 |
|
jens@12
|
219 |
|
jens@12
|
220 |
#pragma mark -
|
jens@12
|
221 |
#pragma mark GAME STATE:
|
jens@12
|
222 |
|
jens@12
|
223 |
|
jens@12
|
224 |
- (NSString*) stateString
|
jens@12
|
225 |
{
|
jens@12
|
226 |
NSMutableString *state = [NSMutableString stringWithCapacity: _cells.count];
|
jens@12
|
227 |
for( GridCell *cell in self.cells ) {
|
jens@12
|
228 |
Bit *bit = cell.bit;
|
jens@12
|
229 |
NSString *name = bit ?bit.name :@"-";
|
jens@12
|
230 |
NSAssert(name.length==1,@"Missing or multicharacter name");
|
jens@12
|
231 |
[state appendString: name];
|
jens@12
|
232 |
}
|
jens@12
|
233 |
return state;
|
jens@12
|
234 |
}
|
jens@12
|
235 |
|
jens@12
|
236 |
- (void) setStateString: (NSString*)state
|
jens@12
|
237 |
{
|
jens@12
|
238 |
Game *game = self.game;
|
jens@12
|
239 |
int i = 0;
|
jens@12
|
240 |
for( GridCell *cell in self.cells )
|
jens@12
|
241 |
cell.bit = [game makePieceNamed: [state substringWithRange: NSMakeRange(i++,1)]];
|
jens@12
|
242 |
}
|
jens@12
|
243 |
|
jens@12
|
244 |
|
jens@12
|
245 |
- (BOOL) applyMoveString: (NSString*)move
|
jens@12
|
246 |
{
|
jens@12
|
247 |
GridCell *src = nil;
|
jens@12
|
248 |
for( NSString *ident in [move componentsSeparatedByString: @"-"] ) {
|
jens@12
|
249 |
while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] )
|
jens@12
|
250 |
ident = [ident substringToIndex: ident.length-1];
|
jens@12
|
251 |
GridCell *dst = [self cellWithName: ident];
|
jens@12
|
252 |
if( dst == nil )
|
jens@12
|
253 |
return NO;
|
jens@12
|
254 |
if( src && ! [self.game animateMoveFrom: src to: dst] )
|
jens@12
|
255 |
return NO;
|
jens@12
|
256 |
src = dst;
|
jens@12
|
257 |
}
|
jens@12
|
258 |
return YES;
|
jens@12
|
259 |
}
|
jens@12
|
260 |
|
jens@12
|
261 |
|
jens@0
|
262 |
#pragma mark -
|
jens@0
|
263 |
#pragma mark DRAWING:
|
jens@0
|
264 |
|
jens@0
|
265 |
|
jens@0
|
266 |
- (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill
|
jens@0
|
267 |
{
|
jens@0
|
268 |
// Subroutine of -drawInContext:. Draws all the cells, with or without a fill.
|
jens@0
|
269 |
for( unsigned row=0; row<_nRows; row++ )
|
jens@0
|
270 |
for( unsigned col=0; col<_nColumns; col++ ) {
|
jens@0
|
271 |
GridCell *cell = [self cellAtRow: row column: col];
|
jens@0
|
272 |
if( cell )
|
jens@0
|
273 |
[cell drawInParentContext: ctx fill: fill];
|
jens@0
|
274 |
}
|
jens@0
|
275 |
}
|
jens@0
|
276 |
|
jens@0
|
277 |
|
jens@0
|
278 |
- (void)drawInContext:(CGContextRef)ctx
|
jens@0
|
279 |
{
|
jens@0
|
280 |
// Custom CALayer drawing implementation. Delegates to the cells to draw themselves
|
jens@0
|
281 |
// in me; this is more efficient than having each cell have its own drawing.
|
jens@3
|
282 |
[super drawInContext: ctx];
|
jens@11
|
283 |
|
jens@11
|
284 |
if( _backgroundImage )
|
jens@11
|
285 |
CGContextDrawImage(ctx, self.bounds, _backgroundImage);
|
jens@11
|
286 |
|
jens@0
|
287 |
if( _cellColor ) {
|
jens@0
|
288 |
CGContextSetFillColorWithColor(ctx, _cellColor);
|
jens@0
|
289 |
[self drawCellsInContext: ctx fill: YES];
|
jens@0
|
290 |
}
|
jens@0
|
291 |
if( _lineColor ) {
|
jens@0
|
292 |
CGContextSetStrokeColorWithColor(ctx,_lineColor);
|
jens@0
|
293 |
[self drawCellsInContext:ctx fill: NO];
|
jens@0
|
294 |
}
|
jens@0
|
295 |
}
|
jens@0
|
296 |
|
jens@0
|
297 |
|
jens@0
|
298 |
@end
|
jens@0
|
299 |
|
jens@0
|
300 |
|
jens@0
|
301 |
|
jens@0
|
302 |
#pragma mark -
|
jens@0
|
303 |
|
jens@0
|
304 |
@implementation GridCell
|
jens@0
|
305 |
|
jens@0
|
306 |
|
jens@0
|
307 |
- (id) initWithGrid: (Grid*)grid
|
jens@0
|
308 |
row: (unsigned)row column: (unsigned)col
|
jens@0
|
309 |
frame: (CGRect)frame
|
jens@0
|
310 |
{
|
jens@0
|
311 |
self = [super init];
|
jens@0
|
312 |
if (self != nil) {
|
jens@0
|
313 |
_grid = grid;
|
jens@0
|
314 |
_row = row;
|
jens@0
|
315 |
_column = col;
|
jens@0
|
316 |
self.position = frame.origin;
|
jens@0
|
317 |
CGRect bounds = frame;
|
jens@0
|
318 |
bounds.origin.x -= floor(bounds.origin.x); // make sure my coords fall on pixel boundaries
|
jens@0
|
319 |
bounds.origin.y -= floor(bounds.origin.y);
|
jens@0
|
320 |
self.bounds = bounds;
|
jens@0
|
321 |
self.anchorPoint = CGPointMake(0,0);
|
jens@0
|
322 |
self.borderColor = kHighlightColor; // Used when highlighting (see -setHighlighted:)
|
jens@0
|
323 |
}
|
jens@0
|
324 |
return self;
|
jens@0
|
325 |
}
|
jens@0
|
326 |
|
jens@0
|
327 |
- (NSString*) description
|
jens@0
|
328 |
{
|
jens@0
|
329 |
return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row];
|
jens@0
|
330 |
}
|
jens@0
|
331 |
|
jens@0
|
332 |
@synthesize grid=_grid, row=_row, column=_column;
|
jens@0
|
333 |
|
jens@0
|
334 |
|
jens@0
|
335 |
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
|
jens@0
|
336 |
{
|
jens@0
|
337 |
// Default implementation just fills or outlines the cell.
|
jens@0
|
338 |
CGRect frame = self.frame;
|
jens@0
|
339 |
if( fill )
|
jens@0
|
340 |
CGContextFillRect(ctx,frame);
|
jens@0
|
341 |
else
|
jens@0
|
342 |
CGContextStrokeRect(ctx, frame);
|
jens@0
|
343 |
}
|
jens@0
|
344 |
|
jens@0
|
345 |
|
jens@0
|
346 |
- (void) setBit: (Bit*)bit
|
jens@0
|
347 |
{
|
jens@0
|
348 |
if( bit != self.bit ) {
|
jens@0
|
349 |
[super setBit: bit];
|
jens@0
|
350 |
if( bit ) {
|
jens@0
|
351 |
// Center it:
|
jens@0
|
352 |
CGSize size = self.bounds.size;
|
jens@0
|
353 |
bit.position = CGPointMake(floor(size.width/2.0),
|
jens@0
|
354 |
floor(size.height/2.0));
|
jens@0
|
355 |
}
|
jens@0
|
356 |
}
|
jens@0
|
357 |
}
|
jens@0
|
358 |
|
jens@0
|
359 |
- (Bit*) canDragBit: (Bit*)bit
|
jens@0
|
360 |
{
|
jens@0
|
361 |
if( _grid.allowsMoves && bit==self.bit )
|
jens@0
|
362 |
return [super canDragBit: bit];
|
jens@0
|
363 |
else
|
jens@0
|
364 |
return nil;
|
jens@0
|
365 |
}
|
jens@0
|
366 |
|
jens@0
|
367 |
- (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point
|
jens@0
|
368 |
{
|
jens@0
|
369 |
return self.bit == nil || _grid.allowsCaptures;
|
jens@0
|
370 |
}
|
jens@0
|
371 |
|
jens@0
|
372 |
|
jens@0
|
373 |
- (BOOL) fwdIsN
|
jens@0
|
374 |
{
|
jens@0
|
375 |
return self.game.currentPlayer.index == 0;
|
jens@0
|
376 |
}
|
jens@0
|
377 |
|
jens@0
|
378 |
|
jens@0
|
379 |
- (NSArray*) neighbors
|
jens@0
|
380 |
{
|
jens@0
|
381 |
BOOL orthogonal = ! _grid.usesDiagonals;
|
jens@0
|
382 |
NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8];
|
jens@0
|
383 |
for( int dy=-1; dy<=1; dy++ )
|
jens@0
|
384 |
for( int dx=-1; dx<=1; dx++ )
|
jens@0
|
385 |
if( (dx || dy) && !(orthogonal && dx && dy) ) {
|
jens@0
|
386 |
GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx];
|
jens@0
|
387 |
if( cell )
|
jens@0
|
388 |
[neighbors addObject: cell];
|
jens@0
|
389 |
}
|
jens@0
|
390 |
return neighbors;
|
jens@0
|
391 |
}
|
jens@0
|
392 |
|
jens@0
|
393 |
|
jens@0
|
394 |
// Recursive subroutine used by getGroup:.
|
jens@0
|
395 |
- (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner
|
jens@0
|
396 |
{
|
jens@0
|
397 |
Bit *bit = self.bit;
|
jens@0
|
398 |
if( bit == nil ) {
|
jens@0
|
399 |
if( [liberties containsObject: self] )
|
jens@0
|
400 |
return; // already traversed
|
jens@0
|
401 |
[liberties addObject: self];
|
jens@0
|
402 |
} else if( bit.owner==owner ) {
|
jens@0
|
403 |
if( [group containsObject: self] )
|
jens@0
|
404 |
return; // already traversed
|
jens@0
|
405 |
[group addObject: self];
|
jens@0
|
406 |
for( GridCell *c in self.neighbors )
|
jens@0
|
407 |
[c x_addToGroup: group liberties: liberties owner: owner];
|
jens@0
|
408 |
}
|
jens@0
|
409 |
}
|
jens@0
|
410 |
|
jens@0
|
411 |
|
jens@0
|
412 |
- (NSSet*) getGroup: (int*)outLiberties
|
jens@0
|
413 |
{
|
jens@0
|
414 |
NSMutableSet *group=[NSMutableSet set], *liberties=nil;
|
jens@0
|
415 |
if( outLiberties )
|
jens@0
|
416 |
liberties = [NSMutableSet set];
|
jens@0
|
417 |
[self x_addToGroup: group liberties: liberties owner: self.bit.owner];
|
jens@0
|
418 |
if( outLiberties )
|
jens@0
|
419 |
*outLiberties = liberties.count;
|
jens@0
|
420 |
return group;
|
jens@0
|
421 |
}
|
jens@0
|
422 |
|
jens@0
|
423 |
|
jens@0
|
424 |
#pragma mark -
|
jens@0
|
425 |
#pragma mark DRAG-AND-DROP:
|
jens@0
|
426 |
|
jens@0
|
427 |
|
jens@8
|
428 |
#if ! TARGET_OS_IPHONE
|
jens@1
|
429 |
|
jens@11
|
430 |
// An image from another app can be dragged onto a Grid to change its background pattern.
|
jens@0
|
431 |
|
jens@0
|
432 |
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
|
jens@0
|
433 |
{
|
jens@11
|
434 |
if( CanGetCGImageFromPasteboard([sender draggingPasteboard]) )
|
jens@0
|
435 |
return NSDragOperationCopy;
|
jens@0
|
436 |
else
|
jens@0
|
437 |
return NSDragOperationNone;
|
jens@0
|
438 |
}
|
jens@0
|
439 |
|
jens@0
|
440 |
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
|
jens@0
|
441 |
{
|
jens@11
|
442 |
CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
|
jens@0
|
443 |
if( image ) {
|
jens@0
|
444 |
CGColorRef pattern = CreatePatternColor(image);
|
jens@0
|
445 |
_grid.cellColor = pattern;
|
jens@0
|
446 |
CGColorRelease(pattern);
|
jens@0
|
447 |
[_grid setNeedsDisplay];
|
jens@0
|
448 |
return YES;
|
jens@0
|
449 |
} else
|
jens@0
|
450 |
return NO;
|
jens@0
|
451 |
}
|
jens@0
|
452 |
|
jens@1
|
453 |
#endif
|
jens@0
|
454 |
|
jens@0
|
455 |
@end
|
jens@0
|
456 |
|
jens@0
|
457 |
|
jens@0
|
458 |
|
jens@0
|
459 |
|
jens@0
|
460 |
#pragma mark -
|
jens@0
|
461 |
|
jens@0
|
462 |
@implementation RectGrid
|
jens@0
|
463 |
|
jens@0
|
464 |
|
jens@0
|
465 |
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
|
jens@0
|
466 |
spacing: (CGSize)spacing
|
jens@0
|
467 |
position: (CGPoint)pos
|
jens@0
|
468 |
{
|
jens@0
|
469 |
self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos];
|
jens@0
|
470 |
if( self ) {
|
jens@0
|
471 |
_cellClass = [Square class];
|
jens@0
|
472 |
}
|
jens@0
|
473 |
return self;
|
jens@0
|
474 |
}
|
jens@0
|
475 |
|
jens@0
|
476 |
|
jens@0
|
477 |
- (CGColorRef) altCellColor {return _altCellColor;}
|
jens@0
|
478 |
- (void) setAltCellColor: (CGColorRef)altCellColor {setcolor(&_altCellColor,altCellColor);}
|
jens@0
|
479 |
|
jens@0
|
480 |
|
jens@0
|
481 |
@end
|
jens@0
|
482 |
|
jens@0
|
483 |
|
jens@0
|
484 |
|
jens@0
|
485 |
#pragma mark -
|
jens@0
|
486 |
|
jens@0
|
487 |
@implementation Square
|
jens@0
|
488 |
|
jens@0
|
489 |
|
jens@0
|
490 |
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
|
jens@0
|
491 |
{
|
jens@0
|
492 |
if( fill ) {
|
jens@0
|
493 |
CGColorRef c = ((RectGrid*)_grid).altCellColor;
|
jens@0
|
494 |
if( c ) {
|
jens@0
|
495 |
if( ! ((_row+_column) & 1) )
|
jens@0
|
496 |
c = _grid.cellColor;
|
jens@0
|
497 |
CGContextSetFillColorWithColor(ctx, c);
|
jens@0
|
498 |
}
|
jens@0
|
499 |
}
|
jens@0
|
500 |
[super drawInParentContext: ctx fill: fill];
|
jens@0
|
501 |
}
|
jens@0
|
502 |
|
jens@0
|
503 |
|
jens@0
|
504 |
- (void) setHighlighted: (BOOL)highlighted
|
jens@0
|
505 |
{
|
jens@0
|
506 |
[super setHighlighted: highlighted];
|
jens@3
|
507 |
self.cornerRadius = self.bounds.size.width/2.0;
|
jens@0
|
508 |
self.borderWidth = (highlighted ?6 :0);
|
jens@0
|
509 |
}
|
jens@0
|
510 |
|
jens@0
|
511 |
|
jens@0
|
512 |
- (Square*) nw {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];}
|
jens@0
|
513 |
- (Square*) n {return (Square*)[_grid cellAtRow: _row+1 column: _column ];}
|
jens@0
|
514 |
- (Square*) ne {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];}
|
jens@0
|
515 |
- (Square*) e {return (Square*)[_grid cellAtRow: _row column: _column+1];}
|
jens@0
|
516 |
- (Square*) se {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];}
|
jens@0
|
517 |
- (Square*) s {return (Square*)[_grid cellAtRow: _row-1 column: _column ];}
|
jens@0
|
518 |
- (Square*) sw {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];}
|
jens@0
|
519 |
- (Square*) w {return (Square*)[_grid cellAtRow: _row column: _column-1];}
|
jens@0
|
520 |
|
jens@0
|
521 |
// Directions relative to the current player:
|
jens@0
|
522 |
- (Square*) fl {return self.fwdIsN ?self.nw :self.se;}
|
jens@0
|
523 |
- (Square*) f {return self.fwdIsN ?self.n :self.s;}
|
jens@0
|
524 |
- (Square*) fr {return self.fwdIsN ?self.ne :self.sw;}
|
jens@0
|
525 |
- (Square*) r {return self.fwdIsN ?self.e :self.w;}
|
jens@0
|
526 |
- (Square*) br {return self.fwdIsN ?self.se :self.nw;}
|
jens@0
|
527 |
- (Square*) b {return self.fwdIsN ?self.s :self.n;}
|
jens@0
|
528 |
- (Square*) bl {return self.fwdIsN ?self.sw :self.ne;}
|
jens@0
|
529 |
- (Square*) l {return self.fwdIsN ?self.w :self.e;}
|
jens@0
|
530 |
|
jens@0
|
531 |
|
jens@12
|
532 |
static int sgn( int n ) {return n<0 ?-1 :(n>0 ?1 :0);}
|
jens@12
|
533 |
|
jens@12
|
534 |
|
jens@12
|
535 |
- (SEL) directionToCell: (GridCell*)dst
|
jens@12
|
536 |
{
|
jens@12
|
537 |
static NSString* const kDirections[9] = {@"sw", @"s", @"se",
|
jens@12
|
538 |
@"w", nil, @"e",
|
jens@12
|
539 |
@"nw", @"n", @"ne"};
|
jens@12
|
540 |
if( dst.grid != self.grid )
|
jens@12
|
541 |
return NULL;
|
jens@12
|
542 |
int dy=dst.row-_row, dx=dst.column-_column;
|
jens@12
|
543 |
if( dx && dy )
|
jens@12
|
544 |
if( !( _grid.usesDiagonals && abs(dx)==abs(dy) ) )
|
jens@12
|
545 |
return NULL;
|
jens@12
|
546 |
NSString *dir = kDirections[ 3*(sgn(dy)+1) + (sgn(dx)+1) ];
|
jens@12
|
547 |
return dir ?NSSelectorFromString(dir) :NULL;
|
jens@12
|
548 |
}
|
jens@12
|
549 |
|
jens@12
|
550 |
- (NSArray*) lineToCell: (GridCell*)dst inclusive: (BOOL)inclusive;
|
jens@12
|
551 |
{
|
jens@12
|
552 |
SEL dir = [self directionToCell: dst];
|
jens@12
|
553 |
if( ! dir )
|
jens@12
|
554 |
return nil;
|
jens@12
|
555 |
NSMutableArray *line = [NSMutableArray array];
|
jens@12
|
556 |
GridCell *cell;
|
jens@12
|
557 |
for( cell=self; cell; cell = [cell performSelector: dir] ) {
|
jens@12
|
558 |
if( inclusive || (cell!=self && cell!=dst) )
|
jens@12
|
559 |
[line addObject: cell];
|
jens@12
|
560 |
if( cell==dst )
|
jens@12
|
561 |
return line;
|
jens@12
|
562 |
}
|
jens@12
|
563 |
return nil; // should be impossible, but just in case
|
jens@12
|
564 |
}
|
jens@12
|
565 |
|
jens@12
|
566 |
|
jens@8
|
567 |
#if ! TARGET_OS_IPHONE
|
jens@1
|
568 |
|
jens@0
|
569 |
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
|
jens@0
|
570 |
{
|
jens@11
|
571 |
CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
|
jens@0
|
572 |
if( image ) {
|
jens@0
|
573 |
CGColorRef color = CreatePatternColor(image);
|
jens@0
|
574 |
RectGrid *rectGrid = (RectGrid*)_grid;
|
jens@0
|
575 |
if( rectGrid.altCellColor && ((_row+_column) & 1) )
|
jens@0
|
576 |
rectGrid.altCellColor = color;
|
jens@0
|
577 |
else
|
jens@0
|
578 |
rectGrid.cellColor = color;
|
jens@0
|
579 |
CGColorRelease(color);
|
jens@0
|
580 |
[rectGrid setNeedsDisplay];
|
jens@0
|
581 |
return YES;
|
jens@0
|
582 |
} else
|
jens@0
|
583 |
return NO;
|
jens@0
|
584 |
}
|
jens@0
|
585 |
|
jens@1
|
586 |
#endif
|
jens@1
|
587 |
|
jens@0
|
588 |
@end
|
jens@0
|
589 |
|
jens@0
|
590 |
|
jens@0
|
591 |
|
jens@0
|
592 |
#pragma mark -
|
jens@0
|
593 |
|
jens@0
|
594 |
@implementation GoSquare
|
jens@0
|
595 |
|
jens@0
|
596 |
@synthesize dotted=_dotted;
|
jens@0
|
597 |
|
jens@0
|
598 |
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
|
jens@0
|
599 |
{
|
jens@0
|
600 |
if( fill )
|
jens@0
|
601 |
[super drawInParentContext: ctx fill: fill];
|
jens@0
|
602 |
else {
|
jens@0
|
603 |
CGRect frame = self.frame;
|
jens@0
|
604 |
const CGFloat midx=floor(CGRectGetMidX(frame))+0.5,
|
jens@0
|
605 |
midy=floor(CGRectGetMidY(frame))+0.5;
|
jens@0
|
606 |
CGPoint p[4] = {{CGRectGetMinX(frame),midy},
|
jens@0
|
607 |
{CGRectGetMaxX(frame),midy},
|
jens@0
|
608 |
{midx,CGRectGetMinY(frame)},
|
jens@0
|
609 |
{midx,CGRectGetMaxY(frame)}};
|
jens@0
|
610 |
if( ! self.s ) p[2].y = midy;
|
jens@0
|
611 |
if( ! self.n ) p[3].y = midy;
|
jens@0
|
612 |
if( ! self.w ) p[0].x = midx;
|
jens@0
|
613 |
if( ! self.e ) p[1].x = midx;
|
jens@0
|
614 |
CGContextStrokeLineSegments(ctx, p, 4);
|
jens@0
|
615 |
|
jens@0
|
616 |
if( _dotted ) {
|
jens@0
|
617 |
CGContextSetFillColorWithColor(ctx,_grid.lineColor);
|
jens@0
|
618 |
CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5);
|
jens@0
|
619 |
CGContextFillEllipseInRect(ctx, dot);
|
jens@0
|
620 |
}
|
jens@0
|
621 |
}
|
jens@0
|
622 |
}
|
jens@0
|
623 |
|
jens@1
|
624 |
|
jens@0
|
625 |
@end
|