Minor compiler-compatibility fixes.
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 "Game+Protected.h"
28 #import "QuartzUtils.h"
31 @interface GridCell ()
32 - (void) setBitTransform: (CATransform3D)bitTransform;
39 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
40 spacing: (CGSize)spacing
41 position: (CGPoint)pos
43 NSParameterAssert(nRows>0 && nColumns>0);
49 _cellClass = [GridCell class];
50 self.lineColor = kBlackColor;
53 _bitTransform = CATransform3DIdentity;
55 self.bounds = CGRectMake(-1, -1, nColumns*spacing.width+2, nRows*spacing.height+2);
57 self.anchorPoint = CGPointMake(0,0);
58 self.zPosition = kBoardZ;
59 self.needsDisplayOnBoundsChange = YES;
61 unsigned n = nRows*nColumns;
62 _cells = [[NSMutableArray alloc] initWithCapacity: n];
63 id null = [NSNull null];
65 [_cells addObject: null];
67 [self setNeedsDisplay];
73 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
76 CGFloat spacing = floor(MIN( (frame.size.width -2)/(CGFloat)nColumns,
77 (frame.size.height-2)/(CGFloat)nRows) );
78 CGSize size = CGSizeMake(nColumns*spacing+2, nRows*spacing+2);
79 CGPoint position = frame.origin;
80 position.x += round( (frame.size.width -size.width )/2.0 );
81 position.y += round( (frame.size.height-size.height)/2.0 );
83 return [self initWithRows: nRows columns: nColumns
84 spacing: CGSizeMake(spacing,spacing)
91 CGColorRelease(_cellColor);
92 CGColorRelease(_lineColor);
98 static void setcolor( CGColorRef *var, CGColorRef color )
100 if( color != *var ) {
101 CGColorRelease(*var);
102 *var = CGColorRetain(color);
106 - (CGColorRef) cellColor {return _cellColor;}
107 - (void) setCellColor: (CGColorRef)cellColor {setcolor(&_cellColor,cellColor);}
109 - (CGColorRef) lineColor {return _lineColor;}
110 - (void) setLineColor: (CGColorRef)lineColor {setcolor(&_lineColor,lineColor);}
112 - (CGImageRef) backgroundImage {return _backgroundImage;}
113 - (void) setBackgroundImage: (CGImageRef)image
115 if( image != _backgroundImage ) {
116 CGImageRelease(_backgroundImage);
117 _backgroundImage = CGImageRetain(image);
121 @synthesize cellClass=_cellClass, rows=_nRows, columns=_nColumns, spacing=_spacing, reversed=_reversed,
122 usesDiagonals=_usesDiagonals, allowsMoves=_allowsMoves, allowsCaptures=_allowsCaptures,
123 bitTransform=_bitTransform;
127 #pragma mark GEOMETRY:
130 - (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col
132 if( row < _nRows && col < _nColumns ) {
133 id cell = [_cells objectAtIndex: row*_nColumns+col];
134 if( cell != [NSNull null] )
141 /** Subclasses can override this, to change the cell's class or frame. */
142 - (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col
143 suggestedFrame: (CGRect)frame
145 GridCell *cell = [[_cellClass alloc] initWithGrid: self
148 cell.name = [NSString stringWithFormat: @"%c%u", ('A'+row),(1+col)];
149 return [cell autorelease];
153 - (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col
155 NSParameterAssert(row<_nRows);
156 NSParameterAssert(col<_nColumns);
157 unsigned index = row*_nColumns+col;
158 GridCell *cell = [_cells objectAtIndex: index];
159 if( (id)cell == [NSNull null] ) {
160 unsigned effectiveRow=row, effectiveCol=col;
162 effectiveRow = _nRows-1 - effectiveRow;
163 effectiveCol = _nColumns-1 - effectiveCol;
165 CGRect frame = CGRectMake(effectiveCol*_spacing.width, effectiveRow*_spacing.height,
166 _spacing.width,_spacing.height);
167 cell = [self createCellAtRow: row column: col suggestedFrame: frame];
169 [_cells replaceObjectAtIndex: index withObject: cell];
171 [self addSublayer: cell];
173 [self insertSublayer: cell atIndex: 0];
174 [self setNeedsDisplay];
183 for( int row=0; row<_nRows; row++ )
184 for( int col=0; col<_nColumns; col++ )
185 [self addCellAtRow: row column: col];
189 - (void) removeCellAtRow: (unsigned)row column: (unsigned)col
191 NSParameterAssert(row<_nRows);
192 NSParameterAssert(col<_nColumns);
193 unsigned index = row*_nColumns+col;
194 id cell = [_cells objectAtIndex: index];
195 if( cell != [NSNull null] )
196 [cell removeFromSuperlayer];
197 [_cells replaceObjectAtIndex: index withObject: [NSNull null]];
198 [self setNeedsDisplay];
204 NSMutableArray *cells = [[_cells mutableCopy] autorelease];
205 for( int i=cells.count-1; i>=0; i-- )
206 if( [cells objectAtIndex: i] == [NSNull null] )
207 [cells removeObjectAtIndex: i];
212 - (GridCell*) cellWithName: (NSString*)name
214 for( CALayer *layer in self.sublayers )
215 if( [layer isKindOfClass: [GridCell class]] )
216 if( [name isEqualToString: ((GridCell*)layer).name] )
217 return (GridCell*)layer;
222 - (NSCountedSet*) countPiecesByPlayer
224 NSCountedSet *players = [NSCountedSet set];
225 for( GridCell *cell in self.cells ) {
226 Player *owner = cell.bit.owner;
228 [players addObject: owner];
234 - (CATransform3D) bitTransform
236 return _bitTransform;
239 - (void) setBitTransform: (CATransform3D)t
242 for( GridCell *cell in self.cells )
243 [cell setBitTransform: t];
246 - (void) updateCellTransform
248 CATransform3D t = self.aggregateTransform;
249 t.m41 = t.m42 = t.m43 = 0.0f; // remove translation component
250 t = CATransform3DInvert(t);
251 self.bitTransform = t;
256 #pragma mark GAME STATE:
259 - (NSString*) stateString
261 NSMutableString *state = [NSMutableString stringWithCapacity: _cells.count];
262 for( GridCell *cell in self.cells ) {
264 NSString *name = bit ?bit.name :@"-";
265 NSAssert(name.length==1,@"Missing or multicharacter name");
266 [state appendString: name];
271 - (void) setStateString: (NSString*)state
273 Game *game = self.game;
275 for( GridCell *cell in self.cells )
276 cell.bit = [game makePieceNamed: [state substringWithRange: NSMakeRange(i++,1)]];
280 - (BOOL) applyMoveString: (NSString*)move
283 for( NSString *ident in [move componentsSeparatedByString: @"-"] ) {
284 while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] )
285 ident = [ident substringToIndex: ident.length-1];
286 GridCell *dst = [self cellWithName: ident];
289 if( src && ! [self.game animateMoveFrom: src to: dst] )
298 #pragma mark DRAWING:
301 - (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill
303 // Subroutine of -drawInContext:. Draws all the cells, with or without a fill.
304 for( unsigned row=0; row<_nRows; row++ )
305 for( unsigned col=0; col<_nColumns; col++ ) {
306 GridCell *cell = [self cellAtRow: row column: col];
308 [cell drawInParentContext: ctx fill: fill];
312 - (void) drawBackgroundInContext: (CGContextRef)ctx
314 if( _backgroundImage ) {
315 CGRect bounds = self.bounds;
317 CGContextSaveGState(ctx);
318 CGContextRotateCTM(ctx, M_PI);
319 CGContextTranslateCTM(ctx, -bounds.size.width, -bounds.size.height);
321 CGContextDrawImage(ctx, bounds, _backgroundImage);
323 CGContextRestoreGState(ctx);
328 - (void)drawInContext:(CGContextRef)ctx
330 // Custom CALayer drawing implementation. Delegates to the cells to draw themselves
331 // in me; this is more efficient than having each cell have its own drawing.
332 [super drawInContext: ctx];
334 [self drawBackgroundInContext: ctx];
337 CGContextSetFillColorWithColor(ctx, _cellColor);
338 [self drawCellsInContext: ctx fill: YES];
341 CGContextSetStrokeColorWithColor(ctx,_lineColor);
342 [self drawCellsInContext:ctx fill: NO];
353 @implementation GridCell
356 - (id) initWithGrid: (Grid*)grid
357 row: (unsigned)row column: (unsigned)col
365 self.anchorPoint = CGPointMake(0,0);
366 self.position = frame.origin;
367 CGRect bounds = frame;
368 bounds.origin.x -= floor(bounds.origin.x); // make sure my coords fall on pixel boundaries
369 bounds.origin.y -= floor(bounds.origin.y);
370 self.bounds = bounds;
371 self.borderColor = kHighlightColor; // Used when highlighting (see -setHighlighted:)
372 [self setBitTransform: grid.bitTransform];
377 - (NSString*) description
379 return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row];
382 @synthesize grid=_grid, row=_row, column=_column;
385 - (void) setBitTransform: (CATransform3D)bitTransform
387 // To make the bitTransform relative to my center, I need to offset the center to the origin
388 // first, and then back afterwards.
389 CGSize size = self.bounds.size;
390 size.width = roundf(size.width/2.0);
391 size.height = roundf(size.height/2.0);
392 CATransform3D x = CATransform3DMakeTranslation(-size.width, -size.height, 0);
393 x = CATransform3DConcat(x, bitTransform);
394 x = CATransform3DConcat(x, CATransform3DMakeTranslation(size.width, size.height ,0));
395 self.sublayerTransform = x;
399 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
401 // Default implementation just fills or outlines the cell.
402 CGRect frame = self.frame;
404 CGContextFillRect(ctx,frame);
406 CGContextStrokeRect(ctx, frame);
410 - (void) setBit: (Bit*)bit
412 if( bit != self.bit ) {
415 bit.position = GetCGRectCenter(self.bounds);
419 - (Bit*) canDragBit: (Bit*)bit
421 if( _grid.allowsMoves && bit==self.bit )
422 return [super canDragBit: bit];
427 - (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point
429 return self.bit == nil || _grid.allowsCaptures;
435 return self.game.currentPlayer.index == 0;
439 - (NSArray*) neighbors
441 BOOL orthogonal = ! _grid.usesDiagonals;
442 NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8];
443 for( int dy=-1; dy<=1; dy++ )
444 for( int dx=-1; dx<=1; dx++ )
445 if( (dx || dy) && !(orthogonal && dx && dy) ) {
446 GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx];
448 [neighbors addObject: cell];
454 // Recursive subroutine used by getGroup:.
455 - (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner
459 if( [liberties containsObject: self] )
460 return; // already traversed
461 [liberties addObject: self];
462 } else if( bit.owner==owner ) {
463 if( [group containsObject: self] )
464 return; // already traversed
465 [group addObject: self];
466 for( GridCell *c in self.neighbors )
467 [c x_addToGroup: group liberties: liberties owner: owner];
472 - (NSSet*) getGroup: (int*)outLiberties
474 NSMutableSet *group=[NSMutableSet set], *liberties=nil;
476 liberties = [NSMutableSet set];
477 [self x_addToGroup: group liberties: liberties owner: self.bit.owner];
479 *outLiberties = liberties.count;
485 #pragma mark DRAG-AND-DROP:
488 #if ! TARGET_OS_IPHONE
490 // An image from another app can be dragged onto a Grid to change its background pattern.
492 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
494 if( CanGetCGImageFromPasteboard([sender draggingPasteboard]) )
495 return NSDragOperationCopy;
497 return NSDragOperationNone;
500 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
502 CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
504 CGColorRef pattern = CreatePatternColor(image);
505 _grid.cellColor = pattern;
506 CGColorRelease(pattern);
507 [_grid setNeedsDisplay];
522 @implementation RectGrid
525 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
526 spacing: (CGSize)spacing
527 position: (CGPoint)pos
529 self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos];
531 _cellClass = [Square class];
537 - (CGColorRef) altCellColor {return _altCellColor;}
538 - (void) setAltCellColor: (CGColorRef)altCellColor {setcolor(&_altCellColor,altCellColor);}
547 @implementation Square
550 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
553 CGColorRef c = ((RectGrid*)_grid).altCellColor;
555 if( ! ((_row+_column) & 1) )
557 CGContextSetFillColorWithColor(ctx, c);
560 [super drawInParentContext: ctx fill: fill];
564 - (void) setHighlighted: (BOOL)highlighted
566 [super setHighlighted: highlighted];
567 self.cornerRadius = self.bounds.size.width/2.0;
568 self.borderWidth = (highlighted ?6 :0);
572 - (Square*) nw {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];}
573 - (Square*) n {return (Square*)[_grid cellAtRow: _row+1 column: _column ];}
574 - (Square*) ne {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];}
575 - (Square*) e {return (Square*)[_grid cellAtRow: _row column: _column+1];}
576 - (Square*) se {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];}
577 - (Square*) s {return (Square*)[_grid cellAtRow: _row-1 column: _column ];}
578 - (Square*) sw {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];}
579 - (Square*) w {return (Square*)[_grid cellAtRow: _row column: _column-1];}
581 // Directions relative to the current player:
582 - (Square*) fl {return self.fwdIsN ?self.nw :self.se;}
583 - (Square*) f {return self.fwdIsN ?self.n :self.s;}
584 - (Square*) fr {return self.fwdIsN ?self.ne :self.sw;}
585 - (Square*) r {return self.fwdIsN ?self.e :self.w;}
586 - (Square*) br {return self.fwdIsN ?self.se :self.nw;}
587 - (Square*) b {return self.fwdIsN ?self.s :self.n;}
588 - (Square*) bl {return self.fwdIsN ?self.sw :self.ne;}
589 - (Square*) l {return self.fwdIsN ?self.w :self.e;}
592 static int sgn( int n ) {return n<0 ?-1 :(n>0 ?1 :0);}
595 - (SEL) directionToCell: (GridCell*)dst
597 static NSString* const kDirections[9] = {@"sw", @"s", @"se",
600 if( dst.grid != self.grid )
602 int dy=dst.row-_row, dx=dst.column-_column;
604 if( !( _grid.usesDiagonals && abs(dx)==abs(dy) ) )
606 NSString *dir = kDirections[ 3*(sgn(dy)+1) + (sgn(dx)+1) ];
607 return dir ?NSSelectorFromString(dir) :NULL;
610 - (NSArray*) lineToCell: (GridCell*)dst inclusive: (BOOL)inclusive;
612 SEL dir = [self directionToCell: dst];
615 NSMutableArray *line = [NSMutableArray array];
617 for( cell=self; cell; cell = [cell performSelector: dir] ) {
618 if( inclusive || (cell!=self && cell!=dst) )
619 [line addObject: cell];
623 return nil; // should be impossible, but just in case
627 #if ! TARGET_OS_IPHONE
629 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
631 CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
633 CGColorRef color = CreatePatternColor(image);
634 RectGrid *rectGrid = (RectGrid*)_grid;
635 if( rectGrid.altCellColor && ((_row+_column) & 1) )
636 rectGrid.altCellColor = color;
638 rectGrid.cellColor = color;
639 CGColorRelease(color);
640 [rectGrid setNeedsDisplay];
654 @implementation GoSquare
656 @synthesize dotted=_dotted;
658 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
661 [super drawInParentContext: ctx fill: fill];
663 CGRect frame = self.frame;
664 const CGFloat midx=floor(CGRectGetMidX(frame))+0.5,
665 midy=floor(CGRectGetMidY(frame))+0.5;
666 CGPoint p[4] = {{CGRectGetMinX(frame),midy},
667 {CGRectGetMaxX(frame),midy},
668 {midx,CGRectGetMinY(frame)},
669 {midx,CGRectGetMaxY(frame)}};
670 if( ! self.s ) p[2].y = midy;
671 if( ! self.n ) p[3].y = midy;
672 if( ! self.w ) p[0].x = midx;
673 if( ! self.e ) p[1].x = midx;
674 CGContextStrokeLineSegments(ctx, p, 4);
677 CGContextSetFillColorWithColor(ctx,_grid.lineColor);
678 CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5);
679 CGContextFillEllipseInRect(ctx, dot);