Source/Grid.m
author Jens Alfke <jens@mooseyard.com>
Thu Jul 31 11:18:13 2008 -0700 (2008-07-31)
changeset 22 4cb50131788f
parent 15 73f8c889f053
child 24 db8640a38faf
permissions -rw-r--r--
* Working on 3D rotation of game board.
* Added some colored balls ("drawn" myself using Photoshop glass effect.)
jens@0
     1
/*  This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
jens@0
     2
    http://developer.apple.com/samplecode/GeekGameBoard/
jens@0
     3
    Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
jens@0
     4
jens@0
     5
    Redistribution and use in source and binary forms, with or without modification, are permitted
jens@0
     6
    provided that the following conditions are met:
jens@0
     7
jens@0
     8
    * Redistributions of source code must retain the above copyright notice, this list of conditions
jens@0
     9
      and the following disclaimer.
jens@0
    10
    * Redistributions in binary form must reproduce the above copyright notice, this list of
jens@0
    11
      conditions and the following disclaimer in the documentation and/or other materials provided
jens@0
    12
      with the distribution.
jens@0
    13
jens@0
    14
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
jens@0
    15
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
jens@0
    16
    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
jens@0
    17
    BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
jens@0
    18
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
jens@0
    19
    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
jens@0
    20
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
jens@0
    21
    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
jens@0
    22
*/
jens@0
    23
#import "Grid.h"
jens@0
    24
#import "Bit.h"
jens@12
    25
#import "Piece.h"
jens@12
    26
#import "Game+Protected.h"
jens@10
    27
#import "Player.h"
jens@0
    28
#import "QuartzUtils.h"
jens@0
    29
jens@0
    30
jens@22
    31
@interface GridCell ()
jens@22
    32
- (void) setBitTransform: (CATransform3D)bitTransform;
jens@22
    33
@end
jens@22
    34
jens@22
    35
jens@0
    36
@implementation Grid
jens@0
    37
jens@0
    38
jens@0
    39
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
jens@0
    40
            spacing: (CGSize)spacing
jens@0
    41
           position: (CGPoint)pos
jens@0
    42
{
jens@0
    43
    NSParameterAssert(nRows>0 && nColumns>0);
jens@0
    44
    self = [super init];
jens@0
    45
    if( self ) {
jens@0
    46
        _nRows = nRows;
jens@0
    47
        _nColumns = nColumns;
jens@0
    48
        _spacing = spacing;
jens@0
    49
        _cellClass = [GridCell class];
jens@1
    50
        self.lineColor = kBlackColor;
jens@0
    51
        _allowsMoves = YES;
jens@0
    52
        _usesDiagonals = YES;
jens@22
    53
        _bitTransform = CATransform3DIdentity;
jens@0
    54
jens@0
    55
        self.bounds = CGRectMake(-1, -1, nColumns*spacing.width+2, nRows*spacing.height+2);
jens@0
    56
        self.position = pos;
jens@0
    57
        self.anchorPoint = CGPointMake(0,0);
jens@0
    58
        self.zPosition = kBoardZ;
jens@0
    59
        self.needsDisplayOnBoundsChange = YES;
jens@0
    60
        
jens@0
    61
        unsigned n = nRows*nColumns;
jens@0
    62
        _cells = [[NSMutableArray alloc] initWithCapacity: n];
jens@0
    63
        id null = [NSNull null];
jens@0
    64
        while( n-- > 0 )
jens@0
    65
            [_cells addObject: null];
jens@0
    66
jens@0
    67
        [self setNeedsDisplay];
jens@0
    68
    }
jens@0
    69
    return self;
jens@0
    70
}
jens@0
    71
jens@0
    72
jens@0
    73
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
jens@0
    74
              frame: (CGRect)frame
jens@0
    75
{
jens@0
    76
    CGFloat spacing = floor(MIN( (frame.size.width -2)/(CGFloat)nColumns,
jens@0
    77
                               (frame.size.height-2)/(CGFloat)nRows) );
jens@3
    78
    CGSize size = CGSizeMake(nColumns*spacing+2, nRows*spacing+2);
jens@3
    79
    CGPoint position = frame.origin;
jens@3
    80
    position.x += round( (frame.size.width -size.width )/2.0 );
jens@3
    81
    position.y += round( (frame.size.height-size.height)/2.0 );
jens@3
    82
jens@0
    83
    return [self initWithRows: nRows columns: nColumns
jens@0
    84
                      spacing: CGSizeMake(spacing,spacing)
jens@3
    85
                     position: position];
jens@0
    86
}
jens@0
    87
jens@0
    88
jens@0
    89
- (void) dealloc
jens@0
    90
{
jens@0
    91
    CGColorRelease(_cellColor);
jens@0
    92
    CGColorRelease(_lineColor);
jens@0
    93
    [_cells release];
jens@0
    94
    [super dealloc];
jens@0
    95
}
jens@0
    96
jens@0
    97
jens@0
    98
static void setcolor( CGColorRef *var, CGColorRef color )
jens@0
    99
{
jens@0
   100
    if( color != *var ) {
jens@0
   101
        CGColorRelease(*var);
jens@0
   102
        *var = CGColorRetain(color);
jens@0
   103
    }
jens@0
   104
}
jens@0
   105
jens@0
   106
- (CGColorRef) cellColor                        {return _cellColor;}
jens@0
   107
- (void) setCellColor: (CGColorRef)cellColor    {setcolor(&_cellColor,cellColor);}
jens@0
   108
jens@0
   109
- (CGColorRef) lineColor                        {return _lineColor;}
jens@0
   110
- (void) setLineColor: (CGColorRef)lineColor    {setcolor(&_lineColor,lineColor);}
jens@0
   111
jens@11
   112
- (CGImageRef) backgroundImage                  {return _backgroundImage;}
jens@11
   113
- (void) setBackgroundImage: (CGImageRef)image
jens@11
   114
{
jens@11
   115
    if( image != _backgroundImage ) {
jens@11
   116
        CGImageRelease(_backgroundImage);
jens@11
   117
        _backgroundImage = CGImageRetain(image);
jens@11
   118
    }
jens@11
   119
}
jens@11
   120
jens@15
   121
@synthesize cellClass=_cellClass, rows=_nRows, columns=_nColumns, spacing=_spacing, reversed=_reversed,
jens@22
   122
            usesDiagonals=_usesDiagonals, allowsMoves=_allowsMoves, allowsCaptures=_allowsCaptures,
jens@22
   123
            bitTransform=_bitTransform;
jens@0
   124
jens@0
   125
jens@0
   126
#pragma mark -
jens@0
   127
#pragma mark GEOMETRY:
jens@0
   128
jens@0
   129
jens@0
   130
- (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col
jens@0
   131
{
jens@0
   132
    if( row < _nRows && col < _nColumns ) {
jens@0
   133
        id cell = [_cells objectAtIndex: row*_nColumns+col];
jens@0
   134
        if( cell != [NSNull null] )
jens@0
   135
            return cell;
jens@0
   136
    }
jens@0
   137
    return nil;
jens@0
   138
}
jens@0
   139
jens@0
   140
jens@0
   141
/** Subclasses can override this, to change the cell's class or frame. */
jens@0
   142
- (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col 
jens@0
   143
               suggestedFrame: (CGRect)frame
jens@0
   144
{
jens@7
   145
    GridCell *cell = [[_cellClass alloc] initWithGrid: self 
jens@15
   146
                                                  row: row column: col
jens@7
   147
                                                frame: frame];
jens@10
   148
    cell.name = [NSString stringWithFormat: @"%c%u", ('A'+row),(1+col)];
jens@7
   149
    return [cell autorelease];
jens@0
   150
}
jens@0
   151
jens@0
   152
jens@0
   153
- (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col
jens@0
   154
{
jens@0
   155
    NSParameterAssert(row<_nRows);
jens@0
   156
    NSParameterAssert(col<_nColumns);
jens@0
   157
    unsigned index = row*_nColumns+col;
jens@0
   158
    GridCell *cell = [_cells objectAtIndex: index];
jens@0
   159
    if( (id)cell == [NSNull null] ) {
jens@15
   160
        unsigned effectiveRow=row, effectiveCol=col;
jens@15
   161
        if( _reversed ) {
jens@15
   162
            effectiveRow = _nRows-1    - effectiveRow;
jens@15
   163
            effectiveCol = _nColumns-1 - effectiveCol;
jens@15
   164
        }
jens@15
   165
        CGRect frame = CGRectMake(effectiveCol*_spacing.width, effectiveRow*_spacing.height,
jens@0
   166
                                  _spacing.width,_spacing.height);
jens@0
   167
        cell = [self createCellAtRow: row column: col suggestedFrame: frame];
jens@0
   168
        if( cell ) {
jens@0
   169
            [_cells replaceObjectAtIndex: index withObject: cell];
jens@22
   170
            //[self addSublayer: cell];
jens@22
   171
            [self insertSublayer: cell atIndex: 0];
jens@0
   172
            [self setNeedsDisplay];
jens@0
   173
        }
jens@0
   174
    }
jens@0
   175
    return cell;
jens@0
   176
}
jens@0
   177
jens@0
   178
jens@0
   179
- (void) addAllCells
jens@0
   180
{
jens@0
   181
    for( int row=_nRows-1; row>=0; row-- )                // makes 'upper' cells be in 'back'
jens@0
   182
        for( int col=0; col<_nColumns; col++ ) 
jens@0
   183
            [self addCellAtRow: row column: col];
jens@0
   184
}
jens@0
   185
jens@0
   186
jens@0
   187
- (void) removeCellAtRow: (unsigned)row column: (unsigned)col
jens@0
   188
{
jens@0
   189
    NSParameterAssert(row<_nRows);
jens@0
   190
    NSParameterAssert(col<_nColumns);
jens@0
   191
    unsigned index = row*_nColumns+col;
jens@0
   192
    id cell = [_cells objectAtIndex: index];
jens@0
   193
    if( cell != [NSNull null] )
jens@0
   194
        [cell removeFromSuperlayer];
jens@0
   195
    [_cells replaceObjectAtIndex: index withObject: [NSNull null]];
jens@0
   196
    [self setNeedsDisplay];
jens@0
   197
}
jens@0
   198
jens@0
   199
jens@12
   200
- (NSArray*) cells
jens@12
   201
{
jens@12
   202
    NSMutableArray *cells = [_cells mutableCopy];
jens@12
   203
    for( int i=cells.count-1; i>=0; i-- )
jens@12
   204
        if( [cells objectAtIndex: i] == [NSNull null] )
jens@12
   205
            [cells removeObjectAtIndex: i];
jens@12
   206
    return cells;
jens@12
   207
}
jens@12
   208
jens@12
   209
jens@7
   210
- (GridCell*) cellWithName: (NSString*)name
jens@7
   211
{
jens@7
   212
    for( CALayer *layer in self.sublayers )
jens@7
   213
        if( [layer isKindOfClass: [GridCell class]] )
jens@7
   214
            if( [name isEqualToString: ((GridCell*)layer).name] )
jens@7
   215
                return (GridCell*)layer;
jens@7
   216
    return nil;
jens@7
   217
}
jens@7
   218
jens@7
   219
jens@12
   220
- (NSCountedSet*) countPiecesByPlayer
jens@12
   221
{
jens@12
   222
    NSCountedSet *players = [NSCountedSet set];
jens@12
   223
    for( GridCell *cell in self.cells ) {
jens@12
   224
        Player *owner = cell.bit.owner;
jens@12
   225
        if( owner )
jens@12
   226
            [players addObject: owner];
jens@12
   227
    }
jens@12
   228
    return players;
jens@12
   229
}
jens@12
   230
jens@12
   231
jens@22
   232
- (CATransform3D) bitTransform
jens@22
   233
{
jens@22
   234
    return _bitTransform;
jens@22
   235
}
jens@22
   236
jens@22
   237
- (void) setBitTransform: (CATransform3D)t
jens@22
   238
{
jens@22
   239
    _bitTransform = t;
jens@22
   240
    for( GridCell *cell in self.cells )
jens@22
   241
        [cell setBitTransform: t];
jens@22
   242
}
jens@22
   243
jens@22
   244
- (void) updateCellTransform
jens@22
   245
{
jens@22
   246
    CATransform3D t = self.aggregateTransform;
jens@22
   247
    t.m41 = t.m42 = t.m43 = 0.0f;           // remove translation component
jens@22
   248
    t = CATransform3DInvert(t);
jens@22
   249
    self.bitTransform = t;
jens@22
   250
}
jens@22
   251
jens@12
   252
jens@12
   253
#pragma mark -
jens@12
   254
#pragma mark GAME STATE:
jens@12
   255
jens@12
   256
jens@12
   257
- (NSString*) stateString
jens@12
   258
{
jens@12
   259
    NSMutableString *state = [NSMutableString stringWithCapacity: _cells.count];
jens@12
   260
    for( GridCell *cell in self.cells ) {
jens@12
   261
        Bit *bit = cell.bit;
jens@12
   262
        NSString *name = bit ?bit.name :@"-";
jens@12
   263
        NSAssert(name.length==1,@"Missing or multicharacter name");
jens@12
   264
        [state appendString: name];
jens@12
   265
    }
jens@12
   266
    return state;
jens@12
   267
}
jens@12
   268
jens@12
   269
- (void) setStateString: (NSString*)state
jens@12
   270
{
jens@12
   271
    Game *game = self.game;
jens@12
   272
    int i = 0;
jens@12
   273
    for( GridCell *cell in self.cells )
jens@12
   274
        cell.bit = [game makePieceNamed: [state substringWithRange: NSMakeRange(i++,1)]];
jens@12
   275
}
jens@12
   276
jens@12
   277
jens@12
   278
- (BOOL) applyMoveString: (NSString*)move
jens@12
   279
{
jens@12
   280
    GridCell *src = nil;
jens@12
   281
    for( NSString *ident in [move componentsSeparatedByString: @"-"] ) {
jens@12
   282
        while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] )
jens@12
   283
            ident = [ident substringToIndex: ident.length-1];
jens@12
   284
        GridCell *dst = [self cellWithName: ident];
jens@12
   285
        if( dst == nil )
jens@12
   286
            return NO;
jens@12
   287
        if( src && ! [self.game animateMoveFrom: src to: dst] )
jens@12
   288
            return NO;
jens@12
   289
        src = dst;
jens@12
   290
    }
jens@12
   291
    return YES;
jens@12
   292
}
jens@12
   293
jens@12
   294
jens@0
   295
#pragma mark -
jens@0
   296
#pragma mark DRAWING:
jens@0
   297
jens@0
   298
jens@0
   299
- (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   300
{
jens@0
   301
    // Subroutine of -drawInContext:. Draws all the cells, with or without a fill.
jens@0
   302
    for( unsigned row=0; row<_nRows; row++ )
jens@0
   303
        for( unsigned col=0; col<_nColumns; col++ ) {
jens@0
   304
            GridCell *cell = [self cellAtRow: row column: col];
jens@0
   305
            if( cell )
jens@0
   306
                [cell drawInParentContext: ctx fill: fill];
jens@0
   307
        }
jens@0
   308
}
jens@0
   309
jens@15
   310
- (void) drawBackgroundInContext: (CGContextRef)ctx
jens@15
   311
{
jens@15
   312
    if( _backgroundImage ) {
jens@15
   313
        CGRect bounds = self.bounds;
jens@15
   314
        if( _reversed ) {
jens@15
   315
            CGContextSaveGState(ctx);
jens@15
   316
            CGContextRotateCTM(ctx, M_PI);
jens@15
   317
            CGContextTranslateCTM(ctx, -bounds.size.width, -bounds.size.height);
jens@15
   318
        }
jens@15
   319
        CGContextDrawImage(ctx, bounds, _backgroundImage);
jens@15
   320
        if( _reversed )
jens@15
   321
            CGContextRestoreGState(ctx);
jens@15
   322
    }
jens@15
   323
}
jens@15
   324
jens@0
   325
jens@0
   326
- (void)drawInContext:(CGContextRef)ctx
jens@0
   327
{
jens@0
   328
    // Custom CALayer drawing implementation. Delegates to the cells to draw themselves
jens@0
   329
    // in me; this is more efficient than having each cell have its own drawing.
jens@3
   330
    [super drawInContext: ctx];
jens@11
   331
    
jens@15
   332
    [self drawBackgroundInContext: ctx];
jens@11
   333
    
jens@0
   334
    if( _cellColor ) {
jens@0
   335
        CGContextSetFillColorWithColor(ctx, _cellColor);
jens@0
   336
        [self drawCellsInContext: ctx fill: YES];
jens@0
   337
    }
jens@0
   338
    if( _lineColor ) {
jens@0
   339
        CGContextSetStrokeColorWithColor(ctx,_lineColor);
jens@0
   340
        [self drawCellsInContext:ctx fill: NO];
jens@0
   341
    }
jens@0
   342
}
jens@0
   343
jens@0
   344
jens@0
   345
@end
jens@0
   346
jens@0
   347
jens@0
   348
jens@0
   349
#pragma mark -
jens@0
   350
jens@0
   351
@implementation GridCell
jens@0
   352
jens@0
   353
jens@0
   354
- (id) initWithGrid: (Grid*)grid 
jens@0
   355
                row: (unsigned)row column: (unsigned)col
jens@0
   356
              frame: (CGRect)frame
jens@0
   357
{
jens@0
   358
    self = [super init];
jens@0
   359
    if (self != nil) {
jens@0
   360
        _grid = grid;
jens@0
   361
        _row = row;
jens@0
   362
        _column = col;
jens@22
   363
        self.anchorPoint = CGPointMake(0,0);
jens@0
   364
        self.position = frame.origin;
jens@0
   365
        CGRect bounds = frame;
jens@0
   366
        bounds.origin.x -= floor(bounds.origin.x);  // make sure my coords fall on pixel boundaries
jens@0
   367
        bounds.origin.y -= floor(bounds.origin.y);
jens@0
   368
        self.bounds = bounds;
jens@0
   369
        self.borderColor = kHighlightColor;         // Used when highlighting (see -setHighlighted:)
jens@22
   370
        [self setBitTransform: grid.bitTransform];
jens@0
   371
    }
jens@0
   372
    return self;
jens@0
   373
}
jens@0
   374
jens@0
   375
- (NSString*) description
jens@0
   376
{
jens@0
   377
    return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row];
jens@0
   378
}
jens@0
   379
jens@0
   380
@synthesize grid=_grid, row=_row, column=_column;
jens@0
   381
jens@0
   382
jens@22
   383
- (void) setBitTransform: (CATransform3D)bitTransform
jens@22
   384
{
jens@22
   385
    // To make the bitTransform relative to my center, I need to offset the center to the origin
jens@22
   386
    // first, and then back afterwards.
jens@22
   387
    CGSize size = self.bounds.size;
jens@22
   388
    CATransform3D x = CATransform3DMakeTranslation(-size.width/2, -size.height/2,0);
jens@22
   389
    x = CATransform3DConcat(x, bitTransform);
jens@22
   390
    x = CATransform3DConcat(x, CATransform3DMakeTranslation(size.width/2, size.height/2,0));
jens@22
   391
    self.sublayerTransform = x;    
jens@22
   392
}
jens@22
   393
jens@22
   394
jens@0
   395
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   396
{
jens@0
   397
    // Default implementation just fills or outlines the cell.
jens@0
   398
    CGRect frame = self.frame;
jens@0
   399
    if( fill )
jens@0
   400
        CGContextFillRect(ctx,frame);
jens@0
   401
    else
jens@0
   402
        CGContextStrokeRect(ctx, frame);
jens@0
   403
}
jens@0
   404
jens@0
   405
jens@0
   406
- (void) setBit: (Bit*)bit
jens@0
   407
{
jens@0
   408
    if( bit != self.bit ) {
jens@0
   409
        [super setBit: bit];
jens@22
   410
        if( bit )
jens@22
   411
            bit.position = GetCGRectCenter(self.bounds);
jens@0
   412
    }
jens@0
   413
}
jens@0
   414
jens@0
   415
- (Bit*) canDragBit: (Bit*)bit
jens@0
   416
{
jens@0
   417
    if( _grid.allowsMoves && bit==self.bit )
jens@0
   418
        return [super canDragBit: bit];
jens@0
   419
    else
jens@0
   420
        return nil;
jens@0
   421
}
jens@0
   422
jens@0
   423
- (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point
jens@0
   424
{
jens@0
   425
    return self.bit == nil || _grid.allowsCaptures;
jens@0
   426
}
jens@0
   427
jens@0
   428
jens@0
   429
- (BOOL) fwdIsN 
jens@0
   430
{
jens@0
   431
    return self.game.currentPlayer.index == 0;
jens@0
   432
}
jens@0
   433
jens@0
   434
jens@0
   435
- (NSArray*) neighbors
jens@0
   436
{
jens@0
   437
    BOOL orthogonal = ! _grid.usesDiagonals;
jens@0
   438
    NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8];
jens@0
   439
    for( int dy=-1; dy<=1; dy++ )
jens@0
   440
        for( int dx=-1; dx<=1; dx++ )
jens@0
   441
            if( (dx || dy) && !(orthogonal && dx && dy) ) {
jens@0
   442
                GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx];
jens@0
   443
                if( cell )
jens@0
   444
                    [neighbors addObject: cell];
jens@0
   445
            }
jens@0
   446
    return neighbors;
jens@0
   447
}
jens@0
   448
jens@0
   449
jens@0
   450
// Recursive subroutine used by getGroup:.
jens@0
   451
- (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner
jens@0
   452
{
jens@0
   453
    Bit *bit = self.bit;
jens@0
   454
    if( bit == nil ) {
jens@0
   455
        if( [liberties containsObject: self] )
jens@0
   456
            return; // already traversed
jens@0
   457
        [liberties addObject: self];
jens@0
   458
    } else if( bit.owner==owner ) {
jens@0
   459
        if( [group containsObject: self] )
jens@0
   460
            return; // already traversed
jens@0
   461
        [group addObject: self];
jens@0
   462
        for( GridCell *c in self.neighbors )
jens@0
   463
            [c x_addToGroup: group liberties: liberties owner: owner];
jens@0
   464
    }
jens@0
   465
}
jens@0
   466
jens@0
   467
jens@0
   468
- (NSSet*) getGroup: (int*)outLiberties
jens@0
   469
{
jens@0
   470
    NSMutableSet *group=[NSMutableSet set], *liberties=nil;
jens@0
   471
    if( outLiberties )
jens@0
   472
        liberties = [NSMutableSet set];
jens@0
   473
    [self x_addToGroup: group liberties: liberties owner: self.bit.owner];
jens@0
   474
    if( outLiberties )
jens@0
   475
        *outLiberties = liberties.count;
jens@0
   476
    return group;
jens@0
   477
}
jens@0
   478
jens@0
   479
jens@0
   480
#pragma mark -
jens@0
   481
#pragma mark DRAG-AND-DROP:
jens@0
   482
jens@0
   483
jens@8
   484
#if ! TARGET_OS_IPHONE
jens@1
   485
jens@11
   486
// An image from another app can be dragged onto a Grid to change its background pattern.
jens@0
   487
jens@0
   488
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
jens@0
   489
{
jens@11
   490
    if( CanGetCGImageFromPasteboard([sender draggingPasteboard]) )
jens@0
   491
        return NSDragOperationCopy;
jens@0
   492
    else
jens@0
   493
        return NSDragOperationNone;
jens@0
   494
}
jens@0
   495
jens@0
   496
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
jens@0
   497
{
jens@11
   498
    CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
jens@0
   499
    if( image ) {
jens@0
   500
        CGColorRef pattern = CreatePatternColor(image);
jens@0
   501
        _grid.cellColor = pattern;
jens@0
   502
        CGColorRelease(pattern);
jens@0
   503
        [_grid setNeedsDisplay];
jens@0
   504
        return YES;
jens@0
   505
    } else
jens@0
   506
        return NO;
jens@0
   507
}
jens@0
   508
jens@1
   509
#endif
jens@0
   510
jens@0
   511
@end
jens@0
   512
jens@0
   513
jens@0
   514
jens@0
   515
jens@0
   516
#pragma mark -
jens@0
   517
jens@0
   518
@implementation RectGrid
jens@0
   519
jens@0
   520
jens@0
   521
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
jens@0
   522
            spacing: (CGSize)spacing
jens@0
   523
           position: (CGPoint)pos
jens@0
   524
{
jens@0
   525
    self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos];
jens@0
   526
    if( self ) {
jens@0
   527
        _cellClass = [Square class];
jens@0
   528
    }
jens@0
   529
    return self;
jens@0
   530
}
jens@0
   531
jens@0
   532
jens@0
   533
- (CGColorRef) altCellColor                         {return _altCellColor;}
jens@0
   534
- (void) setAltCellColor: (CGColorRef)altCellColor  {setcolor(&_altCellColor,altCellColor);}
jens@0
   535
jens@0
   536
jens@0
   537
@end
jens@0
   538
jens@0
   539
jens@0
   540
jens@0
   541
#pragma mark -
jens@0
   542
jens@0
   543
@implementation Square
jens@0
   544
jens@0
   545
jens@0
   546
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   547
{
jens@0
   548
    if( fill ) {
jens@0
   549
        CGColorRef c = ((RectGrid*)_grid).altCellColor;
jens@0
   550
        if( c ) {
jens@0
   551
            if( ! ((_row+_column) & 1) )
jens@0
   552
                c = _grid.cellColor;
jens@0
   553
            CGContextSetFillColorWithColor(ctx, c);
jens@0
   554
        }
jens@0
   555
    }
jens@0
   556
    [super drawInParentContext: ctx fill: fill];
jens@0
   557
}
jens@0
   558
jens@0
   559
jens@0
   560
- (void) setHighlighted: (BOOL)highlighted
jens@0
   561
{
jens@0
   562
    [super setHighlighted: highlighted];
jens@3
   563
    self.cornerRadius = self.bounds.size.width/2.0;
jens@0
   564
    self.borderWidth = (highlighted ?6 :0);
jens@0
   565
}
jens@0
   566
jens@0
   567
jens@0
   568
- (Square*) nw     {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];}
jens@0
   569
- (Square*) n      {return (Square*)[_grid cellAtRow: _row+1 column: _column  ];}
jens@0
   570
- (Square*) ne     {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];}
jens@0
   571
- (Square*) e      {return (Square*)[_grid cellAtRow: _row   column: _column+1];}
jens@0
   572
- (Square*) se     {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];}
jens@0
   573
- (Square*) s      {return (Square*)[_grid cellAtRow: _row-1 column: _column  ];}
jens@0
   574
- (Square*) sw     {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];}
jens@0
   575
- (Square*) w      {return (Square*)[_grid cellAtRow: _row   column: _column-1];}
jens@0
   576
jens@0
   577
// Directions relative to the current player:
jens@0
   578
- (Square*) fl     {return self.fwdIsN ?self.nw :self.se;}
jens@0
   579
- (Square*) f      {return self.fwdIsN ?self.n  :self.s;}
jens@0
   580
- (Square*) fr     {return self.fwdIsN ?self.ne :self.sw;}
jens@0
   581
- (Square*) r      {return self.fwdIsN ?self.e  :self.w;}
jens@0
   582
- (Square*) br     {return self.fwdIsN ?self.se :self.nw;}
jens@0
   583
- (Square*) b      {return self.fwdIsN ?self.s  :self.n;}
jens@0
   584
- (Square*) bl     {return self.fwdIsN ?self.sw :self.ne;}
jens@0
   585
- (Square*) l      {return self.fwdIsN ?self.w  :self.e;}
jens@0
   586
jens@0
   587
jens@12
   588
static int sgn( int n ) {return n<0 ?-1 :(n>0 ?1 :0);}
jens@12
   589
jens@12
   590
jens@12
   591
- (SEL) directionToCell: (GridCell*)dst
jens@12
   592
{
jens@12
   593
    static NSString* const kDirections[9] = {@"sw", @"s", @"se",
jens@12
   594
                                             @"w",  nil,  @"e",
jens@12
   595
                                             @"nw", @"n", @"ne"};
jens@12
   596
    if( dst.grid != self.grid )
jens@12
   597
        return NULL;
jens@12
   598
    int dy=dst.row-_row, dx=dst.column-_column;
jens@12
   599
    if( dx && dy )
jens@12
   600
        if( !( _grid.usesDiagonals && abs(dx)==abs(dy) ) )
jens@12
   601
            return NULL;
jens@12
   602
    NSString *dir = kDirections[ 3*(sgn(dy)+1) + (sgn(dx)+1) ];
jens@12
   603
    return dir ?NSSelectorFromString(dir) :NULL;
jens@12
   604
}
jens@12
   605
jens@12
   606
- (NSArray*) lineToCell: (GridCell*)dst inclusive: (BOOL)inclusive;
jens@12
   607
{
jens@12
   608
    SEL dir = [self directionToCell: dst];
jens@12
   609
    if( ! dir )
jens@12
   610
        return nil;
jens@12
   611
    NSMutableArray *line = [NSMutableArray array];
jens@12
   612
    GridCell *cell;
jens@12
   613
    for( cell=self; cell; cell = [cell performSelector: dir] ) {
jens@12
   614
        if( inclusive || (cell!=self && cell!=dst) )
jens@12
   615
            [line addObject: cell];
jens@12
   616
        if( cell==dst )
jens@12
   617
            return line;
jens@12
   618
    }
jens@12
   619
    return nil; // should be impossible, but just in case
jens@12
   620
}
jens@12
   621
                
jens@12
   622
jens@8
   623
#if ! TARGET_OS_IPHONE
jens@1
   624
jens@0
   625
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
jens@0
   626
{
jens@11
   627
    CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
jens@0
   628
    if( image ) {
jens@0
   629
        CGColorRef color = CreatePatternColor(image);
jens@0
   630
        RectGrid *rectGrid = (RectGrid*)_grid;
jens@0
   631
        if( rectGrid.altCellColor && ((_row+_column) & 1) )
jens@0
   632
            rectGrid.altCellColor = color;
jens@0
   633
        else
jens@0
   634
            rectGrid.cellColor = color;
jens@0
   635
        CGColorRelease(color);
jens@0
   636
        [rectGrid setNeedsDisplay];
jens@0
   637
        return YES;
jens@0
   638
    } else
jens@0
   639
        return NO;
jens@0
   640
}
jens@0
   641
jens@1
   642
#endif
jens@1
   643
jens@0
   644
@end
jens@0
   645
jens@0
   646
jens@0
   647
jens@0
   648
#pragma mark -
jens@0
   649
jens@0
   650
@implementation GoSquare
jens@0
   651
jens@0
   652
@synthesize dotted=_dotted;
jens@0
   653
jens@0
   654
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   655
{
jens@0
   656
    if( fill )
jens@0
   657
        [super drawInParentContext: ctx fill: fill];
jens@0
   658
    else {
jens@0
   659
        CGRect frame = self.frame;
jens@0
   660
        const CGFloat midx=floor(CGRectGetMidX(frame))+0.5, 
jens@0
   661
                    midy=floor(CGRectGetMidY(frame))+0.5;
jens@0
   662
        CGPoint p[4] = {{CGRectGetMinX(frame),midy},
jens@0
   663
                        {CGRectGetMaxX(frame),midy},
jens@0
   664
                        {midx,CGRectGetMinY(frame)},
jens@0
   665
                        {midx,CGRectGetMaxY(frame)}};
jens@0
   666
        if( ! self.s )  p[2].y = midy;
jens@0
   667
        if( ! self.n )  p[3].y = midy;
jens@0
   668
        if( ! self.w )  p[0].x = midx;
jens@0
   669
        if( ! self.e )  p[1].x = midx;
jens@0
   670
        CGContextStrokeLineSegments(ctx, p, 4);
jens@0
   671
        
jens@0
   672
        if( _dotted ) {
jens@0
   673
            CGContextSetFillColorWithColor(ctx,_grid.lineColor);
jens@0
   674
            CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5);
jens@0
   675
            CGContextFillEllipseInRect(ctx, dot);
jens@0
   676
        }
jens@0
   677
    }
jens@0
   678
}
jens@0
   679
jens@1
   680
jens@0
   681
@end