diff --git a/src/2048_engine.c b/src/2048_engine.c index 321879c..eac7b1f 100644 --- a/src/2048_engine.c +++ b/src/2048_engine.c @@ -2,6 +2,9 @@ #include #include "2048_engine.h" +/* Utilize block counter to improve some of the functions so they can run + * quicker */ + void gravitate(struct gamestate *g, direction d, void (*callback)(struct gamestate *g)) { @@ -72,7 +75,6 @@ void gravitate(struct gamestate *g, direction d, void (*callback)(struct gamesta } #undef swap_if_space - } void merge(struct gamestate *g, direction d, void (*callback)(struct gamestate *g)) @@ -83,6 +85,7 @@ void merge(struct gamestate *g, direction d, void (*callback)(struct gamestate * if (g->grid[x][y] && (g->grid[x][y] == g->grid[x+xoff][y+yoff])) {\ g->grid[x][y] += g->grid[x+xoff][y+yoff];\ g->grid[x+xoff][y+yoff] = 0;\ + g->blocks_in_play -= 1;\ g->score_last += g->grid[x][y];\ g->score += g->grid[x][y];\ g->moved = 1;\ @@ -136,10 +139,18 @@ void merge(struct gamestate *g, direction d, void (*callback)(struct gamestate * int end_condition(struct gamestate *g) { int ret = -1; + size_t blocks_counted = 0; size_t x, y; for (x = 0; x < g->opts->grid_width; ++x) { for (y = 0; y < g->opts->grid_height; ++y) { + if (g->grid[x][y]) { + blocks_counted++; + if (g->grid[x][y] >= g->opts->goal) + return 1; + else if (blocks_counted >= g->blocks_in_play) + return ret; + } if (g->grid[x][y] >= g->opts->goal) return 1; if (!g->grid[x][y] || ((x + 1 < g->opts->grid_width) && @@ -153,6 +164,9 @@ int end_condition(struct gamestate *g) return ret; } +/* Find a better method for getting a random square. It would be useful to keep + * track of how many blocks are in play, which we can query here, end_condition + * to improve them. */ void random_block(struct gamestate *g) { /* pick random square, if it is full, then move forward until we find @@ -164,22 +178,37 @@ void random_block(struct gamestate *g) srand(time(NULL)); } - size_t x = (size_t)rand() % g->opts->grid_width; - size_t y = (size_t)rand() % g->opts->grid_height; + /* Fix up this random number generator */ + /* Method: + * - Find a non-biased index between 0 and blocks_play, n + * - Find the nth 0 element in the array + * - insert a random value there + */ - while (g->grid[x][y]) { - x++; - if (x == g->opts->grid_width) { - x = 0; y++; - if (y == g->opts->grid_height) - y = 0; - } + /* Error here */ +#ifdef NULLO + size_t block_position = (size_t)rand() % ( + g->opts->grid_width * g->opts->grid_height - g->blocks_in_play); + + size_t i, ps; + for (i = 0, ps = 0; ps < block_position; ++i) { + if (!g->grid[i / g->opts->grid_width][i % g->opts->grid_height]) ps++; } + g->grid[i / g->opts->grid_width][i % g->opts->grid_height] + = (rand() & 3) ? g->opts->spawn_value : g->opts->spawn_value * 2; +#endif + + /* Use rudimentary for now */ + int x, y; + while (g->grid[x = rand() % g->opts->grid_width][y = rand() % g->opts->grid_height]); g->grid[x][y] = (rand() & 3) ? g->opts->spawn_value : g->opts->spawn_value * 2; + g->blocks_in_play += 1; } -static int flog10(unsigned int n) +/* This returns the number of digits in the base10 rep of n. The ceiling is + * taken so this will be one greater than required */ +static int clog10(unsigned int n) { int l = 0; while (n) n /= 10, ++l; @@ -192,28 +221,41 @@ struct gamestate* gamestate_init(struct gameoptions *opt) struct gamestate *g = malloc(sizeof(struct gamestate)); if (!g) goto gamestate_alloc_fail; + g->gridsize = opt->grid_width * opt->grid_height; - //long *grid_back = malloc(opt->grid_width * opt->grid_height * sizeof(long)); - //if (!grid_back) goto grid_back_alloc_fail; + long *grid_back = calloc(g->gridsize, sizeof(long)); + if (!grid_back) goto grid_back_alloc_fail; - g->grid = malloc(opt->grid_width * sizeof(long*)); + g->grid = malloc(opt->grid_height * sizeof(long*)); if (!g->grid) goto grid_alloc_fail; + /* Switch to two allocation version */ size_t i; - for (i = 0; i < opt->grid_height; ++i) - g->grid[i] = calloc(opt->grid_height, sizeof(long)); + long **iterator = g->grid; + for (i = 0; i < g->gridsize; i += opt->grid_width) + *iterator++ = &grid_back[i]; + + /* Switch back to the two allocs, setting pointers by iterating though */ + //size_t i; + //for (i = 0; i < opt->grid_height; ++i) + // g->grid[i] = calloc(opt->grid_height, sizeof(long)); g->moved = 0; g->score = 0; g->score_high = 0; g->score_last = 0; - g->print_width = flog10(opt->goal); + g->print_width = clog10(opt->goal); + g->blocks_in_play = 0; g->opts = opt; + /* Initial 3 random blocks */ + random_block(g); + random_block(g); + random_block(g); return g; grid_alloc_fail: -//grid_back_alloc_fail: +grid_back_alloc_fail: free(g); gamestate_alloc_fail: return NULL; @@ -237,6 +279,7 @@ struct gameoptions* gameoptions_default(void) int gamestate_tick(struct gamestate *g, direction d, void (*callback)(struct gamestate*)) { + /* Reset move. Altered by gravitate and merge if we do move */ g->moved = 0; gravitate(g, d, callback); merge(g, d, callback); @@ -247,8 +290,8 @@ int gamestate_tick(struct gamestate *g, direction d, void (*callback)(struct gam void gamestate_clear(struct gamestate *g) { free(g->opts); - free(g->grid[0]); - free(g->grid); + free(g->grid[0]); /* Free grid data */ + free(g->grid); /* Free pointers to data slots */ free(g); } diff --git a/src/2048_engine.h b/src/2048_engine.h index 57d7fe0..c126f64 100644 --- a/src/2048_engine.h +++ b/src/2048_engine.h @@ -39,11 +39,13 @@ struct gameoptions { struct gamestate { /* Game state */ long **grid; + size_t gridsize; int moved; long score; long score_high; long score_last; - int print_width; + size_t print_width; + size_t blocks_in_play; /* Options */ struct gameoptions *opts; }; @@ -54,7 +56,7 @@ void merge(struct gamestate*, direction, void (*callback)(struct gamestate*)); int end_condition(struct gamestate *); void random_block(struct gamestate *); int gamestate_tick(struct gamestate*, direction, void (*callback)(struct gamestate*)); -void gamestate_clear(struct gamestate*);; +void gamestate_clear(struct gamestate*); struct gamestate* gamestate_init(struct gameoptions *); struct gameoptions* gameoptions_default(void); #endif diff --git a/src/2048_rewrite.c b/src/2048_rewrite.c index f154217..a525c98 100644 --- a/src/2048_rewrite.c +++ b/src/2048_rewrite.c @@ -1,18 +1,34 @@ #include #include #include -#include -#include -#include #include "2048_engine.h" +/* Need to alter termios settings before actually cross-platform. */ +/* It will be something like this, maybe switch to using a time.h sleep + * method to get by os dependence, too? */ +#if (defined _WIN32 || defined __WIN64) +#include +#define syssleep(s) Sleep(s) +#elif defined __unix__ +#include +#define syssleep(s) usleep((s) * 1000) +#else +#error "Unsupported os: no sleep function" +#endif + +#ifdef HAVE_CURSES +#include +#else +#include +#endif + #define iterate(x, expr)\ do {\ size_t i;\ for (i = 0; i < x; ++i) { expr; }\ } while (0) -#ifdef HAVE_CURSES +#ifdef HAVE_CURSES /* PLATFORM/OUTPUT DEPENDENT */ void draw_screen(struct gamestate *g) { static WINDOW *gamewin; @@ -73,6 +89,9 @@ int get_keypress(void) } #else /* vt100 and standard shared functions */ + +/* Search for windows, blocking, non newline getchar */ +/* Could bypass termios that way */ struct termios sattr; void drawstate_clear() { @@ -83,13 +102,15 @@ void drawstate_init(void) { tcgetattr(STDIN_FILENO, &sattr); - /* alters terminal stdin to not echo and doesn't need \n before reading getchar */ struct termios tattr; tcgetattr(STDIN_FILENO, &tattr); tattr.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDOUT_FILENO, TCSANOW, &tattr); } +/* Need to check input queue, and remove all queued values on each getc to get + * by any caught chars during any sleeps which may occur. This is easy in the + * ncurses version (check until ERR), but requires extra tweaking here */ int get_keypress(void) { return fgetc(stdin); @@ -98,6 +119,8 @@ int get_keypress(void) void draw_screen(struct gamestate *g) { /* Clear the screen each draw if we are able to */ + + /* Check for windows clear screen */ #ifdef VT100_COMPATIBLE printf("\033[2J\033[H"); #endif @@ -123,12 +146,12 @@ void draw_screen(struct gamestate *g) iterate((g->print_width + 2) * g->opts->grid_width + 1, printf("-")); printf("\n\n"); } -#endif /* CURSES */ +#endif /* PLATFORM/OUTPUT DEPENDENT */ void ddraw(struct gamestate *g) { draw_screen(g); - usleep(40000); + syssleep(40); } int main(int argc, char **argv)