Fixed: An exception in the Go game if you mouse down on the board but then drag to the captured-pieces area and release the mouse.
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 CGSize size = CGSizeMake(nColumns*spacing+2, nRows*spacing+2);
71 CGPoint position = frame.origin;
72 position.x += round( (frame.size.width -size.width )/2.0 );
73 position.y += round( (frame.size.height-size.height)/2.0 );
75 return [self initWithRows: nRows columns: nColumns
76 spacing: CGSizeMake(spacing,spacing)
83 CGColorRelease(_cellColor);
84 CGColorRelease(_lineColor);
90 static void setcolor( CGColorRef *var, CGColorRef color )
94 *var = CGColorRetain(color);
98 - (CGColorRef) cellColor {return _cellColor;}
99 - (void) setCellColor: (CGColorRef)cellColor {setcolor(&_cellColor,cellColor);}
101 - (CGColorRef) lineColor {return _lineColor;}
102 - (void) setLineColor: (CGColorRef)lineColor {setcolor(&_lineColor,lineColor);}
104 @synthesize cellClass=_cellClass, rows=_nRows, columns=_nColumns, spacing=_spacing,
105 usesDiagonals=_usesDiagonals, allowsMoves=_allowsMoves, allowsCaptures=_allowsCaptures;
109 #pragma mark GEOMETRY:
112 - (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col
114 if( row < _nRows && col < _nColumns ) {
115 id cell = [_cells objectAtIndex: row*_nColumns+col];
116 if( cell != [NSNull null] )
123 /** Subclasses can override this, to change the cell's class or frame. */
124 - (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col
125 suggestedFrame: (CGRect)frame
127 return [[[_cellClass alloc] initWithGrid: self
134 - (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col
136 NSParameterAssert(row<_nRows);
137 NSParameterAssert(col<_nColumns);
138 unsigned index = row*_nColumns+col;
139 GridCell *cell = [_cells objectAtIndex: index];
140 if( (id)cell == [NSNull null] ) {
141 CGRect frame = CGRectMake(col*_spacing.width, row*_spacing.height,
142 _spacing.width,_spacing.height);
143 cell = [self createCellAtRow: row column: col suggestedFrame: frame];
145 [_cells replaceObjectAtIndex: index withObject: cell];
146 [self addSublayer: cell];
147 [self setNeedsDisplay];
156 for( int row=_nRows-1; row>=0; row-- ) // makes 'upper' cells be in 'back'
157 for( int col=0; col<_nColumns; col++ )
158 [self addCellAtRow: row column: col];
162 - (void) removeCellAtRow: (unsigned)row column: (unsigned)col
164 NSParameterAssert(row<_nRows);
165 NSParameterAssert(col<_nColumns);
166 unsigned index = row*_nColumns+col;
167 id cell = [_cells objectAtIndex: index];
168 if( cell != [NSNull null] )
169 [cell removeFromSuperlayer];
170 [_cells replaceObjectAtIndex: index withObject: [NSNull null]];
171 [self setNeedsDisplay];
176 #pragma mark DRAWING:
179 - (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill
181 // Subroutine of -drawInContext:. Draws all the cells, with or without a fill.
182 for( unsigned row=0; row<_nRows; row++ )
183 for( unsigned col=0; col<_nColumns; col++ ) {
184 GridCell *cell = [self cellAtRow: row column: col];
186 [cell drawInParentContext: ctx fill: fill];
191 - (void)drawInContext:(CGContextRef)ctx
193 // Custom CALayer drawing implementation. Delegates to the cells to draw themselves
194 // in me; this is more efficient than having each cell have its own drawing.
195 [super drawInContext: ctx];
197 CGContextSetFillColorWithColor(ctx, _cellColor);
198 [self drawCellsInContext: ctx fill: YES];
201 CGContextSetStrokeColorWithColor(ctx,_lineColor);
202 [self drawCellsInContext:ctx fill: NO];
213 @implementation GridCell
216 - (id) initWithGrid: (Grid*)grid
217 row: (unsigned)row column: (unsigned)col
225 self.position = frame.origin;
226 CGRect bounds = frame;
227 bounds.origin.x -= floor(bounds.origin.x); // make sure my coords fall on pixel boundaries
228 bounds.origin.y -= floor(bounds.origin.y);
229 self.bounds = bounds;
230 self.anchorPoint = CGPointMake(0,0);
231 self.borderColor = kHighlightColor; // Used when highlighting (see -setHighlighted:)
236 - (NSString*) description
238 return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row];
241 @synthesize grid=_grid, row=_row, column=_column;
244 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
246 // Default implementation just fills or outlines the cell.
247 CGRect frame = self.frame;
249 CGContextFillRect(ctx,frame);
251 CGContextStrokeRect(ctx, frame);
255 - (void) setBit: (Bit*)bit
257 if( bit != self.bit ) {
261 CGSize size = self.bounds.size;
262 bit.position = CGPointMake(floor(size.width/2.0),
263 floor(size.height/2.0));
268 - (Bit*) canDragBit: (Bit*)bit
270 if( _grid.allowsMoves && bit==self.bit )
271 return [super canDragBit: bit];
276 - (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point
278 return self.bit == nil || _grid.allowsCaptures;
284 return self.game.currentPlayer.index == 0;
288 - (NSArray*) neighbors
290 BOOL orthogonal = ! _grid.usesDiagonals;
291 NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8];
292 for( int dy=-1; dy<=1; dy++ )
293 for( int dx=-1; dx<=1; dx++ )
294 if( (dx || dy) && !(orthogonal && dx && dy) ) {
295 GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx];
297 [neighbors addObject: cell];
303 // Recursive subroutine used by getGroup:.
304 - (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner
308 if( [liberties containsObject: self] )
309 return; // already traversed
310 [liberties addObject: self];
311 } else if( bit.owner==owner ) {
312 if( [group containsObject: self] )
313 return; // already traversed
314 [group addObject: self];
315 for( GridCell *c in self.neighbors )
316 [c x_addToGroup: group liberties: liberties owner: owner];
321 - (NSSet*) getGroup: (int*)outLiberties
323 NSMutableSet *group=[NSMutableSet set], *liberties=nil;
325 liberties = [NSMutableSet set];
326 [self x_addToGroup: group liberties: liberties owner: self.bit.owner];
328 *outLiberties = liberties.count;
334 #pragma mark DRAG-AND-DROP:
337 #if ! TARGET_OS_ASPEN
339 // An image from another app can be dragged onto a Dispenser to change the Piece's appearance.
342 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
344 NSPasteboard *pb = [sender draggingPasteboard];
345 if( [NSImage canInitWithPasteboard: pb] )
346 return NSDragOperationCopy;
348 return NSDragOperationNone;
351 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
353 CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]);
355 CGColorRef pattern = CreatePatternColor(image);
356 _grid.cellColor = pattern;
357 CGColorRelease(pattern);
358 [_grid setNeedsDisplay];
373 @implementation RectGrid
376 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
377 spacing: (CGSize)spacing
378 position: (CGPoint)pos
380 self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos];
382 _cellClass = [Square class];
388 - (CGColorRef) altCellColor {return _altCellColor;}
389 - (void) setAltCellColor: (CGColorRef)altCellColor {setcolor(&_altCellColor,altCellColor);}
398 @implementation Square
401 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
404 CGColorRef c = ((RectGrid*)_grid).altCellColor;
406 if( ! ((_row+_column) & 1) )
408 CGContextSetFillColorWithColor(ctx, c);
411 [super drawInParentContext: ctx fill: fill];
415 - (void) setHighlighted: (BOOL)highlighted
417 [super setHighlighted: highlighted];
418 self.cornerRadius = self.bounds.size.width/2.0;
419 self.borderWidth = (highlighted ?6 :0);
423 - (Square*) nw {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];}
424 - (Square*) n {return (Square*)[_grid cellAtRow: _row+1 column: _column ];}
425 - (Square*) ne {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];}
426 - (Square*) e {return (Square*)[_grid cellAtRow: _row column: _column+1];}
427 - (Square*) se {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];}
428 - (Square*) s {return (Square*)[_grid cellAtRow: _row-1 column: _column ];}
429 - (Square*) sw {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];}
430 - (Square*) w {return (Square*)[_grid cellAtRow: _row column: _column-1];}
432 // Directions relative to the current player:
433 - (Square*) fl {return self.fwdIsN ?self.nw :self.se;}
434 - (Square*) f {return self.fwdIsN ?self.n :self.s;}
435 - (Square*) fr {return self.fwdIsN ?self.ne :self.sw;}
436 - (Square*) r {return self.fwdIsN ?self.e :self.w;}
437 - (Square*) br {return self.fwdIsN ?self.se :self.nw;}
438 - (Square*) b {return self.fwdIsN ?self.s :self.n;}
439 - (Square*) bl {return self.fwdIsN ?self.sw :self.ne;}
440 - (Square*) l {return self.fwdIsN ?self.w :self.e;}
443 #if ! TARGET_OS_ASPEN
445 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
447 CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]);
449 CGColorRef color = CreatePatternColor(image);
450 RectGrid *rectGrid = (RectGrid*)_grid;
451 if( rectGrid.altCellColor && ((_row+_column) & 1) )
452 rectGrid.altCellColor = color;
454 rectGrid.cellColor = color;
455 CGColorRelease(color);
456 [rectGrid setNeedsDisplay];
470 @implementation GoSquare
472 @synthesize dotted=_dotted;
474 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
477 [super drawInParentContext: ctx fill: fill];
479 CGRect frame = self.frame;
480 const CGFloat midx=floor(CGRectGetMidX(frame))+0.5,
481 midy=floor(CGRectGetMidY(frame))+0.5;
482 CGPoint p[4] = {{CGRectGetMinX(frame),midy},
483 {CGRectGetMaxX(frame),midy},
484 {midx,CGRectGetMinY(frame)},
485 {midx,CGRectGetMaxY(frame)}};
486 if( ! self.s ) p[2].y = midy;
487 if( ! self.n ) p[3].y = midy;
488 if( ! self.w ) p[0].x = midx;
489 if( ! self.e ) p[1].x = midx;
490 CGContextStrokeLineSegments(ctx, p, 4);
493 CGContextSetFillColorWithColor(ctx,_grid.lineColor);
494 CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5);
495 CGContextFillEllipseInRect(ctx, dot);