Initial check-in into Mercurial. Branched from 1.0 release of Apple's sample code. No longer requires garbage collection. Fixed some memory leaks of CG objects. Fixed a bug when advancing to the 8th row in the Checkers game.
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 _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 // An image from another app can be dragged onto a Dispenser to change the Piece's appearance.
334 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
336 NSPasteboard *pb = [sender draggingPasteboard];
337 if( [NSImage canInitWithPasteboard: pb] )
338 return NSDragOperationCopy;
340 return NSDragOperationNone;
343 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
345 CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]);
347 CGColorRef pattern = CreatePatternColor(image);
348 _grid.cellColor = pattern;
349 CGColorRelease(pattern);
350 [_grid setNeedsDisplay];
364 @implementation RectGrid
367 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
368 spacing: (CGSize)spacing
369 position: (CGPoint)pos
371 self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos];
373 _cellClass = [Square class];
379 - (CGColorRef) altCellColor {return _altCellColor;}
380 - (void) setAltCellColor: (CGColorRef)altCellColor {setcolor(&_altCellColor,altCellColor);}
389 @implementation Square
392 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
395 CGColorRef c = ((RectGrid*)_grid).altCellColor;
397 if( ! ((_row+_column) & 1) )
399 CGContextSetFillColorWithColor(ctx, c);
402 [super drawInParentContext: ctx fill: fill];
406 - (void) setHighlighted: (BOOL)highlighted
408 [super setHighlighted: highlighted];
409 self.borderWidth = (highlighted ?6 :0);
413 - (Square*) nw {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];}
414 - (Square*) n {return (Square*)[_grid cellAtRow: _row+1 column: _column ];}
415 - (Square*) ne {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];}
416 - (Square*) e {return (Square*)[_grid cellAtRow: _row column: _column+1];}
417 - (Square*) se {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];}
418 - (Square*) s {return (Square*)[_grid cellAtRow: _row-1 column: _column ];}
419 - (Square*) sw {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];}
420 - (Square*) w {return (Square*)[_grid cellAtRow: _row column: _column-1];}
422 // Directions relative to the current player:
423 - (Square*) fl {return self.fwdIsN ?self.nw :self.se;}
424 - (Square*) f {return self.fwdIsN ?self.n :self.s;}
425 - (Square*) fr {return self.fwdIsN ?self.ne :self.sw;}
426 - (Square*) r {return self.fwdIsN ?self.e :self.w;}
427 - (Square*) br {return self.fwdIsN ?self.se :self.nw;}
428 - (Square*) b {return self.fwdIsN ?self.s :self.n;}
429 - (Square*) bl {return self.fwdIsN ?self.sw :self.ne;}
430 - (Square*) l {return self.fwdIsN ?self.w :self.e;}
433 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
435 CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]);
437 CGColorRef color = CreatePatternColor(image);
438 RectGrid *rectGrid = (RectGrid*)_grid;
439 if( rectGrid.altCellColor && ((_row+_column) & 1) )
440 rectGrid.altCellColor = color;
442 rectGrid.cellColor = color;
443 CGColorRelease(color);
444 [rectGrid setNeedsDisplay];
456 @implementation GoSquare
458 @synthesize dotted=_dotted;
460 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
463 [super drawInParentContext: ctx fill: fill];
465 CGRect frame = self.frame;
466 const CGFloat midx=floor(CGRectGetMidX(frame))+0.5,
467 midy=floor(CGRectGetMidY(frame))+0.5;
468 CGPoint p[4] = {{CGRectGetMinX(frame),midy},
469 {CGRectGetMaxX(frame),midy},
470 {midx,CGRectGetMinY(frame)},
471 {midx,CGRectGetMaxY(frame)}};
472 if( ! self.s ) p[2].y = midy;
473 if( ! self.n ) p[3].y = midy;
474 if( ! self.w ) p[0].x = midx;
475 if( ! self.e ) p[1].x = midx;
476 CGContextStrokeLineSegments(ctx, p, 4);
479 CGContextSetFillColorWithColor(ctx,_grid.lineColor);
480 CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5);
481 CGContextFillEllipseInRect(ctx, dot);