From 8843fcb658f7b235ef6a9fc530bf9e1e3158f2c7 Mon Sep 17 00:00:00 2001 From: Tiehuis Date: Fri, 12 Sep 2014 23:17:04 +1200 Subject: [PATCH] Cleaned up source, directories, and added header file to improve general readability --- 2048_curses.c | 413 ----------------------- Makefile | 17 +- README.md | 13 +- src/2048.c | 350 +++++++++++++++++++ src/2048.h | 55 +++ 2048_no_curses.c => src/2048_no_curses.c | 103 ++---- 6 files changed, 448 insertions(+), 503 deletions(-) delete mode 100644 2048_curses.c create mode 100644 src/2048.c create mode 100644 src/2048.h rename 2048_no_curses.c => src/2048_no_curses.c (80%) diff --git a/2048_curses.c b/2048_curses.c deleted file mode 100644 index 9836c08..0000000 --- a/2048_curses.c +++ /dev/null @@ -1,413 +0,0 @@ -#include -#include /* for malloc */ -#include /* for time */ -#include /* for getopts */ - -// Repeat an expression y, x times */ -#define ITER(x, expr)\ - do {\ - int i;\ - for (i = 0; i < x; i++){ expr;}\ - } while (0) - -/* Allocates a square pointer of array of arrays onto heap */ -#define CALLOC2D(ptr, sz)\ - do {\ - int i;\ - ptr = calloc(sz, sizeof(*ptr));\ - for (i = 0; i < sz; i++)\ - ptr[i] = calloc(sz, sizeof(*ptr));\ - } while (0) - -/* Frees a square pointer of arrays to arrays */ -#define FREE2D(ptr, sz)\ - do {\ - int i;\ - for (i = 0; i < sz; i++)\ - free(ptr[i]);\ - free(ptr);\ - } while (0) - -/* should be executed each turn */ -#define TURN(x)\ - gravitate(x) +\ - merge(x) +\ - gravitate(x) - -/* maximum length of any value in grid, for printing */ -int MAXVAL; - -/* direction enumeration */ -enum {DR, DU, DL, DD}; - -/* grid pointer */ -int **g; - -/* grid size */ -int SZ; - -/* score, and last turn score */ -int s; -int sl; - -/* highscore */ -int hs; - -/* highscore file */ -char *file; - -/* Merges adjacent squares of the same value together in a certain direction */ -int merge(int d) -{ - int moved = 0; - if (d == DL) { - int i, j; - for (i = 0; i < SZ; i++) { - for (j = 0; j < SZ; j++) { - if (j + 1 < SZ && g[i][j] && g[i][j] == g[i][j + 1]) { - g[i][j] <<= 1; - sl += g[i][j]; - s += g[i][j]; - g[i][j++ + 1] = 0; - moved = 1; - } - } - } - } - else if (d == DU) { - int i, j; - for (i = 0; i < SZ; i++) { - for (j = 0; j < SZ; j++) { - if (j + 1 < SZ && g[j][i] && g[j][i] == g[j + 1][i]) { - g[j][i] <<= 1; - sl += g[j][i]; - s += g[j][i]; - g[j++ + 1][i] = 0; - moved = 1; - } - } - } - } - else if (d == DR) { - int i, j; - for (i = SZ - 1; i >= 0; i--) { - for (j = SZ - 1; j >= 0; j--) { - if (j > 0 && g[i][j] && g[i][j] == g[i][j - 1]) { - g[i][j] <<= 1; - sl += g[i][j]; - s += g[i][j]; - g[i][j-- - 1] = 0; - moved = 1; - } - } - } - } - else if (d == DD) { - int i, j; - for (i = SZ - 1; i >= 0; i--) { - for (j = SZ - 1; j >= 0; j--) { - if (j > 0 && g[j][i] && g[j][i] == g[j - 1][i]) { - g[j][i] <<= 1; - sl += g[j][i]; - s += g[j][i]; - g[j-- - 1][i] = 0; - moved = 1; - } - } - } - } - return moved; -} - - -/* move all values in the grid to the edge given by the direction pressed */ -/* would be nice to generalize this code a little so didn't need four loops */ -/* if animations are wanted, then need to alter this so it moves each square one at a time */ -int gravitate(int d) -{ - int moved = 0; - if (d == DL) { - int i, j; - for (i = 0; i < SZ; i++) { - for (j = 0; j < SZ - 1; j++) { - if (g[i][j]) continue; - int st = 1; - while (j + st < SZ && !g[i][j + st]) st++; - if (j + st < SZ) { - g[i][j] = g[i][j + st]; - g[i][j + st] = 0; - moved = 1; - } - } - } - } - else if (d == DU) { - int i, j; - for (i = 0; i < SZ; i++) { - for (j = 0; j < SZ - 1; j++) { - if (g[j][i]) continue; - int st = 1; - while (j + st < SZ && !g[j + st][i]) st++; - if (j + st < SZ) { - g[j][i] = g[j + st][i]; - g[j + st][i] = 0; - moved = 1; - } - } - } - } - else if (d == DR) { - int i, j; - for (i = 0; i < SZ; i++) { - for (j = SZ - 1; j > 0; j--) { - if (g[i][j]) continue; - int st = 1; - while (j - st >= 0 && !g[i][j - st]) st++; - if (j - st >= 0) { - g[i][j] = g[i][j - st]; - g[i][j - st] = 0; - moved = 1; - } - } - } - } - else if (d == DD) { - int i, j; - for (i = 0; i < SZ; i++) { - for (j = SZ - 1; j > 0; j--) { - if (g[j][i]) continue; - int st = 1; - while (j - st >= 0 && !g[j - st][i]) st++; - if (j - st >= 0) { - g[j][i] = g[j - st][i]; - g[j - st][i] = 0; - moved = 1; - } - } - } - } - return moved; -} - -/* load hiscore */ -void load_score() { - FILE *fd = fopen(file, "r"); - if (fd == NULL) fd = fopen(file, "w+"); - if (fscanf(fd, "%d", &hs) == EOF) hs = 0; - fclose(fd); -} - -/* saves hiscore, but only if playing on standard size grid */ -void save_score() { - if (s > hs && SZ == 4) { - hs = s; - FILE *fd = fopen(file, "w+"); - fprintf(fd, "%d", hs); - fclose(fd); - } -} - -/* returns if there are any possible moves */ -int moves_available() -{ - int i, j; - for (i = 0; i < SZ; i++) - for (j = 0; j < SZ; j++) - if (!g[i][j] || ((i + 1 < SZ) && (g[i][j] == g[i + 1][j])) || ((j + 1 < SZ) && (g[i][j] == g[i][j + 1]))) - return 1; - return 0; -} - -/* places a random block onto the grid - either a 4, or a 2 with a chance of 1:3 respectively */ -/* could do this in a much smarter fashion by finding which spaces are free */ -void rand_block() -{ - int x_p, y_p; - while (g[x_p = rand() % SZ][y_p = rand() % SZ]); - g[x_p][y_p] = (rand() & 3) ? 2 : 4; -} - -/* quick floor log2(n) */ -int flog2(int n) -{ - int k = 0; - while (n) - k++, n >>= 1; - return k; -} - -/* draws the grid and fills it with the current values */ -/* colors just rotate around, works for now, can be confusing when you have some fairly high values on the board */ -void draw_grid(WINDOW *gamewin) -{ - // mvwprintw will sometimes have a useless arg, this is warned, but doesn't affect the program - char *scr = sl ? "SCORE: %d (+%d)\n" : "SCORE: %d\n"; - mvwprintw(gamewin, 0, 0, scr, s, sl); - mvwprintw(gamewin, 1, 0, "HISCR: %d\n", hs); - - ITER(SZ*(MAXVAL + 2) + 1, waddch(gamewin, '-')); - int i, j, xps = 0, yps = 3; - for (i = 0; i < SZ; i++, xps = 0, yps++) { - mvwprintw(gamewin, yps, xps++, "|"); - for (j = 0; j < SZ; j++) { - if (g[i][j]) { - wattron(gamewin, COLOR_PAIR(flog2(g[i][j]) & 7)); - mvwprintw(gamewin, yps, xps, "%*d", MAXVAL, g[i][j]); - wattroff(gamewin, COLOR_PAIR(flog2(g[i][j]) & 7)); - mvwprintw(gamewin, yps, xps + MAXVAL, " |"); - } - else { - ITER(MAXVAL + 1, waddch(gamewin, ' ')); - waddch(gamewin, '|'); - } - xps += (MAXVAL + 2); - } - } - ITER(SZ*(MAXVAL + 2) + 1, waddch(gamewin, '-')); - wrefresh(gamewin); -} - -/* entry point for the program */ -/* parses options and stores the main game loop */ -int main(int argc, char **argv) -{ - /* init ncurses environment */ - initscr(); - cbreak(); - noecho(); - curs_set(FALSE); - - /* init variables */ - file = ".hs2048g"; - hs = 0; - s = 0; - sl = 0; - SZ = 4; - MAXVAL = 4; - CALLOC2D(g, SZ); - - load_score(); - int n_blocks = 1; - - /* parse options */ - int c; - while ((c = getopt(argc, argv, "rchs:b:")) != -1) { - switch (c) { - // color support - assumes your terminal can display colours - // should still work regardless - case 'c': - start_color(); - init_pair(1, 1, 0); - init_pair(2, 2, 0); - init_pair(3, 3, 0); - init_pair(4, 4, 0); - init_pair(5, 5, 0); - init_pair(6, 6, 0); - init_pair(7, 7, 0); - break; - // different board sizes - case 's': - FREE2D(g, SZ); - int optint = atoi(optarg); - SZ = optint > 4 ? optint : 4; - CALLOC2D(g, SZ); - break; - // different block spawn rate - case 'b': - n_blocks = atoi(optarg); - break; - // reset hiscores - case 'r': - endwin(); - printf("Are you sure you want to reset your highscores? (Y)es or (N)o\n"); - int response; - if ((response = getchar()) == 'y' || response == 'Y') { - FILE *fd = fopen(file, "w+"); - fclose(fd); - } - exit(EXIT_SUCCESS); - // help menu - case 'h': - endwin(); - printf("Controls:\n" - " hjkl, wasd Movement\n" - " q Quit\n" - "\n" - "Usage:\n" - " 2048 [options]\n" - "\n" - "Options:\n" - " -s Set the grid border length\n" - " -b Set the block spawn rate\n" - " -c Enables color support\n"); - exit(EXIT_SUCCESS); - } - } - - int width = SZ * (MAXVAL + 2) + 1; - int height = SZ * (MAXVAL + 2) + 3; - - // might center in middle of screen - WINDOW *gamewin = newwin(height, width, 1, 1); - keypad(gamewin, TRUE); - - /* random seed */ - srand((unsigned int)time(NULL)); - ITER(2, rand_block()); - draw_grid(gamewin); - - int key, moved; - while (1) { - /* will goto this if we didn't get a valid keypress */ - retry:; - moved = 0; - key = wgetch(gamewin); - sl = 0; - - /* should check if anything changed during merge and if not retry */ - switch (key) { - case 'h': - case 'a': - case KEY_LEFT: - moved = TURN(DL); - break; - case 'l': - case 'd': - case KEY_RIGHT: - moved = TURN(DR); - break; - case 'j': - case 's': - case KEY_DOWN: - moved = TURN(DD); - break; - case 'k': - case 'w': - case KEY_UP: - moved = TURN(DU); - break; - case 'q': - FREE2D(g, SZ); - erase(); - refresh(); - endwin(); - save_score(); - exit(EXIT_SUCCESS); - default: - goto retry; - } - - if (!moves_available()) { - endwin(); - printf("\n" - "YOU LOSE! - Your score was %d\n", s); - save_score(); - exit(EXIT_SUCCESS); - } - - if (moved) { - ITER(n_blocks, rand_block()); - draw_grid(gamewin); - } - } -} diff --git a/Makefile b/Makefile index 70fd8d9..c5fe6b4 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,14 @@ -CC?=gcc -CFLAGS+= +CC ?= gcc +CFLAGS += -Wall -Wextra +LIBS = -lcurses -all: 2048 2048_no_curses +all: 2048 -2048: 2048_curses.c - $(CC) $(CFLAGS) 2048_curses.c -lcurses -o 2048 +2048: src/2048.c + $(CC) $(CFLAGS) src/2048.c -o 2048 $(LIBS) -2048_no_curses: 2048_no_curses.c - $(CC) $(CFLAGS) 2048_no_curses.c -o 2048_no_curses +2048nc: src/2048_no_curses.c + $(CC) $(CFLAGS) src/2048_no_curses.c -o 2048nc clean: - rm -f 2048 2048_no_curses + rm -f 2048 2048nc diff --git a/README.md b/README.md index 66799d6..c9e763e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ #2048-cli -A cli version of the game [2048](https://github.com/gabrielecirulli/2048) for your Linux terminal. +A cli version of the game [2048](https://github.com/gabrielecirulli/2048) for your Linux +terminal. #####2048_curses.c ![Screenshot](http://i.imgur.com/QU7t5mH.png) @@ -9,11 +10,13 @@ A cli version of the game [2048](https://github.com/gabrielecirulli/2048) for yo ![Screenshot](http://i.imgur.com/fwZEvdh.png) ## Installation -If you want to use the ncurses version, make sure that you have the required ncurses library and link against this during compilation with `-lcurses`. The program creates and uses a file named `.hs2048g` in the directory it is run. Ensure that you have no file of this name in the directory otherwise it will be overwritten. +This requires the ncurses library to link against during compilation. It is available +in most package managers. The program creates and uses a file name `.hs2048g` in the +working directory. Any file with this name will be modified and replaced. + ### Get - cd ~/$INSTALL_DIR - wget https://raw.githubusercontent.com/Tiehuis/2048-cli/master/2048_no_curses.c - gcc 2048_no_curses.c -o 2048 + git clone https://github.com/Tiehuis/2048-cli.git + make ### Run ./2048 diff --git a/src/2048.c b/src/2048.c new file mode 100644 index 0000000..b00828e --- /dev/null +++ b/src/2048.c @@ -0,0 +1,350 @@ +/* + * 2048.c - Main version that should be worked on. + * + * Things to do: + * - Finish cleaning up code. + * - Ask user if they would like to play again instead of exiting immediately. + * - Add an actual goal (what happens when we reach 2048?). + * - Keep track list of past scores, and store in a better fashion independent + * of cwd. Also, store in not in plaintext. + * */ + +#include /* for malloc */ +#include /* for time */ +#include /* for getopts */ +#include +#include "2048.h" + +int **grid; /* grid pointer */ +int grid_size; /* grid size */ +int score; /* score */ +int score_last; /* Score for last move */ +int score_high; /* Hiscore */ +int printwidth; /* maximum length of any value in grid, for printing */ + +/* Merges adjacent squares of the same value together in a certain direction */ +int merge(dir_t d) +{ + int moved = 0; + int i, j; + + if (d == DIR_LEFT || d == DIR_RIGHT) { + for (i = 0; i < grid_size; i++) { + for (j = 0; j < grid_size - 1; j++) { + if (grid[i][j] == grid[i][j + 1]) { + grid[i][j] <<= 1; + grid[i][j + 1] = 0; + score_last += grid[i][j]; + score += grid[i][j]; + moved = 1; + } + } + } + } + else { + for (i = 0; i < grid_size - 1; i++) { + for (j = 0; j < grid_size; j++) { + if (grid[i][j] == grid[i + 1][j]) { + grid[i][j] <<= 2; + grid[i + 1][j] = 0; + score_last += grid[i][j]; + score += grid[i][j]; + moved = 1; + } + } + } + } + + return moved; +} + + +/* move all values in the grid to the edge given by the direction pressed */ +/* would be nice to generalize this code a little so didn't need four loops */ +/* if animations are wanted, then need to alter this so it moves each square one at a time */ +int gravitate(dir_t d) +{ + int moved = 0; + int i, j; + + if (d == DIR_LEFT) { + for (i = 0; i < grid_size; i++) { + for (j = 0; j < grid_size - 1; j++) { + if (grid[i][j]) continue; + int st = 1; + while (j + st < grid_size && !grid[i][j + st]) st++; + if (j + st < grid_size) { + grid[i][j] = grid[i][j + st]; + grid[i][j + st] = 0; + moved = 1; + } + } + } + } + else if (d == DIR_UP) { + for (i = 0; i < grid_size; i++) { + for (j = 0; j < grid_size - 1; j++) { + if (grid[j][i]) continue; + int st = 1; + while (j + st < grid_size && !grid[j + st][i]) st++; + if (j + st < grid_size) { + grid[j][i] = grid[j + st][i]; + grid[j + st][i] = 0; + moved = 1; + } + } + } + } + else if (d == DIR_RIGHT) { + for (i = 0; i < grid_size; i++) { + for (j = grid_size - 1; j > 0; j--) { + if (grid[i][j]) continue; + int st = 1; + while (j - st >= 0 && !grid[i][j - st]) st++; + if (j - st >= 0) { + grid[i][j] = grid[i][j - st]; + grid[i][j - st] = 0; + moved = 1; + } + } + } + } + else if (d == DIR_DOWN) { + for (i = 0; i < grid_size; i++) { + for (j = grid_size - 1; j > 0; j--) { + if (grid[j][i]) continue; + int st = 1; + while (j - st >= 0 && !grid[j - st][i]) st++; + if (j - st >= 0) { + grid[j][i] = grid[j - st][i]; + grid[j - st][i] = 0; + moved = 1; + } + } + } + } + return moved; +} + +/* Return the current highscore */ +int get_score_high() { + int s = 0; + FILE *fd = fopen(HISCORE_FILE, "r"); + if (fd == NULL) + fd = fopen(HISCORE_FILE, "w+"); + + fscanf(fd, "%d", &s); + fclose(fd); + return s; +} + +/* saves hiscore, but only if playing on standard size grid */ +void save_score_high() { + if (score > score_high && grid_size == 4) { + score_high = score; + FILE *fd = fopen(HISCORE_FILE, "w+"); + fprintf(fd, "%d", score_high); + fclose(fd); + } +} + +/* returns if there are any possible moves */ +int moves_available() +{ + int i, j; + for (i = 0; i < grid_size; i++) + for (j = 0; j < grid_size; j++) + if (!grid[i][j] + || ((i + 1 < grid_size) && (grid[i][j] == grid[i + 1][j])) + || ((j + 1 < grid_size) && (grid[i][j] == grid[i][j + 1]))) + return 1; + return 0; +} + +/* places a random block onto the grid - either a 4, or a 2 with a chance of 1:3 respectively */ +/* could do this in a much smarter fashion by finding which spaces are free */ +void rand_block() +{ + int x, y; + while (grid[x = rand() % grid_size][y = rand() % grid_size]); + grid[x][y] = (rand() & 3) ? 2 : 4; +} + +/* quick floor log2(n) */ +int flog2(int n) +{ + int k = 0; + while (n) + k++, n >>= 1; + return k; +} + +/* draws the grid and fills it with the current values */ +/* colors just rotate around, works for now, can be confusing when you have some fairly high values on the board */ +void draw_grid(WINDOW *gamewin) +{ + // mvwprintw will sometimes have a useless arg, this is warned, but doesn't affect the program + char *scr = score_last ? "SCORE: %d (+%d)\n" : "SCORE: %d\n"; + mvwprintw(gamewin, 0, 0, scr, score, score_last); + mvwprintw(gamewin, 1, 0, "HISCR: %d\n", score_high); + + ITER(grid_size*(printwidth + 2) + 1, waddch(gamewin, '-')); + int i, j, xps = 0, yps = 3; + for (i = 0; i < grid_size; i++, xps = 0, yps++) { + mvwprintw(gamewin, yps, xps++, "|"); + for (j = 0; j < grid_size; j++) { + if (grid[i][j]) { + wattron(gamewin, COLOR_PAIR(flog2(grid[i][j]) & 7)); + mvwprintw(gamewin, yps, xps, "%*d", printwidth, grid[i][j]); + wattroff(gamewin, COLOR_PAIR(flog2(grid[i][j]) & 7)); + mvwprintw(gamewin, yps, xps + printwidth, " |"); + } + else { + ITER(printwidth + 1, waddch(gamewin, ' ')); + waddch(gamewin, '|'); + } + xps += (printwidth + 2); + } + } + ITER(grid_size*(printwidth + 2) + 1, waddch(gamewin, '-')); + wrefresh(gamewin); +} + +/* entry point for the program */ +/* parses options and stores the main game loop */ +int main(int argc, char **argv) +{ + /* init ncurses environment */ + initscr(); + cbreak(); + noecho(); + curs_set(FALSE); + + /* init variables */ + score = 0; + score_last = 0; + score_high = get_score_high(); + grid_size = DEFAULT_GRID_SIZE; + printwidth = DEFAULT_GRID_SIZE; + + int n_blocks = 1; + + /* parse options */ + int c; + while ((c = getopt(argc, argv, "rchs:b:")) != -1) { + switch (c) { + /* Color support */ + case 'c': + if (has_colors()) { + start_color(); + init_pair(0, 1, 0); + init_pair(1, 2, 0); + init_pair(2, 3, 0); + init_pair(3, 4, 0); + init_pair(4, 5, 0); + init_pair(5, 6, 0); + init_pair(6, 7, 0); + } + else { + fprintf(stderr, "Terminal does not support color\n"); + } + break; + // different board sizes + case 's':; + int optint = strtol(optarg, NULL, 10); + grid_size = optint > DEFAULT_GRID_SIZE ? optint : DEFAULT_GRID_SIZE; + break; + // different block spawn rate + case 'b': + n_blocks = strtol(optarg, NULL, 10); + break; + // reset hiscores + case 'r': + endwin(); + printf("Are you sure you want to reset your highscores? (Y)es or (N)o\n"); + int response; + if ((response = getchar()) == 'y' || response == 'Y') { + FILE *fd = fopen(HISCORE_FILE, "w+"); + fclose(fd); + } + exit(EXIT_SUCCESS); + // help menu + case 'h': + endwin(); + printf(USAGE_STR); + exit(EXIT_SUCCESS); + } + } + + /* Allocate memory once we actually know amount */ + CALLOC2D(grid, grid_size); + + int width = grid_size * (printwidth + 2) + 1; + int height = grid_size * (printwidth + 2) + 3; + + // might center in middle of screen + WINDOW *gamewin = newwin(height, width, 1, 1); + keypad(gamewin, TRUE); + + /* random seed */ + srand((unsigned int)time(NULL)); + ITER(2, rand_block()); + draw_grid(gamewin); + + int key, moved; + while (1) { + /* will goto this if we didn't get a valid keypress */ +retry:; + moved = 0; + score_last = 0; + key = wgetch(gamewin); + + /* should check if anything changed during merge and if not retry */ + switch (key) { + case 'h': + case 'a': + case KEY_LEFT: + moved = TURN(DIR_LEFT); + break; + case 'l': + case 'd': + case KEY_RIGHT: + moved = TURN(DIR_RIGHT); + break; + case 'j': + case 's': + case KEY_DOWN: + moved = TURN(DIR_DOWN); + break; + case 'k': + case 'w': + case KEY_UP: + moved = TURN(DIR_UP); + break; + case 'q': + FREE2D(grid, grid_size); + erase(); + refresh(); + endwin(); + save_score_high(); + exit(EXIT_SUCCESS); + default: + goto retry; + } + + if (!moves_available()) { + endwin(); + printf("\n" + "YOU LOSE! - Your score was %d\n", score); + save_score_high(); + exit(EXIT_SUCCESS); + } + + if (moved) { + ITER(n_blocks, rand_block()); + draw_grid(gamewin); + } + } + + return 0; +} diff --git a/src/2048.h b/src/2048.h new file mode 100644 index 0000000..d1f5b3e --- /dev/null +++ b/src/2048.h @@ -0,0 +1,55 @@ +#ifndef _2048_H_ +#define _2048_H_ + +typedef enum { + DIR_UP, + DIR_RIGHT, + DIR_DOWN, + DIR_LEFT +} dir_t; + +// Repeat an expression y, x times */ +#define ITER(x, expr)\ + do {\ + int i;\ + for (i = 0; i < (x); i++){ expr;}\ + } while (0) + +/* Allocates a square pointer of array of arrays onto heap */ +#define CALLOC2D(ptr, sz)\ + do {\ + int i;\ + ptr = calloc((sz), sizeof(*ptr));\ + for (i = 0; i < (sz); i++)\ + ptr[i] = calloc((sz), sizeof(*ptr));\ + } while (0) + +/* Frees a square pointer of arrays to arrays */ +#define FREE2D(ptr, sz)\ + do {\ + int i;\ + for (i = 0; i < (sz); i++)\ + free(ptr[i]);\ + free(ptr);\ + } while (0) + +/* What occurs during a 'turn' of execution */ +#define TURN(x) (gravitate(x) + merge(x) + gravitate(x)) + +/* Constants */ +#define DEFAULT_GRID_SIZE 4 +#define HISCORE_FILE ".hs2048g" +#define USAGE_STR\ + "Usage:\n"\ + " ./2048 [options]\n"\ + "\n"\ + "Controls:\n"\ + " hjkl, wasd Movement\n"\ + " q Quit\n"\ + "\n"\ + "Options:\n"\ + " -s Set the grid border length\n"\ + " -b Set the block spawn rate\n"\ + " -c Enables color support (ncurses version only)\n" + +#endif diff --git a/2048_no_curses.c b/src/2048_no_curses.c similarity index 80% rename from 2048_no_curses.c rename to src/2048_no_curses.c index 66dfd4e..ce281f8 100644 --- a/2048_no_curses.c +++ b/src/2048_no_curses.c @@ -1,66 +1,25 @@ -/* animations would be nice */ +/* + * 2048_no_curses.c - Non-curses version that can be updated at a later time + **/ #include /* for printf */ #include /* for malloc */ #include /* */ #include /* for time */ #include /* for getopts */ +#include "2048.h" -// Repeat an expression y, x times */ -#define ITER(x, expr)\ - do {\ - int i;\ - for (i = 0; i < x; i++){ expr;}\ - } while (0) - -/* Allocates a square pointer of array of arrays onto heap */ -#define CALLOC2D(ptr, sz)\ - do {\ - int i;\ - ptr = calloc(sz, sizeof(*ptr));\ - for (i = 0; i < sz; i++)\ - ptr[i] = calloc(sz, sizeof(*ptr));\ - } while (0) - -/* Frees a square pointer of arrays to arrays */ -#define FREE2D(ptr, sz)\ - do {\ - int i;\ - for (i = 0; i < sz; i++)\ - free(ptr[i]);\ - free(ptr);\ - } while (0) - -/* Define a sequence that should be executed each turn */ -#define TURN(x)\ - gravitate(x) +\ - merge(x) +\ - gravitate(x) - -/* direction enumeration */ -enum {DR, DU, DL, DD}; - -/* grid pointer */ -int **g; - -/* grid size */ -int SZ; - -/* score, and last turn score */ -int s; -int sl; - -/* highscore */ -int hs; - -/* highscore file */ -char *file; +int **g; /* grid pointer */ +int SZ; /* grid size */ +int s; /* Current score */ +int sl; /* Score for last turn */ +int hs; /* Highscore */ /* Merges adjacent squares of the same value together in a certain direction */ int merge(int d) { int moved = 0; - if (d == DL) { + if (d == DIR_LEFT) { int i, j; for (i = 0; i < SZ; i++) { for (j = 0; j < SZ; j++) { @@ -74,7 +33,7 @@ int merge(int d) } } } - else if (d == DU) { + else if (d == DIR_UP) { int i, j; for (i = 0; i < SZ; i++) { for (j = 0; j < SZ; j++) { @@ -88,7 +47,7 @@ int merge(int d) } } } - else if (d == DR) { + else if (d == DIR_RIGHT) { int i, j; for (i = SZ - 1; i >= 0; i--) { for (j = SZ - 1; j >= 0; j--) { @@ -102,7 +61,7 @@ int merge(int d) } } } - else if (d == DD) { + else if (d == DIR_DOWN) { int i, j; for (i = SZ - 1; i >= 0; i--) { for (j = SZ - 1; j >= 0; j--) { @@ -125,7 +84,7 @@ int merge(int d) int gravitate(int d) { int moved = 0; - if (d == DL) { + if (d == DIR_LEFT) { int i, j; for (i = 0; i < SZ; i++) { for (j = 0; j < SZ - 1; j++) { @@ -140,7 +99,7 @@ int gravitate(int d) } } } - else if (d == DU) { + else if (d == DIR_UP) { int i, j; for (i = 0; i < SZ; i++) { for (j = 0; j < SZ - 1; j++) { @@ -155,7 +114,7 @@ int gravitate(int d) } } } - else if (d == DR) { + else if (d == DIR_RIGHT) { int i, j; for (i = 0; i < SZ; i++) { for (j = SZ - 1; j > 0; j--) { @@ -170,7 +129,7 @@ int gravitate(int d) } } } - else if (d == DD) { + else if (d == DIR_DOWN) { int i, j; for (i = 0; i < SZ; i++) { for (j = SZ - 1; j > 0; j--) { @@ -190,8 +149,8 @@ int gravitate(int d) /* loads hiscore */ void load_score() { - FILE *fd = fopen(file, "r"); - if (fd == NULL) fd = fopen(file, "w+"); + FILE *fd = fopen(HISCORE_FILE, "r"); + if (fd == NULL) fd = fopen(HISCORE_FILE, "w+"); if (fscanf(fd, "%d", &hs) == EOF) hs = 0; fclose(fd); } @@ -200,7 +159,7 @@ void load_score() { void save_score() { if (s > hs && SZ == 4) { hs = s; - FILE *fd = fopen(file, "w+"); + FILE *fd = fopen(HISCORE_FILE, "w+"); fprintf(fd, "%d", hs); fclose(fd); } @@ -265,7 +224,6 @@ int main(int argc, char **argv) { /* init variables */ - file = ".hs2048g"; hs = 0; s = 0; sl = 0; @@ -295,21 +253,12 @@ int main(int argc, char **argv) printf("Are you sure you want to reset your highscores? (Y)es or (N)o\n"); int response; if ((response = getchar()) == 'y' || response == 'Y') { - FILE *fd = fopen(file, "w+"); + FILE *fd = fopen(HISCORE_FILE, "w+"); fclose(fd); } exit(EXIT_SUCCESS); case 'h': - printf("Controls:\n" - " hjkl, wasd Movement\n" - " q Quit\n" - "\n" - "Usage:\n" - " 2048 [options]\n" - "\n" - "Options:\n" - " -s Set the grid border length\n" - " -b Set the block spawn rate\n"); + printf(USAGE_STR); exit(EXIT_SUCCESS); } } @@ -341,19 +290,19 @@ int main(int argc, char **argv) switch (key) { case 'h': case 'a': - moved = TURN(DL); + moved = TURN(DIR_LEFT); break; case 'l': case 'd': - moved = TURN(DR); + moved = TURN(DIR_RIGHT); break; case 'j': case 's': - moved = TURN(DD); + moved = TURN(DIR_DOWN); break; case 'k': case 'w': - moved = TURN(DU); + moved = TURN(DIR_UP); break; case 'q': save_score();