Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@kballenegger
Created June 20, 2012 05:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kballenegger/ac122324fbe724165d61 to your computer and use it in GitHub Desktop.
Save kballenegger/ac122324fbe724165d61 to your computer and use it in GitHub Desktop.
//
// 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