-
-
Save kballenegger/ac122324fbe724165d61 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ATGame.m | |
// Anigma | |
// | |
// Created by Kenneth Ballenegger on 5/12/12. | |
// Copyright (c) 2012 Chartboost. All rights reserved. | |
// | |
#import "ATGame.h" | |
#pragma mark C | |
#pragma mark Private definitions | |
typedef struct { | |
short w; | |
short h; | |
cell *cells; | |
} *board; | |
#pragma mark Helpers | |
static short _board_offset(board b, short r, short c) { | |
return (b->w * r + c); | |
} | |
#pragma mark Cell functions | |
BOOL cell_test_mask(cell c, cellMask m) { | |
return ((c & m) == m); | |
} | |
cell cell_toggle_mask(cell c, cellMask m) { | |
return (c ^ m); | |
} | |
cell cell_set_mask(cell c, BOOL v, cellMask m) { | |
return (v ? c | m : c & ~m); | |
} | |
#pragma mark Board functions | |
BOOL board_test_mask(board b, short r, short c, cellMask m) { | |
return cell_test_mask(*(b->cells + _board_offset(b, r, c)), m); | |
} | |
void board_toggle_mask(board b, short r, short c, cellMask m) { | |
*(b->cells + _board_offset(b, r, c)) = cell_toggle_mask(*(b->cells + _board_offset(b, r, c)), m); | |
} | |
void board_set_mask(board b, short r, short c, BOOL v, cellMask m) { | |
*(b->cells + _board_offset(b, r, c)) = cell_set_mask(*(b->cells + _board_offset(b, r, c)), v, m); | |
} | |
#pragma mark Board construct & destruct | |
board board_new(w, h) { | |
board b = malloc(sizeof(board)); | |
b->w = w; | |
b->h = h; | |
b->cells = calloc(w * h, 1); | |
return b; | |
} | |
void board_free(board b) { | |
free(b->cells); | |
free(b); | |
} | |
#pragma mark Board accessors | |
void board_set_cell(board b, short r, short c, cell d) { | |
*(b->cells + _board_offset(b, r, c)) = d; | |
} | |
cell board_get_cell(board b, short r, short c) { | |
return *(b->cells + _board_offset(b, r, c)); | |
} | |
#pragma mark Debug | |
#ifdef DEBUG | |
void print_cell(cell c) { | |
printf("c-%c%c%c----%c", | |
(cell_test_mask(c, cellHasPlayerMask) ? 'P' : 'p'), | |
(cell_test_mask(c, cellHasBoxMask) ? 'B' : 'b'), | |
(cell_test_mask(c, cellHasObjectiveMask) ? 'O' : 'o'), | |
(cell_test_mask(c, cellHasWallMask) ? 'W' : 'w')); | |
} | |
void print_board_cell(board b, short r, short c) { | |
print_cell(*(b->cells + _board_offset(b, r, c))); | |
} | |
void print_cell_short(cell c) { | |
if (cell_test_mask(c, cellHasPlayerMask)) { | |
printf("P "); | |
} else if (cell_test_mask(c, cellHasWallMask)) { | |
printf("# "); | |
} else if (cell_test_mask(c, cellHasObjectiveMask) && cell_test_mask(c, cellHasBoxMask)) { | |
printf("@ "); | |
} else if (cell_test_mask(c, cellHasObjectiveMask)) { | |
printf("O "); | |
} else if (cell_test_mask(c, cellHasBoxMask)) { | |
printf("X "); | |
} else { | |
printf(". "); | |
} | |
} | |
void print_board(board b) { | |
for (short r = 0; r < b->h; r++) { | |
for (short c = 0; c < b->w; c++) { | |
print_cell_short(*(b->cells + _board_offset(b, r, c))); | |
} | |
printf("\n"); | |
} | |
} | |
#else | |
#define print_cell(c) /* do nothing */ | |
#define print_board_cell(b,r,c) /* do nothing */ | |
#define print_cell_short(c) /* do nothing */ | |
#define print_board(b) /* do nothing */ | |
#endif | |
// TEST CODE | |
NSData *generate_test_game_board() { | |
board b = board_new(8, 10); | |
board_set_mask(b, 0, 0, true, cellHasWallMask); | |
board_set_mask(b, 0, 1, true, cellHasWallMask); | |
board_set_mask(b, 0, 2, true, cellHasWallMask); | |
board_set_mask(b, 0, 3, true, cellHasWallMask); | |
board_set_mask(b, 0, 4, true, cellHasWallMask); | |
board_set_mask(b, 0, 5, true, cellHasWallMask); | |
board_set_mask(b, 0, 6, true, cellHasWallMask); | |
board_set_mask(b, 0, 7, true, cellHasWallMask); | |
board_set_mask(b, 1, 0, true, cellHasWallMask); | |
board_set_mask(b, 1, 3, true, cellHasWallMask); | |
board_set_mask(b, 1, 4, true, cellHasWallMask); | |
board_set_mask(b, 1, 5, true, cellHasWallMask); | |
board_set_mask(b, 2, 1, true, cellHasBoxMask); | |
board_set_mask(b, 3, 2, true, cellHasPlayerMask); | |
board_set_mask(b, 3, 3, true, cellHasBoxMask); | |
board_set_mask(b, 3, 5, true, cellHasWallMask); | |
board_set_mask(b, 4, 0, true, cellHasWallMask); | |
board_set_mask(b, 4, 2, true, cellHasBoxMask); | |
board_set_mask(b, 4, 3, true, cellHasWallMask); | |
board_set_mask(b, 4, 4, true, cellHasWallMask); | |
board_set_mask(b, 4, 5, true, cellHasWallMask); | |
board_set_mask(b, 5, 0, true, cellHasWallMask); | |
board_set_mask(b, 5, 3, true, cellHasWallMask); | |
board_set_mask(b, 5, 4, true, cellHasObjectiveMask); | |
board_set_mask(b, 5, 5, true, cellHasObjectiveMask); | |
board_set_mask(b, 6, 0, true, cellHasWallMask); | |
board_set_mask(b, 6, 2, true, cellHasWallMask); | |
board_set_mask(b, 6, 3, true, cellHasWallMask); | |
board_set_mask(b, 6, 4, true, cellHasObjectiveMask); | |
board_set_mask(b, 6, 5, true, cellHasWallMask); | |
board_set_mask(b, 6, 7, true, cellHasWallMask); | |
board_set_mask(b, 7, 6, true, cellHasWallMask); | |
board_set_mask(b, 7, 7, true, cellHasWallMask); | |
board_set_mask(b, 8, 5, true, cellHasWallMask); | |
board_set_mask(b, 8, 6, true, cellHasWallMask); | |
board_set_mask(b, 8, 7, true, cellHasWallMask); | |
board_set_mask(b, 9, 0, true, cellHasWallMask); | |
board_set_mask(b, 9, 1, true, cellHasWallMask); | |
board_set_mask(b, 9, 2, true, cellHasWallMask); | |
board_set_mask(b, 9, 3, true, cellHasWallMask); | |
board_set_mask(b, 9, 4, true, cellHasWallMask); | |
board_set_mask(b, 9, 5, true, cellHasWallMask); | |
board_set_mask(b, 9, 6, true, cellHasWallMask); | |
board_set_mask(b, 9, 7, true, cellHasWallMask); | |
NSData *data = [NSData dataWithBytes:b->cells length:8*10]; | |
board_free(b); | |
return data; | |
} | |
// TODO: undo stack | |
#pragma mark - Objective-C | |
#pragma mark Private interface | |
@interface ATGame () { | |
@private | |
board board; | |
} | |
- (void)findPlayerWithCallback:(ATPositionCallback)callback; | |
@end | |
#pragma mark Implementation | |
@implementation ATGame | |
@synthesize playerMoveCallback = _playerMoveCallback, boxMoveCallback = _boxMoveCallback; | |
@synthesize moveCount = _moveCount; | |
- (id)init { | |
NSLog(@"Must use initWithLevelData:"); abort(); | |
} | |
- (id)initWithLevelData:(NSData *)data { | |
if (self = [super init]) { | |
// init code | |
const int W = COLUMNS; | |
const int H = ROWS; | |
cell *cells = (cell *)[data bytes]; | |
self->board = board_new(W, H); | |
if ([data length] != W*H) return nil; // ensure data length | |
for (short r = 0; r < H; r++) { | |
for (short c = 0; c < W; c++) { | |
board_set_cell(self->board, r, c, *(cells + W * r + c)); | |
} | |
} | |
print_board(self->board); | |
} | |
return self; | |
} | |
- (void)dealloc { | |
board_free(self->board); | |
} | |
#pragma mark Helpers | |
- (void)findPlayerWithCallback:(ATPositionCallback)callback { | |
for (short r = 0; r < self->board->h; r++) { | |
for (short c = 0; c < self->board->w; c++) { | |
if (board_test_mask(self->board, r, c, cellHasPlayerMask)) { | |
callback(ATCoordinateMake(r, c)); | |
return; | |
} | |
} | |
} | |
} | |
#pragma mark Position callbacks | |
- (void)positionPlayerWithCallback:(ATPositionCallback)callback { | |
[self findPlayerWithCallback:callback]; | |
} | |
- (void)positionEachBoxWithCallback:(ATBoxPositionCallback)callback { | |
for (short r = 0; r < self->board->h; r++) { | |
for (short c = 0; c < self->board->w; c++) { | |
if (board_test_mask(self->board, r, c, cellHasBoxMask)) { | |
callback(ATCoordinateMake(r, c), board_test_mask(self->board, r, c, cellHasObjectiveMask)); | |
} | |
} | |
} | |
} | |
- (void)positionEachWallWithCallback:(ATPositionCallback)callback { | |
for (short r = 0; r < self->board->h; r++) { | |
for (short c = 0; c < self->board->w; c++) { | |
if (board_test_mask(self->board, r, c, cellHasWallMask)) { | |
callback(ATCoordinateMake(r, c)); | |
} | |
} | |
} | |
} | |
- (void)positionEachEmptyTileWithCallback:(ATPositionCallback)callback { | |
for (short r = 0; r < self->board->h; r++) { | |
for (short c = 0; c < self->board->w; c++) { | |
if (!board_test_mask(self->board, r, c, cellHasWallMask) && | |
!board_test_mask(self->board, r, c, cellHasObjectiveMask)) { | |
callback(ATCoordinateMake(r, c)); | |
} | |
} | |
} | |
} | |
- (void)positionEachObjectiveWithCallback:(ATPositionCallback)callback { | |
for (short r = 0; r < self->board->h; r++) { | |
for (short c = 0; c < self->board->w; c++) { | |
if (board_test_mask(self->board, r, c, cellHasObjectiveMask)) { | |
callback(ATCoordinateMake(r, c)); | |
} | |
} | |
} | |
} | |
- (cell)cellAtCoordinate:(ATCoordinate)coordinate { | |
if (![self coordinateIsInBounds:coordinate]) return '\0'; | |
return *(self->board->cells + _board_offset(self->board, coordinate.row, coordinate.column)); | |
} | |
- (BOOL)coordinateIsInBounds:(ATCoordinate)c { | |
if (c.row < 0 || c.row >= self->board->h || | |
c.column < 0 || c.column >= self->board->w) { | |
return NO; // out of bounds | |
} | |
return YES; | |
} | |
#pragma mark Attempt move | |
- (BOOL)attemptMoveToPosition:(ATCoordinate)target { | |
__block ATCoordinate position; | |
[self findPlayerWithCallback:^(ATCoordinate p){ position = p; }]; | |
// Check adjacentness | |
if (!ATCoordinatesAreAdjacent(position, target)) { | |
return NO; // not adjacent | |
} | |
// Check bounds | |
if (![self coordinateIsInBounds:target]) { | |
return NO; // out of bounds | |
} | |
// Check for wall | |
if (board_test_mask(self->board, target.row, target.column, cellHasWallMask)) { | |
return NO; // wall | |
} | |
// Check for box | |
if (board_test_mask(self->board, target.row, target.column, cellHasBoxMask)) { | |
ATCoordinate next = target; | |
ATDirection direction = ATDirectionWithCoordinates(position, target, NULL); | |
switch (direction) { | |
case north: next.row--; break; | |
case east: next.column++; break; | |
case south: next.row++; break; | |
case west: next.column--; break; | |
} | |
if (![self coordinateIsInBounds:next]) return NO; // cannot fall into the ether | |
if (board_test_mask(self->board, next.row, next.column, cellHasWallMask)) return NO; // cannot walk into a wall | |
if (board_test_mask(self->board, next.row, next.column, cellHasBoxMask)) return NO; // or into another box | |
// if we're still here, all the tests have passed and the move is valid | |
board_toggle_mask(self->board, target.row, target.column, cellHasBoxMask); | |
board_toggle_mask(self->board, next.row, next.column, cellHasBoxMask); | |
self.boxMoveCallback(target, next, board_test_mask(self->board, next.row, next.column, cellHasObjectiveMask)); | |
} | |
// If we're still here, it means the move is valid | |
board_toggle_mask(self->board, position.row, position.column, cellHasPlayerMask); | |
board_toggle_mask(self->board, target.row, target.column, cellHasPlayerMask); | |
self.playerMoveCallback(position, target); | |
// if event objective has a box, the game is won | |
BOOL winPossible = YES; | |
for (short r = 0; r < self->board->h; r++) { | |
for (short c = 0; c < self->board->w; c++) { | |
if (board_test_mask(self->board, r, c, cellHasObjectiveMask)) { | |
winPossible *= board_test_mask(self->board, r, c, cellHasBoxMask); | |
} | |
} | |
} | |
if (winPossible) { | |
printf("Game is won!\n"); | |
} | |
return YES; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment