Source/Grid.m
author Jens Alfke <jens@mooseyard.com>
Tue Jul 08 13:12:01 2008 -0700 (2008-07-08)
changeset 13 db7bb080c3d5
parent 11 436cbdf56810
child 15 73f8c889f053
permissions -rw-r--r--
Fixed some memory leaks, and took the address-related properties out of Player.
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@0
    31
@implementation Grid
jens@0
    32
jens@0
    33
jens@0
    34
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
jens@0
    35
            spacing: (CGSize)spacing
jens@0
    36
           position: (CGPoint)pos
jens@0
    37
{
jens@0
    38
    NSParameterAssert(nRows>0 && nColumns>0);
jens@0
    39
    self = [super init];
jens@0
    40
    if( self ) {
jens@0
    41
        _nRows = nRows;
jens@0
    42
        _nColumns = nColumns;
jens@0
    43
        _spacing = spacing;
jens@0
    44
        _cellClass = [GridCell class];
jens@1
    45
        self.lineColor = kBlackColor;
jens@0
    46
        _allowsMoves = YES;
jens@0
    47
        _usesDiagonals = YES;
jens@0
    48
jens@0
    49
        self.bounds = CGRectMake(-1, -1, nColumns*spacing.width+2, nRows*spacing.height+2);
jens@0
    50
        self.position = pos;
jens@0
    51
        self.anchorPoint = CGPointMake(0,0);
jens@0
    52
        self.zPosition = kBoardZ;
jens@0
    53
        self.needsDisplayOnBoundsChange = YES;
jens@0
    54
        
jens@0
    55
        unsigned n = nRows*nColumns;
jens@0
    56
        _cells = [[NSMutableArray alloc] initWithCapacity: n];
jens@0
    57
        id null = [NSNull null];
jens@0
    58
        while( n-- > 0 )
jens@0
    59
            [_cells addObject: null];
jens@0
    60
jens@0
    61
        [self setNeedsDisplay];
jens@0
    62
    }
jens@0
    63
    return self;
jens@0
    64
}
jens@0
    65
jens@0
    66
jens@0
    67
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
jens@0
    68
              frame: (CGRect)frame
jens@0
    69
{
jens@0
    70
    CGFloat spacing = floor(MIN( (frame.size.width -2)/(CGFloat)nColumns,
jens@0
    71
                               (frame.size.height-2)/(CGFloat)nRows) );
jens@3
    72
    CGSize size = CGSizeMake(nColumns*spacing+2, nRows*spacing+2);
jens@3
    73
    CGPoint position = frame.origin;
jens@3
    74
    position.x += round( (frame.size.width -size.width )/2.0 );
jens@3
    75
    position.y += round( (frame.size.height-size.height)/2.0 );
jens@3
    76
jens@0
    77
    return [self initWithRows: nRows columns: nColumns
jens@0
    78
                      spacing: CGSizeMake(spacing,spacing)
jens@3
    79
                     position: position];
jens@0
    80
}
jens@0
    81
jens@0
    82
jens@0
    83
- (void) dealloc
jens@0
    84
{
jens@0
    85
    CGColorRelease(_cellColor);
jens@0
    86
    CGColorRelease(_lineColor);
jens@0
    87
    [_cells release];
jens@0
    88
    [super dealloc];
jens@0
    89
}
jens@0
    90
jens@0
    91
jens@0
    92
static void setcolor( CGColorRef *var, CGColorRef color )
jens@0
    93
{
jens@0
    94
    if( color != *var ) {
jens@0
    95
        CGColorRelease(*var);
jens@0
    96
        *var = CGColorRetain(color);
jens@0
    97
    }
jens@0
    98
}
jens@0
    99
jens@0
   100
- (CGColorRef) cellColor                        {return _cellColor;}
jens@0
   101
- (void) setCellColor: (CGColorRef)cellColor    {setcolor(&_cellColor,cellColor);}
jens@0
   102
jens@0
   103
- (CGColorRef) lineColor                        {return _lineColor;}
jens@0
   104
- (void) setLineColor: (CGColorRef)lineColor    {setcolor(&_lineColor,lineColor);}
jens@0
   105
jens@11
   106
- (CGImageRef) backgroundImage                  {return _backgroundImage;}
jens@11
   107
- (void) setBackgroundImage: (CGImageRef)image
jens@11
   108
{
jens@11
   109
    if( image != _backgroundImage ) {
jens@11
   110
        CGImageRelease(_backgroundImage);
jens@11
   111
        _backgroundImage = CGImageRetain(image);
jens@11
   112
    }
jens@11
   113
}
jens@11
   114
jens@0
   115
@synthesize cellClass=_cellClass, rows=_nRows, columns=_nColumns, spacing=_spacing,
jens@12
   116
            usesDiagonals=_usesDiagonals, allowsMoves=_allowsMoves, allowsCaptures=_allowsCaptures;
jens@0
   117
jens@0
   118
jens@0
   119
#pragma mark -
jens@0
   120
#pragma mark GEOMETRY:
jens@0
   121
jens@0
   122
jens@0
   123
- (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col
jens@0
   124
{
jens@0
   125
    if( row < _nRows && col < _nColumns ) {
jens@0
   126
        id cell = [_cells objectAtIndex: row*_nColumns+col];
jens@0
   127
        if( cell != [NSNull null] )
jens@0
   128
            return cell;
jens@0
   129
    }
jens@0
   130
    return nil;
jens@0
   131
}
jens@0
   132
jens@0
   133
jens@0
   134
/** Subclasses can override this, to change the cell's class or frame. */
jens@0
   135
- (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col 
jens@0
   136
               suggestedFrame: (CGRect)frame
jens@0
   137
{
jens@7
   138
    GridCell *cell = [[_cellClass alloc] initWithGrid: self 
jens@0
   139
                                        row: row column: col
jens@7
   140
                                                frame: frame];
jens@10
   141
    cell.name = [NSString stringWithFormat: @"%c%u", ('A'+row),(1+col)];
jens@7
   142
    return [cell autorelease];
jens@0
   143
}
jens@0
   144
jens@0
   145
jens@0
   146
- (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col
jens@0
   147
{
jens@0
   148
    NSParameterAssert(row<_nRows);
jens@0
   149
    NSParameterAssert(col<_nColumns);
jens@0
   150
    unsigned index = row*_nColumns+col;
jens@0
   151
    GridCell *cell = [_cells objectAtIndex: index];
jens@0
   152
    if( (id)cell == [NSNull null] ) {
jens@0
   153
        CGRect frame = CGRectMake(col*_spacing.width, row*_spacing.height,
jens@0
   154
                                  _spacing.width,_spacing.height);
jens@0
   155
        cell = [self createCellAtRow: row column: col suggestedFrame: frame];
jens@0
   156
        if( cell ) {
jens@0
   157
            [_cells replaceObjectAtIndex: index withObject: cell];
jens@0
   158
            [self addSublayer: cell];
jens@0
   159
            [self setNeedsDisplay];
jens@0
   160
        }
jens@0
   161
    }
jens@0
   162
    return cell;
jens@0
   163
}
jens@0
   164
jens@0
   165
jens@0
   166
- (void) addAllCells
jens@0
   167
{
jens@0
   168
    for( int row=_nRows-1; row>=0; row-- )                // makes 'upper' cells be in 'back'
jens@0
   169
        for( int col=0; col<_nColumns; col++ ) 
jens@0
   170
            [self addCellAtRow: row column: col];
jens@0
   171
}
jens@0
   172
jens@0
   173
jens@0
   174
- (void) removeCellAtRow: (unsigned)row column: (unsigned)col
jens@0
   175
{
jens@0
   176
    NSParameterAssert(row<_nRows);
jens@0
   177
    NSParameterAssert(col<_nColumns);
jens@0
   178
    unsigned index = row*_nColumns+col;
jens@0
   179
    id cell = [_cells objectAtIndex: index];
jens@0
   180
    if( cell != [NSNull null] )
jens@0
   181
        [cell removeFromSuperlayer];
jens@0
   182
    [_cells replaceObjectAtIndex: index withObject: [NSNull null]];
jens@0
   183
    [self setNeedsDisplay];
jens@0
   184
}
jens@0
   185
jens@0
   186
jens@12
   187
- (NSArray*) cells
jens@12
   188
{
jens@12
   189
    NSMutableArray *cells = [_cells mutableCopy];
jens@12
   190
    for( int i=cells.count-1; i>=0; i-- )
jens@12
   191
        if( [cells objectAtIndex: i] == [NSNull null] )
jens@12
   192
            [cells removeObjectAtIndex: i];
jens@12
   193
    return cells;
jens@12
   194
}
jens@12
   195
jens@12
   196
jens@7
   197
- (GridCell*) cellWithName: (NSString*)name
jens@7
   198
{
jens@7
   199
    for( CALayer *layer in self.sublayers )
jens@7
   200
        if( [layer isKindOfClass: [GridCell class]] )
jens@7
   201
            if( [name isEqualToString: ((GridCell*)layer).name] )
jens@7
   202
                return (GridCell*)layer;
jens@7
   203
    return nil;
jens@7
   204
}
jens@7
   205
jens@7
   206
jens@12
   207
- (NSCountedSet*) countPiecesByPlayer
jens@12
   208
{
jens@12
   209
    NSCountedSet *players = [NSCountedSet set];
jens@12
   210
    for( GridCell *cell in self.cells ) {
jens@12
   211
        Player *owner = cell.bit.owner;
jens@12
   212
        if( owner )
jens@12
   213
            [players addObject: owner];
jens@12
   214
    }
jens@12
   215
    return players;
jens@12
   216
}
jens@12
   217
jens@12
   218
jens@12
   219
jens@12
   220
#pragma mark -
jens@12
   221
#pragma mark GAME STATE:
jens@12
   222
jens@12
   223
jens@12
   224
- (NSString*) stateString
jens@12
   225
{
jens@12
   226
    NSMutableString *state = [NSMutableString stringWithCapacity: _cells.count];
jens@12
   227
    for( GridCell *cell in self.cells ) {
jens@12
   228
        Bit *bit = cell.bit;
jens@12
   229
        NSString *name = bit ?bit.name :@"-";
jens@12
   230
        NSAssert(name.length==1,@"Missing or multicharacter name");
jens@12
   231
        [state appendString: name];
jens@12
   232
    }
jens@12
   233
    return state;
jens@12
   234
}
jens@12
   235
jens@12
   236
- (void) setStateString: (NSString*)state
jens@12
   237
{
jens@12
   238
    Game *game = self.game;
jens@12
   239
    int i = 0;
jens@12
   240
    for( GridCell *cell in self.cells )
jens@12
   241
        cell.bit = [game makePieceNamed: [state substringWithRange: NSMakeRange(i++,1)]];
jens@12
   242
}
jens@12
   243
jens@12
   244
jens@12
   245
- (BOOL) applyMoveString: (NSString*)move
jens@12
   246
{
jens@12
   247
    GridCell *src = nil;
jens@12
   248
    for( NSString *ident in [move componentsSeparatedByString: @"-"] ) {
jens@12
   249
        while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] )
jens@12
   250
            ident = [ident substringToIndex: ident.length-1];
jens@12
   251
        GridCell *dst = [self cellWithName: ident];
jens@12
   252
        if( dst == nil )
jens@12
   253
            return NO;
jens@12
   254
        if( src && ! [self.game animateMoveFrom: src to: dst] )
jens@12
   255
            return NO;
jens@12
   256
        src = dst;
jens@12
   257
    }
jens@12
   258
    return YES;
jens@12
   259
}
jens@12
   260
jens@12
   261
jens@0
   262
#pragma mark -
jens@0
   263
#pragma mark DRAWING:
jens@0
   264
jens@0
   265
jens@0
   266
- (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   267
{
jens@0
   268
    // Subroutine of -drawInContext:. Draws all the cells, with or without a fill.
jens@0
   269
    for( unsigned row=0; row<_nRows; row++ )
jens@0
   270
        for( unsigned col=0; col<_nColumns; col++ ) {
jens@0
   271
            GridCell *cell = [self cellAtRow: row column: col];
jens@0
   272
            if( cell )
jens@0
   273
                [cell drawInParentContext: ctx fill: fill];
jens@0
   274
        }
jens@0
   275
}
jens@0
   276
jens@0
   277
jens@0
   278
- (void)drawInContext:(CGContextRef)ctx
jens@0
   279
{
jens@0
   280
    // Custom CALayer drawing implementation. Delegates to the cells to draw themselves
jens@0
   281
    // in me; this is more efficient than having each cell have its own drawing.
jens@3
   282
    [super drawInContext: ctx];
jens@11
   283
    
jens@11
   284
    if( _backgroundImage )
jens@11
   285
        CGContextDrawImage(ctx, self.bounds, _backgroundImage);
jens@11
   286
    
jens@0
   287
    if( _cellColor ) {
jens@0
   288
        CGContextSetFillColorWithColor(ctx, _cellColor);
jens@0
   289
        [self drawCellsInContext: ctx fill: YES];
jens@0
   290
    }
jens@0
   291
    if( _lineColor ) {
jens@0
   292
        CGContextSetStrokeColorWithColor(ctx,_lineColor);
jens@0
   293
        [self drawCellsInContext:ctx fill: NO];
jens@0
   294
    }
jens@0
   295
}
jens@0
   296
jens@0
   297
jens@0
   298
@end
jens@0
   299
jens@0
   300
jens@0
   301
jens@0
   302
#pragma mark -
jens@0
   303
jens@0
   304
@implementation GridCell
jens@0
   305
jens@0
   306
jens@0
   307
- (id) initWithGrid: (Grid*)grid 
jens@0
   308
                row: (unsigned)row column: (unsigned)col
jens@0
   309
              frame: (CGRect)frame
jens@0
   310
{
jens@0
   311
    self = [super init];
jens@0
   312
    if (self != nil) {
jens@0
   313
        _grid = grid;
jens@0
   314
        _row = row;
jens@0
   315
        _column = col;
jens@0
   316
        self.position = frame.origin;
jens@0
   317
        CGRect bounds = frame;
jens@0
   318
        bounds.origin.x -= floor(bounds.origin.x);  // make sure my coords fall on pixel boundaries
jens@0
   319
        bounds.origin.y -= floor(bounds.origin.y);
jens@0
   320
        self.bounds = bounds;
jens@0
   321
        self.anchorPoint = CGPointMake(0,0);
jens@0
   322
        self.borderColor = kHighlightColor;         // Used when highlighting (see -setHighlighted:)
jens@0
   323
    }
jens@0
   324
    return self;
jens@0
   325
}
jens@0
   326
jens@0
   327
- (NSString*) description
jens@0
   328
{
jens@0
   329
    return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row];
jens@0
   330
}
jens@0
   331
jens@0
   332
@synthesize grid=_grid, row=_row, column=_column;
jens@0
   333
jens@0
   334
jens@0
   335
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   336
{
jens@0
   337
    // Default implementation just fills or outlines the cell.
jens@0
   338
    CGRect frame = self.frame;
jens@0
   339
    if( fill )
jens@0
   340
        CGContextFillRect(ctx,frame);
jens@0
   341
    else
jens@0
   342
        CGContextStrokeRect(ctx, frame);
jens@0
   343
}
jens@0
   344
jens@0
   345
jens@0
   346
- (void) setBit: (Bit*)bit
jens@0
   347
{
jens@0
   348
    if( bit != self.bit ) {
jens@0
   349
        [super setBit: bit];
jens@0
   350
        if( bit ) {
jens@0
   351
            // Center it:
jens@0
   352
            CGSize size = self.bounds.size;
jens@0
   353
            bit.position = CGPointMake(floor(size.width/2.0),
jens@0
   354
                                       floor(size.height/2.0));
jens@0
   355
        }
jens@0
   356
    }
jens@0
   357
}
jens@0
   358
jens@0
   359
- (Bit*) canDragBit: (Bit*)bit
jens@0
   360
{
jens@0
   361
    if( _grid.allowsMoves && bit==self.bit )
jens@0
   362
        return [super canDragBit: bit];
jens@0
   363
    else
jens@0
   364
        return nil;
jens@0
   365
}
jens@0
   366
jens@0
   367
- (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point
jens@0
   368
{
jens@0
   369
    return self.bit == nil || _grid.allowsCaptures;
jens@0
   370
}
jens@0
   371
jens@0
   372
jens@0
   373
- (BOOL) fwdIsN 
jens@0
   374
{
jens@0
   375
    return self.game.currentPlayer.index == 0;
jens@0
   376
}
jens@0
   377
jens@0
   378
jens@0
   379
- (NSArray*) neighbors
jens@0
   380
{
jens@0
   381
    BOOL orthogonal = ! _grid.usesDiagonals;
jens@0
   382
    NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8];
jens@0
   383
    for( int dy=-1; dy<=1; dy++ )
jens@0
   384
        for( int dx=-1; dx<=1; dx++ )
jens@0
   385
            if( (dx || dy) && !(orthogonal && dx && dy) ) {
jens@0
   386
                GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx];
jens@0
   387
                if( cell )
jens@0
   388
                    [neighbors addObject: cell];
jens@0
   389
            }
jens@0
   390
    return neighbors;
jens@0
   391
}
jens@0
   392
jens@0
   393
jens@0
   394
// Recursive subroutine used by getGroup:.
jens@0
   395
- (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner
jens@0
   396
{
jens@0
   397
    Bit *bit = self.bit;
jens@0
   398
    if( bit == nil ) {
jens@0
   399
        if( [liberties containsObject: self] )
jens@0
   400
            return; // already traversed
jens@0
   401
        [liberties addObject: self];
jens@0
   402
    } else if( bit.owner==owner ) {
jens@0
   403
        if( [group containsObject: self] )
jens@0
   404
            return; // already traversed
jens@0
   405
        [group addObject: self];
jens@0
   406
        for( GridCell *c in self.neighbors )
jens@0
   407
            [c x_addToGroup: group liberties: liberties owner: owner];
jens@0
   408
    }
jens@0
   409
}
jens@0
   410
jens@0
   411
jens@0
   412
- (NSSet*) getGroup: (int*)outLiberties
jens@0
   413
{
jens@0
   414
    NSMutableSet *group=[NSMutableSet set], *liberties=nil;
jens@0
   415
    if( outLiberties )
jens@0
   416
        liberties = [NSMutableSet set];
jens@0
   417
    [self x_addToGroup: group liberties: liberties owner: self.bit.owner];
jens@0
   418
    if( outLiberties )
jens@0
   419
        *outLiberties = liberties.count;
jens@0
   420
    return group;
jens@0
   421
}
jens@0
   422
jens@0
   423
jens@0
   424
#pragma mark -
jens@0
   425
#pragma mark DRAG-AND-DROP:
jens@0
   426
jens@0
   427
jens@8
   428
#if ! TARGET_OS_IPHONE
jens@1
   429
jens@11
   430
// An image from another app can be dragged onto a Grid to change its background pattern.
jens@0
   431
jens@0
   432
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
jens@0
   433
{
jens@11
   434
    if( CanGetCGImageFromPasteboard([sender draggingPasteboard]) )
jens@0
   435
        return NSDragOperationCopy;
jens@0
   436
    else
jens@0
   437
        return NSDragOperationNone;
jens@0
   438
}
jens@0
   439
jens@0
   440
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
jens@0
   441
{
jens@11
   442
    CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
jens@0
   443
    if( image ) {
jens@0
   444
        CGColorRef pattern = CreatePatternColor(image);
jens@0
   445
        _grid.cellColor = pattern;
jens@0
   446
        CGColorRelease(pattern);
jens@0
   447
        [_grid setNeedsDisplay];
jens@0
   448
        return YES;
jens@0
   449
    } else
jens@0
   450
        return NO;
jens@0
   451
}
jens@0
   452
jens@1
   453
#endif
jens@0
   454
jens@0
   455
@end
jens@0
   456
jens@0
   457
jens@0
   458
jens@0
   459
jens@0
   460
#pragma mark -
jens@0
   461
jens@0
   462
@implementation RectGrid
jens@0
   463
jens@0
   464
jens@0
   465
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
jens@0
   466
            spacing: (CGSize)spacing
jens@0
   467
           position: (CGPoint)pos
jens@0
   468
{
jens@0
   469
    self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos];
jens@0
   470
    if( self ) {
jens@0
   471
        _cellClass = [Square class];
jens@0
   472
    }
jens@0
   473
    return self;
jens@0
   474
}
jens@0
   475
jens@0
   476
jens@0
   477
- (CGColorRef) altCellColor                         {return _altCellColor;}
jens@0
   478
- (void) setAltCellColor: (CGColorRef)altCellColor  {setcolor(&_altCellColor,altCellColor);}
jens@0
   479
jens@0
   480
jens@0
   481
@end
jens@0
   482
jens@0
   483
jens@0
   484
jens@0
   485
#pragma mark -
jens@0
   486
jens@0
   487
@implementation Square
jens@0
   488
jens@0
   489
jens@0
   490
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   491
{
jens@0
   492
    if( fill ) {
jens@0
   493
        CGColorRef c = ((RectGrid*)_grid).altCellColor;
jens@0
   494
        if( c ) {
jens@0
   495
            if( ! ((_row+_column) & 1) )
jens@0
   496
                c = _grid.cellColor;
jens@0
   497
            CGContextSetFillColorWithColor(ctx, c);
jens@0
   498
        }
jens@0
   499
    }
jens@0
   500
    [super drawInParentContext: ctx fill: fill];
jens@0
   501
}
jens@0
   502
jens@0
   503
jens@0
   504
- (void) setHighlighted: (BOOL)highlighted
jens@0
   505
{
jens@0
   506
    [super setHighlighted: highlighted];
jens@3
   507
    self.cornerRadius = self.bounds.size.width/2.0;
jens@0
   508
    self.borderWidth = (highlighted ?6 :0);
jens@0
   509
}
jens@0
   510
jens@0
   511
jens@0
   512
- (Square*) nw     {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];}
jens@0
   513
- (Square*) n      {return (Square*)[_grid cellAtRow: _row+1 column: _column  ];}
jens@0
   514
- (Square*) ne     {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];}
jens@0
   515
- (Square*) e      {return (Square*)[_grid cellAtRow: _row   column: _column+1];}
jens@0
   516
- (Square*) se     {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];}
jens@0
   517
- (Square*) s      {return (Square*)[_grid cellAtRow: _row-1 column: _column  ];}
jens@0
   518
- (Square*) sw     {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];}
jens@0
   519
- (Square*) w      {return (Square*)[_grid cellAtRow: _row   column: _column-1];}
jens@0
   520
jens@0
   521
// Directions relative to the current player:
jens@0
   522
- (Square*) fl     {return self.fwdIsN ?self.nw :self.se;}
jens@0
   523
- (Square*) f      {return self.fwdIsN ?self.n  :self.s;}
jens@0
   524
- (Square*) fr     {return self.fwdIsN ?self.ne :self.sw;}
jens@0
   525
- (Square*) r      {return self.fwdIsN ?self.e  :self.w;}
jens@0
   526
- (Square*) br     {return self.fwdIsN ?self.se :self.nw;}
jens@0
   527
- (Square*) b      {return self.fwdIsN ?self.s  :self.n;}
jens@0
   528
- (Square*) bl     {return self.fwdIsN ?self.sw :self.ne;}
jens@0
   529
- (Square*) l      {return self.fwdIsN ?self.w  :self.e;}
jens@0
   530
jens@0
   531
jens@12
   532
static int sgn( int n ) {return n<0 ?-1 :(n>0 ?1 :0);}
jens@12
   533
jens@12
   534
jens@12
   535
- (SEL) directionToCell: (GridCell*)dst
jens@12
   536
{
jens@12
   537
    static NSString* const kDirections[9] = {@"sw", @"s", @"se",
jens@12
   538
                                             @"w",  nil,  @"e",
jens@12
   539
                                             @"nw", @"n", @"ne"};
jens@12
   540
    if( dst.grid != self.grid )
jens@12
   541
        return NULL;
jens@12
   542
    int dy=dst.row-_row, dx=dst.column-_column;
jens@12
   543
    if( dx && dy )
jens@12
   544
        if( !( _grid.usesDiagonals && abs(dx)==abs(dy) ) )
jens@12
   545
            return NULL;
jens@12
   546
    NSString *dir = kDirections[ 3*(sgn(dy)+1) + (sgn(dx)+1) ];
jens@12
   547
    return dir ?NSSelectorFromString(dir) :NULL;
jens@12
   548
}
jens@12
   549
jens@12
   550
- (NSArray*) lineToCell: (GridCell*)dst inclusive: (BOOL)inclusive;
jens@12
   551
{
jens@12
   552
    SEL dir = [self directionToCell: dst];
jens@12
   553
    if( ! dir )
jens@12
   554
        return nil;
jens@12
   555
    NSMutableArray *line = [NSMutableArray array];
jens@12
   556
    GridCell *cell;
jens@12
   557
    for( cell=self; cell; cell = [cell performSelector: dir] ) {
jens@12
   558
        if( inclusive || (cell!=self && cell!=dst) )
jens@12
   559
            [line addObject: cell];
jens@12
   560
        if( cell==dst )
jens@12
   561
            return line;
jens@12
   562
    }
jens@12
   563
    return nil; // should be impossible, but just in case
jens@12
   564
}
jens@12
   565
                
jens@12
   566
jens@8
   567
#if ! TARGET_OS_IPHONE
jens@1
   568
jens@0
   569
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
jens@0
   570
{
jens@11
   571
    CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
jens@0
   572
    if( image ) {
jens@0
   573
        CGColorRef color = CreatePatternColor(image);
jens@0
   574
        RectGrid *rectGrid = (RectGrid*)_grid;
jens@0
   575
        if( rectGrid.altCellColor && ((_row+_column) & 1) )
jens@0
   576
            rectGrid.altCellColor = color;
jens@0
   577
        else
jens@0
   578
            rectGrid.cellColor = color;
jens@0
   579
        CGColorRelease(color);
jens@0
   580
        [rectGrid setNeedsDisplay];
jens@0
   581
        return YES;
jens@0
   582
    } else
jens@0
   583
        return NO;
jens@0
   584
}
jens@0
   585
jens@1
   586
#endif
jens@1
   587
jens@0
   588
@end
jens@0
   589
jens@0
   590
jens@0
   591
jens@0
   592
#pragma mark -
jens@0
   593
jens@0
   594
@implementation GoSquare
jens@0
   595
jens@0
   596
@synthesize dotted=_dotted;
jens@0
   597
jens@0
   598
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
jens@0
   599
{
jens@0
   600
    if( fill )
jens@0
   601
        [super drawInParentContext: ctx fill: fill];
jens@0
   602
    else {
jens@0
   603
        CGRect frame = self.frame;
jens@0
   604
        const CGFloat midx=floor(CGRectGetMidX(frame))+0.5, 
jens@0
   605
                    midy=floor(CGRectGetMidY(frame))+0.5;
jens@0
   606
        CGPoint p[4] = {{CGRectGetMinX(frame),midy},
jens@0
   607
                        {CGRectGetMaxX(frame),midy},
jens@0
   608
                        {midx,CGRectGetMinY(frame)},
jens@0
   609
                        {midx,CGRectGetMaxY(frame)}};
jens@0
   610
        if( ! self.s )  p[2].y = midy;
jens@0
   611
        if( ! self.n )  p[3].y = midy;
jens@0
   612
        if( ! self.w )  p[0].x = midx;
jens@0
   613
        if( ! self.e )  p[1].x = midx;
jens@0
   614
        CGContextStrokeLineSegments(ctx, p, 4);
jens@0
   615
        
jens@0
   616
        if( _dotted ) {
jens@0
   617
            CGContextSetFillColorWithColor(ctx,_grid.lineColor);
jens@0
   618
            CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5);
jens@0
   619
            CGContextFillEllipseInRect(ctx, dot);
jens@0
   620
        }
jens@0
   621
    }
jens@0
   622
}
jens@0
   623
jens@1
   624
jens@0
   625
@end