Source/Grid.m
author Jens Alfke <jens@mooseyard.com>
Sun Feb 06 16:31:03 2011 -0800 (2011-02-06)
changeset 29 0b1c315ffc64
parent 27 b0affce7beb1
permissions -rw-r--r--
Minor compiler-compatibility fixes.
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@24
   170
            if( _reversed )
jens@24
   171
                [self addSublayer: cell];
jens@24
   172
            else
jens@24
   173
                [self insertSublayer: cell atIndex: 0];
jens@0
   174
            [self setNeedsDisplay];
jens@0
   175
        }
jens@0
   176
    }
jens@0
   177
    return cell;
jens@0
   178
}
jens@0
   179
jens@0
   180
jens@0
   181
- (void) addAllCells
jens@0
   182
{
jens@24
   183
    for( int row=0; row<_nRows; row++ )
jens@0
   184
        for( int col=0; col<_nColumns; col++ ) 
jens@0
   185
            [self addCellAtRow: row column: col];
jens@0
   186
}
jens@0
   187
jens@0
   188
jens@0
   189
- (void) removeCellAtRow: (unsigned)row column: (unsigned)col
jens@0
   190
{
jens@0
   191
    NSParameterAssert(row<_nRows);
jens@0
   192
    NSParameterAssert(col<_nColumns);
jens@0
   193
    unsigned index = row*_nColumns+col;
jens@0
   194
    id cell = [_cells objectAtIndex: index];
jens@0
   195
    if( cell != [NSNull null] )
jens@0
   196
        [cell removeFromSuperlayer];
jens@0
   197
    [_cells replaceObjectAtIndex: index withObject: [NSNull null]];
jens@0
   198
    [self setNeedsDisplay];
jens@0
   199
}
jens@0
   200
jens@0
   201
jens@12
   202
- (NSArray*) cells
jens@12
   203
{
snej@27
   204
    NSMutableArray *cells = [[_cells mutableCopy] autorelease];
jens@12
   205
    for( int i=cells.count-1; i>=0; i-- )
jens@12
   206
        if( [cells objectAtIndex: i] == [NSNull null] )
jens@12
   207
            [cells removeObjectAtIndex: i];
jens@12
   208
    return cells;
jens@12
   209
}
jens@12
   210
jens@12
   211
jens@7
   212
- (GridCell*) cellWithName: (NSString*)name
jens@7
   213
{
jens@7
   214
    for( CALayer *layer in self.sublayers )
jens@7
   215
        if( [layer isKindOfClass: [GridCell class]] )
jens@7
   216
            if( [name isEqualToString: ((GridCell*)layer).name] )
jens@7
   217
                return (GridCell*)layer;
jens@7
   218
    return nil;
jens@7
   219
}
jens@7
   220
jens@7
   221
jens@12
   222
- (NSCountedSet*) countPiecesByPlayer
jens@12
   223
{
jens@12
   224
    NSCountedSet *players = [NSCountedSet set];
jens@12
   225
    for( GridCell *cell in self.cells ) {
jens@12
   226
        Player *owner = cell.bit.owner;
jens@12
   227
        if( owner )
jens@12
   228
            [players addObject: owner];
jens@12
   229
    }
jens@12
   230
    return players;
jens@12
   231
}
jens@12
   232
jens@12
   233
jens@22
   234
- (CATransform3D) bitTransform
jens@22
   235
{
jens@22
   236
    return _bitTransform;
jens@22
   237
}
jens@22
   238
jens@22
   239
- (void) setBitTransform: (CATransform3D)t
jens@22
   240
{
jens@22
   241
    _bitTransform = t;
jens@22
   242
    for( GridCell *cell in self.cells )
jens@22
   243
        [cell setBitTransform: t];
jens@22
   244
}
jens@22
   245
jens@22
   246
- (void) updateCellTransform
jens@22
   247
{
jens@22
   248
    CATransform3D t = self.aggregateTransform;
jens@22
   249
    t.m41 = t.m42 = t.m43 = 0.0f;           // remove translation component
jens@22
   250
    t = CATransform3DInvert(t);
jens@22
   251
    self.bitTransform = t;
jens@22
   252
}
jens@22
   253
jens@12
   254
jens@12
   255
#pragma mark -
jens@12
   256
#pragma mark GAME STATE:
jens@12
   257
jens@12
   258
jens@12
   259
- (NSString*) stateString
jens@12
   260
{
jens@12
   261
    NSMutableString *state = [NSMutableString stringWithCapacity: _cells.count];
jens@12
   262
    for( GridCell *cell in self.cells ) {
jens@12
   263
        Bit *bit = cell.bit;
jens@12
   264
        NSString *name = bit ?bit.name :@"-";
jens@12
   265
        NSAssert(name.length==1,@"Missing or multicharacter name");
jens@12
   266
        [state appendString: name];
jens@12
   267
    }
jens@12
   268
    return state;
jens@12
   269
}
jens@12
   270
jens@12
   271
- (void) setStateString: (NSString*)state
jens@12
   272
{
jens@12
   273
    Game *game = self.game;
jens@12
   274
    int i = 0;
jens@12
   275
    for( GridCell *cell in self.cells )
jens@12
   276
        cell.bit = [game makePieceNamed: [state substringWithRange: NSMakeRange(i++,1)]];
jens@12
   277
}
jens@12
   278
jens@12
   279
jens@12
   280
- (BOOL) applyMoveString: (NSString*)move
jens@12
   281
{
jens@12
   282
    GridCell *src = nil;
jens@12
   283
    for( NSString *ident in [move componentsSeparatedByString: @"-"] ) {
jens@12
   284
        while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] )
jens@12
   285
            ident = [ident substringToIndex: ident.length-1];
jens@12
   286
        GridCell *dst = [self cellWithName: ident];
jens@12
   287
        if( dst == nil )
jens@12
   288
            return NO;
jens@12
   289
        if( src && ! [self.game animateMoveFrom: src to: dst] )
jens@12
   290
            return NO;
jens@12
   291
        src = dst;
jens@12
   292
    }
jens@12
   293
    return YES;
jens@12
   294
}
jens@12
   295
jens@12
   296
jens@0
   297
#pragma mark -
jens@0
   298
#pragma mark DRAWING:
jens@0
   299
jens@0
   300
jens@0
   301
- (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   302
{
jens@0
   303
    // Subroutine of -drawInContext:. Draws all the cells, with or without a fill.
jens@0
   304
    for( unsigned row=0; row<_nRows; row++ )
jens@0
   305
        for( unsigned col=0; col<_nColumns; col++ ) {
jens@0
   306
            GridCell *cell = [self cellAtRow: row column: col];
jens@0
   307
            if( cell )
jens@0
   308
                [cell drawInParentContext: ctx fill: fill];
jens@0
   309
        }
jens@0
   310
}
jens@0
   311
jens@15
   312
- (void) drawBackgroundInContext: (CGContextRef)ctx
jens@15
   313
{
jens@15
   314
    if( _backgroundImage ) {
jens@15
   315
        CGRect bounds = self.bounds;
jens@15
   316
        if( _reversed ) {
jens@15
   317
            CGContextSaveGState(ctx);
jens@15
   318
            CGContextRotateCTM(ctx, M_PI);
jens@15
   319
            CGContextTranslateCTM(ctx, -bounds.size.width, -bounds.size.height);
jens@15
   320
        }
jens@15
   321
        CGContextDrawImage(ctx, bounds, _backgroundImage);
jens@15
   322
        if( _reversed )
jens@15
   323
            CGContextRestoreGState(ctx);
jens@15
   324
    }
jens@15
   325
}
jens@15
   326
jens@0
   327
jens@0
   328
- (void)drawInContext:(CGContextRef)ctx
jens@0
   329
{
jens@0
   330
    // Custom CALayer drawing implementation. Delegates to the cells to draw themselves
jens@0
   331
    // in me; this is more efficient than having each cell have its own drawing.
jens@3
   332
    [super drawInContext: ctx];
jens@11
   333
    
jens@15
   334
    [self drawBackgroundInContext: ctx];
jens@11
   335
    
jens@0
   336
    if( _cellColor ) {
jens@0
   337
        CGContextSetFillColorWithColor(ctx, _cellColor);
jens@0
   338
        [self drawCellsInContext: ctx fill: YES];
jens@0
   339
    }
jens@0
   340
    if( _lineColor ) {
jens@0
   341
        CGContextSetStrokeColorWithColor(ctx,_lineColor);
jens@0
   342
        [self drawCellsInContext:ctx fill: NO];
jens@0
   343
    }
jens@0
   344
}
jens@0
   345
jens@0
   346
jens@0
   347
@end
jens@0
   348
jens@0
   349
jens@0
   350
jens@0
   351
#pragma mark -
jens@0
   352
jens@0
   353
@implementation GridCell
jens@0
   354
jens@0
   355
jens@0
   356
- (id) initWithGrid: (Grid*)grid 
jens@0
   357
                row: (unsigned)row column: (unsigned)col
jens@0
   358
              frame: (CGRect)frame
jens@0
   359
{
jens@0
   360
    self = [super init];
jens@0
   361
    if (self != nil) {
jens@0
   362
        _grid = grid;
jens@0
   363
        _row = row;
jens@0
   364
        _column = col;
jens@22
   365
        self.anchorPoint = CGPointMake(0,0);
jens@0
   366
        self.position = frame.origin;
jens@0
   367
        CGRect bounds = frame;
jens@0
   368
        bounds.origin.x -= floor(bounds.origin.x);  // make sure my coords fall on pixel boundaries
jens@0
   369
        bounds.origin.y -= floor(bounds.origin.y);
jens@0
   370
        self.bounds = bounds;
jens@0
   371
        self.borderColor = kHighlightColor;         // Used when highlighting (see -setHighlighted:)
jens@22
   372
        [self setBitTransform: grid.bitTransform];
jens@0
   373
    }
jens@0
   374
    return self;
jens@0
   375
}
jens@0
   376
jens@0
   377
- (NSString*) description
jens@0
   378
{
jens@0
   379
    return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row];
jens@0
   380
}
jens@0
   381
jens@0
   382
@synthesize grid=_grid, row=_row, column=_column;
jens@0
   383
jens@0
   384
jens@22
   385
- (void) setBitTransform: (CATransform3D)bitTransform
jens@22
   386
{
jens@22
   387
    // To make the bitTransform relative to my center, I need to offset the center to the origin
jens@22
   388
    // first, and then back afterwards.
jens@22
   389
    CGSize size = self.bounds.size;
jens@28
   390
    size.width = roundf(size.width/2.0);
jens@28
   391
    size.height = roundf(size.height/2.0);
jens@28
   392
    CATransform3D x = CATransform3DMakeTranslation(-size.width, -size.height, 0);
jens@22
   393
    x = CATransform3DConcat(x, bitTransform);
jens@28
   394
    x = CATransform3DConcat(x, CATransform3DMakeTranslation(size.width, size.height ,0));
jens@22
   395
    self.sublayerTransform = x;    
jens@22
   396
}
jens@22
   397
jens@22
   398
jens@0
   399
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   400
{
jens@0
   401
    // Default implementation just fills or outlines the cell.
jens@0
   402
    CGRect frame = self.frame;
jens@0
   403
    if( fill )
jens@0
   404
        CGContextFillRect(ctx,frame);
jens@0
   405
    else
jens@0
   406
        CGContextStrokeRect(ctx, frame);
jens@0
   407
}
jens@0
   408
jens@0
   409
jens@0
   410
- (void) setBit: (Bit*)bit
jens@0
   411
{
jens@0
   412
    if( bit != self.bit ) {
jens@0
   413
        [super setBit: bit];
jens@22
   414
        if( bit )
jens@22
   415
            bit.position = GetCGRectCenter(self.bounds);
jens@0
   416
    }
jens@0
   417
}
jens@0
   418
jens@0
   419
- (Bit*) canDragBit: (Bit*)bit
jens@0
   420
{
jens@0
   421
    if( _grid.allowsMoves && bit==self.bit )
jens@0
   422
        return [super canDragBit: bit];
jens@0
   423
    else
jens@0
   424
        return nil;
jens@0
   425
}
jens@0
   426
jens@0
   427
- (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point
jens@0
   428
{
jens@0
   429
    return self.bit == nil || _grid.allowsCaptures;
jens@0
   430
}
jens@0
   431
jens@0
   432
jens@0
   433
- (BOOL) fwdIsN 
jens@0
   434
{
jens@0
   435
    return self.game.currentPlayer.index == 0;
jens@0
   436
}
jens@0
   437
jens@0
   438
jens@0
   439
- (NSArray*) neighbors
jens@0
   440
{
jens@0
   441
    BOOL orthogonal = ! _grid.usesDiagonals;
jens@0
   442
    NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8];
jens@0
   443
    for( int dy=-1; dy<=1; dy++ )
jens@0
   444
        for( int dx=-1; dx<=1; dx++ )
jens@0
   445
            if( (dx || dy) && !(orthogonal && dx && dy) ) {
jens@0
   446
                GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx];
jens@0
   447
                if( cell )
jens@0
   448
                    [neighbors addObject: cell];
jens@0
   449
            }
jens@0
   450
    return neighbors;
jens@0
   451
}
jens@0
   452
jens@0
   453
jens@0
   454
// Recursive subroutine used by getGroup:.
jens@0
   455
- (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner
jens@0
   456
{
jens@0
   457
    Bit *bit = self.bit;
jens@0
   458
    if( bit == nil ) {
jens@0
   459
        if( [liberties containsObject: self] )
jens@0
   460
            return; // already traversed
jens@0
   461
        [liberties addObject: self];
jens@0
   462
    } else if( bit.owner==owner ) {
jens@0
   463
        if( [group containsObject: self] )
jens@0
   464
            return; // already traversed
jens@0
   465
        [group addObject: self];
jens@0
   466
        for( GridCell *c in self.neighbors )
jens@0
   467
            [c x_addToGroup: group liberties: liberties owner: owner];
jens@0
   468
    }
jens@0
   469
}
jens@0
   470
jens@0
   471
jens@0
   472
- (NSSet*) getGroup: (int*)outLiberties
jens@0
   473
{
jens@0
   474
    NSMutableSet *group=[NSMutableSet set], *liberties=nil;
jens@0
   475
    if( outLiberties )
jens@0
   476
        liberties = [NSMutableSet set];
jens@0
   477
    [self x_addToGroup: group liberties: liberties owner: self.bit.owner];
jens@0
   478
    if( outLiberties )
jens@0
   479
        *outLiberties = liberties.count;
jens@0
   480
    return group;
jens@0
   481
}
jens@0
   482
jens@0
   483
jens@0
   484
#pragma mark -
jens@0
   485
#pragma mark DRAG-AND-DROP:
jens@0
   486
jens@0
   487
jens@8
   488
#if ! TARGET_OS_IPHONE
jens@1
   489
jens@11
   490
// An image from another app can be dragged onto a Grid to change its background pattern.
jens@0
   491
jens@0
   492
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
jens@0
   493
{
jens@11
   494
    if( CanGetCGImageFromPasteboard([sender draggingPasteboard]) )
jens@0
   495
        return NSDragOperationCopy;
jens@0
   496
    else
jens@0
   497
        return NSDragOperationNone;
jens@0
   498
}
jens@0
   499
jens@0
   500
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
jens@0
   501
{
jens@11
   502
    CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
jens@0
   503
    if( image ) {
jens@0
   504
        CGColorRef pattern = CreatePatternColor(image);
jens@0
   505
        _grid.cellColor = pattern;
jens@0
   506
        CGColorRelease(pattern);
jens@0
   507
        [_grid setNeedsDisplay];
jens@0
   508
        return YES;
jens@0
   509
    } else
jens@0
   510
        return NO;
jens@0
   511
}
jens@0
   512
jens@1
   513
#endif
jens@0
   514
jens@0
   515
@end
jens@0
   516
jens@0
   517
jens@0
   518
jens@0
   519
jens@0
   520
#pragma mark -
jens@0
   521
jens@0
   522
@implementation RectGrid
jens@0
   523
jens@0
   524
jens@0
   525
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
jens@0
   526
            spacing: (CGSize)spacing
jens@0
   527
           position: (CGPoint)pos
jens@0
   528
{
jens@0
   529
    self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos];
jens@0
   530
    if( self ) {
jens@0
   531
        _cellClass = [Square class];
jens@0
   532
    }
jens@0
   533
    return self;
jens@0
   534
}
jens@0
   535
jens@0
   536
jens@0
   537
- (CGColorRef) altCellColor                         {return _altCellColor;}
jens@0
   538
- (void) setAltCellColor: (CGColorRef)altCellColor  {setcolor(&_altCellColor,altCellColor);}
jens@0
   539
jens@0
   540
jens@0
   541
@end
jens@0
   542
jens@0
   543
jens@0
   544
jens@0
   545
#pragma mark -
jens@0
   546
jens@0
   547
@implementation Square
jens@0
   548
jens@0
   549
jens@0
   550
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   551
{
jens@0
   552
    if( fill ) {
jens@0
   553
        CGColorRef c = ((RectGrid*)_grid).altCellColor;
jens@0
   554
        if( c ) {
jens@0
   555
            if( ! ((_row+_column) & 1) )
jens@0
   556
                c = _grid.cellColor;
jens@0
   557
            CGContextSetFillColorWithColor(ctx, c);
jens@0
   558
        }
jens@0
   559
    }
jens@0
   560
    [super drawInParentContext: ctx fill: fill];
jens@0
   561
}
jens@0
   562
jens@0
   563
jens@0
   564
- (void) setHighlighted: (BOOL)highlighted
jens@0
   565
{
jens@0
   566
    [super setHighlighted: highlighted];
jens@3
   567
    self.cornerRadius = self.bounds.size.width/2.0;
jens@0
   568
    self.borderWidth = (highlighted ?6 :0);
jens@0
   569
}
jens@0
   570
jens@0
   571
jens@0
   572
- (Square*) nw     {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];}
jens@0
   573
- (Square*) n      {return (Square*)[_grid cellAtRow: _row+1 column: _column  ];}
jens@0
   574
- (Square*) ne     {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];}
jens@0
   575
- (Square*) e      {return (Square*)[_grid cellAtRow: _row   column: _column+1];}
jens@0
   576
- (Square*) se     {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];}
jens@0
   577
- (Square*) s      {return (Square*)[_grid cellAtRow: _row-1 column: _column  ];}
jens@0
   578
- (Square*) sw     {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];}
jens@0
   579
- (Square*) w      {return (Square*)[_grid cellAtRow: _row   column: _column-1];}
jens@0
   580
jens@0
   581
// Directions relative to the current player:
jens@0
   582
- (Square*) fl     {return self.fwdIsN ?self.nw :self.se;}
jens@0
   583
- (Square*) f      {return self.fwdIsN ?self.n  :self.s;}
jens@0
   584
- (Square*) fr     {return self.fwdIsN ?self.ne :self.sw;}
jens@0
   585
- (Square*) r      {return self.fwdIsN ?self.e  :self.w;}
jens@0
   586
- (Square*) br     {return self.fwdIsN ?self.se :self.nw;}
jens@0
   587
- (Square*) b      {return self.fwdIsN ?self.s  :self.n;}
jens@0
   588
- (Square*) bl     {return self.fwdIsN ?self.sw :self.ne;}
jens@0
   589
- (Square*) l      {return self.fwdIsN ?self.w  :self.e;}
jens@0
   590
jens@0
   591
jens@12
   592
static int sgn( int n ) {return n<0 ?-1 :(n>0 ?1 :0);}
jens@12
   593
jens@12
   594
jens@12
   595
- (SEL) directionToCell: (GridCell*)dst
jens@12
   596
{
jens@12
   597
    static NSString* const kDirections[9] = {@"sw", @"s", @"se",
jens@12
   598
                                             @"w",  nil,  @"e",
jens@12
   599
                                             @"nw", @"n", @"ne"};
jens@12
   600
    if( dst.grid != self.grid )
jens@12
   601
        return NULL;
jens@12
   602
    int dy=dst.row-_row, dx=dst.column-_column;
jens@12
   603
    if( dx && dy )
jens@12
   604
        if( !( _grid.usesDiagonals && abs(dx)==abs(dy) ) )
jens@12
   605
            return NULL;
jens@12
   606
    NSString *dir = kDirections[ 3*(sgn(dy)+1) + (sgn(dx)+1) ];
jens@12
   607
    return dir ?NSSelectorFromString(dir) :NULL;
jens@12
   608
}
jens@12
   609
jens@12
   610
- (NSArray*) lineToCell: (GridCell*)dst inclusive: (BOOL)inclusive;
jens@12
   611
{
jens@12
   612
    SEL dir = [self directionToCell: dst];
jens@12
   613
    if( ! dir )
jens@12
   614
        return nil;
jens@12
   615
    NSMutableArray *line = [NSMutableArray array];
jens@12
   616
    GridCell *cell;
jens@12
   617
    for( cell=self; cell; cell = [cell performSelector: dir] ) {
jens@12
   618
        if( inclusive || (cell!=self && cell!=dst) )
jens@12
   619
            [line addObject: cell];
jens@12
   620
        if( cell==dst )
jens@12
   621
            return line;
jens@12
   622
    }
jens@12
   623
    return nil; // should be impossible, but just in case
jens@12
   624
}
jens@12
   625
                
jens@12
   626
jens@8
   627
#if ! TARGET_OS_IPHONE
jens@1
   628
jens@0
   629
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
jens@0
   630
{
jens@11
   631
    CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
jens@0
   632
    if( image ) {
jens@0
   633
        CGColorRef color = CreatePatternColor(image);
jens@0
   634
        RectGrid *rectGrid = (RectGrid*)_grid;
jens@0
   635
        if( rectGrid.altCellColor && ((_row+_column) & 1) )
jens@0
   636
            rectGrid.altCellColor = color;
jens@0
   637
        else
jens@0
   638
            rectGrid.cellColor = color;
jens@0
   639
        CGColorRelease(color);
jens@0
   640
        [rectGrid setNeedsDisplay];
jens@0
   641
        return YES;
jens@0
   642
    } else
jens@0
   643
        return NO;
jens@0
   644
}
jens@0
   645
jens@1
   646
#endif
jens@1
   647
jens@0
   648
@end
jens@0
   649
jens@0
   650
jens@0
   651
jens@0
   652
#pragma mark -
jens@0
   653
jens@0
   654
@implementation GoSquare
jens@0
   655
jens@0
   656
@synthesize dotted=_dotted;
jens@0
   657
jens@0
   658
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   659
{
jens@0
   660
    if( fill )
jens@0
   661
        [super drawInParentContext: ctx fill: fill];
jens@0
   662
    else {
jens@0
   663
        CGRect frame = self.frame;
jens@0
   664
        const CGFloat midx=floor(CGRectGetMidX(frame))+0.5, 
jens@0
   665
                    midy=floor(CGRectGetMidY(frame))+0.5;
jens@0
   666
        CGPoint p[4] = {{CGRectGetMinX(frame),midy},
jens@0
   667
                        {CGRectGetMaxX(frame),midy},
jens@0
   668
                        {midx,CGRectGetMinY(frame)},
jens@0
   669
                        {midx,CGRectGetMaxY(frame)}};
jens@0
   670
        if( ! self.s )  p[2].y = midy;
jens@0
   671
        if( ! self.n )  p[3].y = midy;
jens@0
   672
        if( ! self.w )  p[0].x = midx;
jens@0
   673
        if( ! self.e )  p[1].x = midx;
jens@0
   674
        CGContextStrokeLineSegments(ctx, p, 4);
jens@0
   675
        
jens@0
   676
        if( _dotted ) {
jens@0
   677
            CGContextSetFillColorWithColor(ctx,_grid.lineColor);
jens@0
   678
            CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5);
jens@0
   679
            CGContextFillEllipseInRect(ctx, dot);
jens@0
   680
        }
jens@0
   681
    }
jens@0
   682
}
jens@0
   683
jens@1
   684
jens@0
   685
@end