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.
26 #import "QuartzUtils.h"
32 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
33 spacing: (CGSize)spacing
34 position: (CGPoint)pos
36 NSParameterAssert(nRows>0 && nColumns>0);
42 _cellClass = [GridCell class];
43 self.lineColor = kBlackColor;
47 self.bounds = CGRectMake(-1, -1, nColumns*spacing.width+2, nRows*spacing.height+2);
49 self.anchorPoint = CGPointMake(0,0);
50 self.zPosition = kBoardZ;
51 self.needsDisplayOnBoundsChange = YES;
53 unsigned n = nRows*nColumns;
54 _cells = [[NSMutableArray alloc] initWithCapacity: n];
55 id null = [NSNull null];
57 [_cells addObject: null];
59 [self setNeedsDisplay];
65 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
68 CGFloat spacing = floor(MIN( (frame.size.width -2)/(CGFloat)nColumns,
69 (frame.size.height-2)/(CGFloat)nRows) );
70 return [self initWithRows: nRows columns: nColumns
71 spacing: CGSizeMake(spacing,spacing)
72 position: frame.origin];
78 CGColorRelease(_cellColor);
79 CGColorRelease(_lineColor);
85 static void setcolor( CGColorRef *var, CGColorRef color )
89 *var = CGColorRetain(color);
93 - (CGColorRef) cellColor {return _cellColor;}
94 - (void) setCellColor: (CGColorRef)cellColor {setcolor(&_cellColor,cellColor);}
96 - (CGColorRef) lineColor {return _lineColor;}
97 - (void) setLineColor: (CGColorRef)lineColor {setcolor(&_lineColor,lineColor);}
99 @synthesize cellClass=_cellClass, rows=_nRows, columns=_nColumns, spacing=_spacing,
100 usesDiagonals=_usesDiagonals, allowsMoves=_allowsMoves, allowsCaptures=_allowsCaptures;
104 #pragma mark GEOMETRY:
107 - (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col
109 if( row < _nRows && col < _nColumns ) {
110 id cell = [_cells objectAtIndex: row*_nColumns+col];
111 if( cell != [NSNull null] )
118 /** Subclasses can override this, to change the cell's class or frame. */
119 - (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col
120 suggestedFrame: (CGRect)frame
122 return [[[_cellClass alloc] initWithGrid: self
129 - (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col
131 NSParameterAssert(row<_nRows);
132 NSParameterAssert(col<_nColumns);
133 unsigned index = row*_nColumns+col;
134 GridCell *cell = [_cells objectAtIndex: index];
135 if( (id)cell == [NSNull null] ) {
136 CGRect frame = CGRectMake(col*_spacing.width, row*_spacing.height,
137 _spacing.width,_spacing.height);
138 cell = [self createCellAtRow: row column: col suggestedFrame: frame];
140 [_cells replaceObjectAtIndex: index withObject: cell];
141 [self addSublayer: cell];
142 [self setNeedsDisplay];
151 for( int row=_nRows-1; row>=0; row-- ) // makes 'upper' cells be in 'back'
152 for( int col=0; col<_nColumns; col++ )
153 [self addCellAtRow: row column: col];
157 - (void) removeCellAtRow: (unsigned)row column: (unsigned)col
159 NSParameterAssert(row<_nRows);
160 NSParameterAssert(col<_nColumns);
161 unsigned index = row*_nColumns+col;
162 id cell = [_cells objectAtIndex: index];
163 if( cell != [NSNull null] )
164 [cell removeFromSuperlayer];
165 [_cells replaceObjectAtIndex: index withObject: [NSNull null]];
166 [self setNeedsDisplay];
171 #pragma mark DRAWING:
174 - (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill
176 // Subroutine of -drawInContext:. Draws all the cells, with or without a fill.
177 for( unsigned row=0; row<_nRows; row++ )
178 for( unsigned col=0; col<_nColumns; col++ ) {
179 GridCell *cell = [self cellAtRow: row column: col];
181 [cell drawInParentContext: ctx fill: fill];
186 - (void)drawInContext:(CGContextRef)ctx
188 // Custom CALayer drawing implementation. Delegates to the cells to draw themselves
189 // in me; this is more efficient than having each cell have its own drawing.
191 CGContextSetFillColorWithColor(ctx, _cellColor);
192 [self drawCellsInContext: ctx fill: YES];
195 CGContextSetStrokeColorWithColor(ctx,_lineColor);
196 [self drawCellsInContext:ctx fill: NO];
207 @implementation GridCell
210 - (id) initWithGrid: (Grid*)grid
211 row: (unsigned)row column: (unsigned)col
219 self.position = frame.origin;
220 CGRect bounds = frame;
221 bounds.origin.x -= floor(bounds.origin.x); // make sure my coords fall on pixel boundaries
222 bounds.origin.y -= floor(bounds.origin.y);
223 self.bounds = bounds;
224 self.anchorPoint = CGPointMake(0,0);
225 self.borderColor = kHighlightColor; // Used when highlighting (see -setHighlighted:)
230 - (NSString*) description
232 return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row];
235 @synthesize grid=_grid, row=_row, column=_column;
238 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
240 // Default implementation just fills or outlines the cell.
241 CGRect frame = self.frame;
243 CGContextFillRect(ctx,frame);
245 CGContextStrokeRect(ctx, frame);
249 - (void) setBit: (Bit*)bit
251 if( bit != self.bit ) {
255 CGSize size = self.bounds.size;
256 bit.position = CGPointMake(floor(size.width/2.0),
257 floor(size.height/2.0));
262 - (Bit*) canDragBit: (Bit*)bit
264 if( _grid.allowsMoves && bit==self.bit )
265 return [super canDragBit: bit];
270 - (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point
272 return self.bit == nil || _grid.allowsCaptures;
278 return self.game.currentPlayer.index == 0;
282 - (NSArray*) neighbors
284 BOOL orthogonal = ! _grid.usesDiagonals;
285 NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8];
286 for( int dy=-1; dy<=1; dy++ )
287 for( int dx=-1; dx<=1; dx++ )
288 if( (dx || dy) && !(orthogonal && dx && dy) ) {
289 GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx];
291 [neighbors addObject: cell];
297 // Recursive subroutine used by getGroup:.
298 - (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner
302 if( [liberties containsObject: self] )
303 return; // already traversed
304 [liberties addObject: self];
305 } else if( bit.owner==owner ) {
306 if( [group containsObject: self] )
307 return; // already traversed
308 [group addObject: self];
309 for( GridCell *c in self.neighbors )
310 [c x_addToGroup: group liberties: liberties owner: owner];
315 - (NSSet*) getGroup: (int*)outLiberties
317 NSMutableSet *group=[NSMutableSet set], *liberties=nil;
319 liberties = [NSMutableSet set];
320 [self x_addToGroup: group liberties: liberties owner: self.bit.owner];
322 *outLiberties = liberties.count;
328 #pragma mark DRAG-AND-DROP:
331 #if ! TARGET_OS_ASPEN
333 // An image from another app can be dragged onto a Dispenser to change the Piece's appearance.
336 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
338 NSPasteboard *pb = [sender draggingPasteboard];
339 if( [NSImage canInitWithPasteboard: pb] )
340 return NSDragOperationCopy;
342 return NSDragOperationNone;
345 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
347 CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]);
349 CGColorRef pattern = CreatePatternColor(image);
350 _grid.cellColor = pattern;
351 CGColorRelease(pattern);
352 [_grid setNeedsDisplay];
367 @implementation RectGrid
370 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
371 spacing: (CGSize)spacing
372 position: (CGPoint)pos
374 self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos];
376 _cellClass = [Square class];
382 - (CGColorRef) altCellColor {return _altCellColor;}
383 - (void) setAltCellColor: (CGColorRef)altCellColor {setcolor(&_altCellColor,altCellColor);}
392 @implementation Square
395 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
398 CGColorRef c = ((RectGrid*)_grid).altCellColor;
400 if( ! ((_row+_column) & 1) )
402 CGContextSetFillColorWithColor(ctx, c);
405 [super drawInParentContext: ctx fill: fill];
409 - (void) setHighlighted: (BOOL)highlighted
411 [super setHighlighted: highlighted];
412 self.borderWidth = (highlighted ?6 :0);
416 - (Square*) nw {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];}
417 - (Square*) n {return (Square*)[_grid cellAtRow: _row+1 column: _column ];}
418 - (Square*) ne {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];}
419 - (Square*) e {return (Square*)[_grid cellAtRow: _row column: _column+1];}
420 - (Square*) se {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];}
421 - (Square*) s {return (Square*)[_grid cellAtRow: _row-1 column: _column ];}
422 - (Square*) sw {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];}
423 - (Square*) w {return (Square*)[_grid cellAtRow: _row column: _column-1];}
425 // Directions relative to the current player:
426 - (Square*) fl {return self.fwdIsN ?self.nw :self.se;}
427 - (Square*) f {return self.fwdIsN ?self.n :self.s;}
428 - (Square*) fr {return self.fwdIsN ?self.ne :self.sw;}
429 - (Square*) r {return self.fwdIsN ?self.e :self.w;}
430 - (Square*) br {return self.fwdIsN ?self.se :self.nw;}
431 - (Square*) b {return self.fwdIsN ?self.s :self.n;}
432 - (Square*) bl {return self.fwdIsN ?self.sw :self.ne;}
433 - (Square*) l {return self.fwdIsN ?self.w :self.e;}
436 #if ! TARGET_OS_ASPEN
438 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
440 CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]);
442 CGColorRef color = CreatePatternColor(image);
443 RectGrid *rectGrid = (RectGrid*)_grid;
444 if( rectGrid.altCellColor && ((_row+_column) & 1) )
445 rectGrid.altCellColor = color;
447 rectGrid.cellColor = color;
448 CGColorRelease(color);
449 [rectGrid setNeedsDisplay];
463 @implementation GoSquare
465 @synthesize dotted=_dotted;
467 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
470 [super drawInParentContext: ctx fill: fill];
472 CGRect frame = self.frame;
473 const CGFloat midx=floor(CGRectGetMidX(frame))+0.5,
474 midy=floor(CGRectGetMidY(frame))+0.5;
475 CGPoint p[4] = {{CGRectGetMinX(frame),midy},
476 {CGRectGetMaxX(frame),midy},
477 {midx,CGRectGetMinY(frame)},
478 {midx,CGRectGetMaxY(frame)}};
479 if( ! self.s ) p[2].y = midy;
480 if( ! self.n ) p[3].y = midy;
481 if( ! self.w ) p[0].x = midx;
482 if( ! self.e ) p[1].x = midx;
483 CGContextStrokeLineSegments(ctx, p, 4);
486 CGContextSetFillColorWithColor(ctx,_grid.lineColor);
487 CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5);
488 CGContextFillEllipseInRect(ctx, dot);