Lots of reworking. Completed support for game history, including Turn class. Changed Game API around quite a bit.
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.
27 #import "QuartzUtils.h"
33 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
34 spacing: (CGSize)spacing
35 position: (CGPoint)pos
37 NSParameterAssert(nRows>0 && nColumns>0);
43 _cellClass = [GridCell class];
44 self.lineColor = kBlackColor;
48 self.bounds = CGRectMake(-1, -1, nColumns*spacing.width+2, nRows*spacing.height+2);
50 self.anchorPoint = CGPointMake(0,0);
51 self.zPosition = kBoardZ;
52 self.needsDisplayOnBoundsChange = YES;
54 unsigned n = nRows*nColumns;
55 _cells = [[NSMutableArray alloc] initWithCapacity: n];
56 id null = [NSNull null];
58 [_cells addObject: null];
60 [self setNeedsDisplay];
66 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
69 CGFloat spacing = floor(MIN( (frame.size.width -2)/(CGFloat)nColumns,
70 (frame.size.height-2)/(CGFloat)nRows) );
71 CGSize size = CGSizeMake(nColumns*spacing+2, nRows*spacing+2);
72 CGPoint position = frame.origin;
73 position.x += round( (frame.size.width -size.width )/2.0 );
74 position.y += round( (frame.size.height-size.height)/2.0 );
76 return [self initWithRows: nRows columns: nColumns
77 spacing: CGSizeMake(spacing,spacing)
84 CGColorRelease(_cellColor);
85 CGColorRelease(_lineColor);
91 static void setcolor( CGColorRef *var, CGColorRef color )
95 *var = CGColorRetain(color);
99 - (CGColorRef) cellColor {return _cellColor;}
100 - (void) setCellColor: (CGColorRef)cellColor {setcolor(&_cellColor,cellColor);}
102 - (CGColorRef) lineColor {return _lineColor;}
103 - (void) setLineColor: (CGColorRef)lineColor {setcolor(&_lineColor,lineColor);}
105 @synthesize cellClass=_cellClass, rows=_nRows, columns=_nColumns, spacing=_spacing,
106 usesDiagonals=_usesDiagonals, allowsMoves=_allowsMoves, allowsCaptures=_allowsCaptures;
110 #pragma mark GEOMETRY:
113 - (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col
115 if( row < _nRows && col < _nColumns ) {
116 id cell = [_cells objectAtIndex: row*_nColumns+col];
117 if( cell != [NSNull null] )
124 /** Subclasses can override this, to change the cell's class or frame. */
125 - (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col
126 suggestedFrame: (CGRect)frame
128 GridCell *cell = [[_cellClass alloc] initWithGrid: self
131 cell.name = [NSString stringWithFormat: @"%c%u", ('A'+row),(1+col)];
132 return [cell autorelease];
136 - (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col
138 NSParameterAssert(row<_nRows);
139 NSParameterAssert(col<_nColumns);
140 unsigned index = row*_nColumns+col;
141 GridCell *cell = [_cells objectAtIndex: index];
142 if( (id)cell == [NSNull null] ) {
143 CGRect frame = CGRectMake(col*_spacing.width, row*_spacing.height,
144 _spacing.width,_spacing.height);
145 cell = [self createCellAtRow: row column: col suggestedFrame: frame];
147 [_cells replaceObjectAtIndex: index withObject: cell];
148 [self addSublayer: cell];
149 [self setNeedsDisplay];
158 for( int row=_nRows-1; row>=0; row-- ) // makes 'upper' cells be in 'back'
159 for( int col=0; col<_nColumns; col++ )
160 [self addCellAtRow: row column: col];
164 - (void) removeCellAtRow: (unsigned)row column: (unsigned)col
166 NSParameterAssert(row<_nRows);
167 NSParameterAssert(col<_nColumns);
168 unsigned index = row*_nColumns+col;
169 id cell = [_cells objectAtIndex: index];
170 if( cell != [NSNull null] )
171 [cell removeFromSuperlayer];
172 [_cells replaceObjectAtIndex: index withObject: [NSNull null]];
173 [self setNeedsDisplay];
177 - (GridCell*) cellWithName: (NSString*)name
179 for( CALayer *layer in self.sublayers )
180 if( [layer isKindOfClass: [GridCell class]] )
181 if( [name isEqualToString: ((GridCell*)layer).name] )
182 return (GridCell*)layer;
188 #pragma mark DRAWING:
191 - (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill
193 // Subroutine of -drawInContext:. Draws all the cells, with or without a fill.
194 for( unsigned row=0; row<_nRows; row++ )
195 for( unsigned col=0; col<_nColumns; col++ ) {
196 GridCell *cell = [self cellAtRow: row column: col];
198 [cell drawInParentContext: ctx fill: fill];
203 - (void)drawInContext:(CGContextRef)ctx
205 // Custom CALayer drawing implementation. Delegates to the cells to draw themselves
206 // in me; this is more efficient than having each cell have its own drawing.
207 [super drawInContext: ctx];
209 CGContextSetFillColorWithColor(ctx, _cellColor);
210 [self drawCellsInContext: ctx fill: YES];
213 CGContextSetStrokeColorWithColor(ctx,_lineColor);
214 [self drawCellsInContext:ctx fill: NO];
225 @implementation GridCell
228 - (id) initWithGrid: (Grid*)grid
229 row: (unsigned)row column: (unsigned)col
237 self.position = frame.origin;
238 CGRect bounds = frame;
239 bounds.origin.x -= floor(bounds.origin.x); // make sure my coords fall on pixel boundaries
240 bounds.origin.y -= floor(bounds.origin.y);
241 self.bounds = bounds;
242 self.anchorPoint = CGPointMake(0,0);
243 self.borderColor = kHighlightColor; // Used when highlighting (see -setHighlighted:)
248 - (NSString*) description
250 return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row];
253 @synthesize grid=_grid, row=_row, column=_column;
256 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
258 // Default implementation just fills or outlines the cell.
259 CGRect frame = self.frame;
261 CGContextFillRect(ctx,frame);
263 CGContextStrokeRect(ctx, frame);
267 - (void) setBit: (Bit*)bit
269 if( bit != self.bit ) {
273 CGSize size = self.bounds.size;
274 bit.position = CGPointMake(floor(size.width/2.0),
275 floor(size.height/2.0));
280 - (Bit*) canDragBit: (Bit*)bit
282 if( _grid.allowsMoves && bit==self.bit )
283 return [super canDragBit: bit];
288 - (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point
290 return self.bit == nil || _grid.allowsCaptures;
296 return self.game.currentPlayer.index == 0;
300 - (NSArray*) neighbors
302 BOOL orthogonal = ! _grid.usesDiagonals;
303 NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8];
304 for( int dy=-1; dy<=1; dy++ )
305 for( int dx=-1; dx<=1; dx++ )
306 if( (dx || dy) && !(orthogonal && dx && dy) ) {
307 GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx];
309 [neighbors addObject: cell];
315 // Recursive subroutine used by getGroup:.
316 - (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner
320 if( [liberties containsObject: self] )
321 return; // already traversed
322 [liberties addObject: self];
323 } else if( bit.owner==owner ) {
324 if( [group containsObject: self] )
325 return; // already traversed
326 [group addObject: self];
327 for( GridCell *c in self.neighbors )
328 [c x_addToGroup: group liberties: liberties owner: owner];
333 - (NSSet*) getGroup: (int*)outLiberties
335 NSMutableSet *group=[NSMutableSet set], *liberties=nil;
337 liberties = [NSMutableSet set];
338 [self x_addToGroup: group liberties: liberties owner: self.bit.owner];
340 *outLiberties = liberties.count;
346 #pragma mark DRAG-AND-DROP:
349 #if ! TARGET_OS_IPHONE
351 // An image from another app can be dragged onto a Dispenser to change the Piece's appearance.
354 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
356 NSPasteboard *pb = [sender draggingPasteboard];
357 if( [NSImage canInitWithPasteboard: pb] )
358 return NSDragOperationCopy;
360 return NSDragOperationNone;
363 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
365 CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]);
367 CGColorRef pattern = CreatePatternColor(image);
368 _grid.cellColor = pattern;
369 CGColorRelease(pattern);
370 [_grid setNeedsDisplay];
385 @implementation RectGrid
388 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
389 spacing: (CGSize)spacing
390 position: (CGPoint)pos
392 self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos];
394 _cellClass = [Square class];
400 - (CGColorRef) altCellColor {return _altCellColor;}
401 - (void) setAltCellColor: (CGColorRef)altCellColor {setcolor(&_altCellColor,altCellColor);}
410 @implementation Square
413 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
416 CGColorRef c = ((RectGrid*)_grid).altCellColor;
418 if( ! ((_row+_column) & 1) )
420 CGContextSetFillColorWithColor(ctx, c);
423 [super drawInParentContext: ctx fill: fill];
427 - (void) setHighlighted: (BOOL)highlighted
429 [super setHighlighted: highlighted];
430 self.cornerRadius = self.bounds.size.width/2.0;
431 self.borderWidth = (highlighted ?6 :0);
435 - (Square*) nw {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];}
436 - (Square*) n {return (Square*)[_grid cellAtRow: _row+1 column: _column ];}
437 - (Square*) ne {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];}
438 - (Square*) e {return (Square*)[_grid cellAtRow: _row column: _column+1];}
439 - (Square*) se {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];}
440 - (Square*) s {return (Square*)[_grid cellAtRow: _row-1 column: _column ];}
441 - (Square*) sw {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];}
442 - (Square*) w {return (Square*)[_grid cellAtRow: _row column: _column-1];}
444 // Directions relative to the current player:
445 - (Square*) fl {return self.fwdIsN ?self.nw :self.se;}
446 - (Square*) f {return self.fwdIsN ?self.n :self.s;}
447 - (Square*) fr {return self.fwdIsN ?self.ne :self.sw;}
448 - (Square*) r {return self.fwdIsN ?self.e :self.w;}
449 - (Square*) br {return self.fwdIsN ?self.se :self.nw;}
450 - (Square*) b {return self.fwdIsN ?self.s :self.n;}
451 - (Square*) bl {return self.fwdIsN ?self.sw :self.ne;}
452 - (Square*) l {return self.fwdIsN ?self.w :self.e;}
455 #if ! TARGET_OS_IPHONE
457 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
459 CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]);
461 CGColorRef color = CreatePatternColor(image);
462 RectGrid *rectGrid = (RectGrid*)_grid;
463 if( rectGrid.altCellColor && ((_row+_column) & 1) )
464 rectGrid.altCellColor = color;
466 rectGrid.cellColor = color;
467 CGColorRelease(color);
468 [rectGrid setNeedsDisplay];
482 @implementation GoSquare
484 @synthesize dotted=_dotted;
486 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
489 [super drawInParentContext: ctx fill: fill];
491 CGRect frame = self.frame;
492 const CGFloat midx=floor(CGRectGetMidX(frame))+0.5,
493 midy=floor(CGRectGetMidY(frame))+0.5;
494 CGPoint p[4] = {{CGRectGetMinX(frame),midy},
495 {CGRectGetMaxX(frame),midy},
496 {midx,CGRectGetMinY(frame)},
497 {midx,CGRectGetMaxY(frame)}};
498 if( ! self.s ) p[2].y = midy;
499 if( ! self.n ) p[3].y = midy;
500 if( ! self.w ) p[0].x = midx;
501 if( ! self.e ) p[1].x = midx;
502 CGContextStrokeLineSegments(ctx, p, 4);
505 CGContextSetFillColorWithColor(ctx,_grid.lineColor);
506 CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5);
507 CGContextFillEllipseInRect(ctx, dot);