diff --git a/Makefile b/Makefile index 9ebd6c2..0f29d8c 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,13 @@ OBJ = $(patsubst src/%.c,%.o,$(wildcard src/*.c)) HEADERS = $(wildcard src/*.h) -FLAGS = -std=c17 -pthread -O2 -LIB = -lncurses -lnotcurses-core +CFLAGS += -std=c17 -pthread -O2 +LIB = -lncurses -lnotcurses-core -lm %.o: src/%.c $(HEADERS) - $(CC) $(FLAGS) $(CFLAGS) -c $< -o $@ + $(CC) $(CFLAGS) -c $< -o $@ 2048: $(OBJ) - $(CC) $(LIB) $(FLAGS) $(CFLAGS) $^ -o $@ + $(CC) $(LIB) $(CFLAGS) $^ -o $@ .PHONY: clean diff --git a/src/2048.c b/src/2048.c index a0b6985..9d0950c 100644 --- a/src/2048.c +++ b/src/2048.c @@ -40,15 +40,15 @@ void _2048_draw_cell(struct _2048_cell *cell, struct ncplane *plane, uint y, uin // Draw cell in appropriate place by // manipulating the starting x draw points switch (cell->x) { - case 0: + case 0: // -24 drawx -= 12; - case 1: + case 1: // -12 drawx -= 12; break; - case 3: + case 3: // +12 drawx += 12; - case 2: + case 2: // 0 drawx += 0; break; @@ -58,36 +58,40 @@ void _2048_draw_cell(struct _2048_cell *cell, struct ncplane *plane, uint y, uin // Same for y switch (cell->y) { - case 0: + case 0: // -8 drawy -= 6; - case 1: + case 1: // -2 drawy -= 2; break; - case 3: + case 3: // +10 drawy += 6; - case 2: + case 2: // +4 drawy += 4; break; - default: break; + default: + break; } // Put cursor at starting draw position ncplane_cursor_move_yx(plane, drawy, drawx); + // Top + Bottom sides for (uint xd = 1; xd < 12u; xd++) { ncplane_putwc_yx(plane, drawy, drawx + xd, 0x2500); ncplane_putwc_yx(plane, drawy + 6, drawx + xd, 0x2500); } + // Left + Right sides for (uint yd = 1; yd < 6u; yd++) { ncplane_putwc_yx(plane, drawy + yd, drawx, 0x2502); ncplane_putwc_yx(plane, drawy + yd, drawx + 12, 0x2502); } + // Put level inside square if (cell->level) { - struct string *level = pad_int(1 << cell->level, 0, 10); + struct string *level = int_string(1 << cell->level); ncplane_putstr_yx(plane, drawy + 3, drawx + 6 - (level->length / 2), level->s); free_string(level); } @@ -136,14 +140,14 @@ void _2048_draw_board(struct _2048_board *board, struct ncplane *plane, uint py, } void _2048_place_random(struct _2048_board *board) { - bool emptySpotAvailable = false; + bool empty_spot_somewhere = false; for (uint y = 0; y < 4; y++) for (uint x = 0; x < 4; x++) if (!board->cells[y][x]->level) - emptySpotAvailable = true; + empty_spot_somewhere = true; - if (emptySpotAvailable) { + if (empty_spot_somewhere) { uint x = rand() % 4, y = rand() % 4; while (board->cells[y][x]->level) { @@ -151,11 +155,12 @@ void _2048_place_random(struct _2048_board *board) { y = rand() % 4; } + // 25% chance to be a 4, 75% chance to be a 2 board->cells[y][x]->level = (rand() % 4 == 3 ? 2 : 1); } } -void transpose(struct _2048_board *board) { +void _2048_transpose(struct _2048_board *board) { uint temp_board[4][4]; for (uint y = 0; y < 4; y++) @@ -167,7 +172,7 @@ void transpose(struct _2048_board *board) { board->cells[x][y]->level = temp_board[y][x]; } -void reverse(struct _2048_board *board) { +void _2048_reverse(struct _2048_board *board) { uint temp_board[4][4]; for (uint y = 0; y < 4; y++) @@ -221,30 +226,26 @@ bool _2048_merge(struct _2048_board *board) { return boardChanged; } -bool _2048_move(struct _2048_board *board, enum _2048_direction dir) { +void _2048_move(struct _2048_board *board, enum _2048_direction dir) { switch (dir) { case _2048_left: break; - case _2048_right: reverse(board); break; - case _2048_up: transpose(board); break; - case _2048_down: transpose(board); reverse(board); break; + case _2048_right: _2048_reverse(board); break; + case _2048_up: _2048_transpose(board); break; + case _2048_down: _2048_transpose(board); _2048_reverse(board); break; } - bool changed = false; - changed |= _2048_compress(board); - changed |= _2048_merge(board); - changed |= _2048_compress(board); + _2048_compress(board); + _2048_merge(board); + _2048_compress(board); switch(dir) { case _2048_left: break; - case _2048_right: reverse(board); break; - case _2048_up: transpose(board); break; - case _2048_down: reverse(board); transpose(board); break; + case _2048_right: _2048_reverse(board); break; + case _2048_up: _2048_transpose(board); break; + case _2048_down: _2048_reverse(board); _2048_transpose(board); break; } _2048_place_random(board); - - // facepalm. we add a new tile regardless if it changed so we always need to redraw the board - return true; } enum _2048_gameState _2048_determineState(struct _2048_board *board) { @@ -274,6 +275,6 @@ enum _2048_gameState _2048_determineState(struct _2048_board *board) { return _2048_normal; - // If all else, you lose. + // If all else fails, you lose. return _2048_cant_move; } diff --git a/src/2048.h b/src/2048.h index 18ab854..98c4ec8 100644 --- a/src/2048.h +++ b/src/2048.h @@ -10,7 +10,7 @@ typedef unsigned int uint; struct _2048_cell { - // posiiton + // position uint y; uint x; @@ -21,7 +21,7 @@ struct _2048_cell { struct _2048_board { struct _2048_cell *cells[4][4]; - // 1 = running; 0 = not running; + // "is running?" bool state; }; @@ -49,7 +49,7 @@ void _2048_place_random(struct _2048_board *board); bool _2048_compress(struct _2048_board *board); bool _2048_merge(struct _2048_board *board); -bool _2048_move(struct _2048_board *board, enum _2048_direction dir); +void _2048_move(struct _2048_board *board, enum _2048_direction dir); enum _2048_gameState _2048_determineState(struct _2048_board *board); diff --git a/src/main.c b/src/main.c index 5b27b25..60db33b 100644 --- a/src/main.c +++ b/src/main.c @@ -14,16 +14,14 @@ struct _2048_board *g_board; struct notcurses *g_nc; -struct ncplane *g_stdp; struct ncplane *p_game; struct ncplane *p_status; -uint g_topscore; +uint g_score; #define g_stop_nc() notcurses_stop(g_nc); g_nc == NULL int main(void) { setlocale(LC_ALL, "en_US.UTF-8"); - srand(time(NULL)); { // Create notcurses instance @@ -31,63 +29,45 @@ int main(void) { opt->flags = NCOPTION_SUPPRESS_BANNERS | NCOPTION_NO_QUIT_SIGHANDLERS; g_nc = notcurses_core_init(opt, NULL); free(opt); + + nocbreak(); + noecho(); } g_board = _2048_init_board(); - g_stdp = notcurses_stdplane(g_nc); - g_topscore = 0; + g_score = 0; - nocbreak(); - noecho(); - - { // Create game plane - allocm(struct ncplane_options, opt); - opt->name = (char *)"Game window"; - opt->cols = COLS; - opt->rows = LINES - 2; - opt->x = 0; - opt->y = 2; - opt->flags = NCPLANE_OPTION_FIXED; - opt->margin_b = 0; - opt->margin_r = 0; - p_game = ncplane_create(g_stdp, opt); - free(opt); + // Create planes + { + // Game plane + p_game = create_plane(g_nc, (char *)"Game window", COLS, LINES - 2, 2, 0, NCPLANE_OPTION_FIXED); if (p_game == 0) { - g_stop_nc(); printf("Game plane failed to init\n"); cleanup(1); } - } - { // Create statusbar plane - allocm(struct ncplane_options, opt); - opt->name = (char *)"Status bar"; - opt->cols = COLS; - opt->rows = 2; - opt->x = 0; - opt->y = 0; - opt->flags = NCPLANE_OPTION_FIXED; - opt->margin_b = 0; - opt->margin_r = 0; - p_status = ncplane_create(g_stdp, opt); - free(opt); + // Status bar plane + p_status = create_plane(g_nc, (char *)"Status bar", COLS, 2, 0, 0, NCPLANE_OPTION_FIXED); if (p_status == 0) { - g_stop_nc(); printf("Status bar plane failed to init\n"); cleanup(1); } + // Line for (uint x = 0; x < COLS + 0U; x++) ncplane_putwc_yx(p_status, 1, x, 0x2500); } + // Make sure everything is cleaned up when program is quit - signal(SIGINT, cleanup); - signal(SIGQUIT, cleanup); - signal(SIGTERM, cleanup); - signal(SIGSEGV, cleanup); + { + signal(SIGINT, cleanup); + signal(SIGQUIT, cleanup); + signal(SIGTERM, cleanup); + signal(SIGSEGV, cleanup); + } // Status bar text char *status_text = (char *)malloc(65); @@ -95,27 +75,25 @@ int main(void) { snprintf(status_text, 64, "Press h for help"); // plane dimensions - uint psx, psy, pgx, pgy; + uint psy, psx, pgy, pgx; // ps = Status plane // pg = Game plane ncplane_dim_yx(p_status, &psy, &psx); ncplane_dim_yx(p_game, &pgy, &pgx); _2048_place_random(g_board); - bool f_redrawGameBoard = true; - bool f_redrawStatus = true; + bool f_redraw_game_board = true; + bool f_redraw_status = true; bool f_seen2048 = false; - bool f_lost = false; + bool f_lost = false; // "Game over", used to stop handling movements // Game and event loop while (g_board->state) { - // Event processing ncinput nc_event; uint32_t nc_input; if ((nc_input = notcurses_get_nblock(g_nc, &nc_event))) { if (nc_input == (uint32_t)-1) { - g_stop_nc(); printf("[FATAL] Error occured while processing input\n"); cleanup(1); } @@ -136,18 +114,19 @@ int main(void) { f_seen2048 = false; f_lost = false; - f_redrawStatus = true; - f_redrawGameBoard = true; + f_redraw_status = true; + f_redraw_game_board = true; } - if (nc_event.id == 'h') snprintf(status_text, 64, "shift+q to Quit, shift+r to Restart"), f_redrawStatus = true; + if (nc_event.id == 'h') snprintf(status_text, 64, "shift+q to Quit, shift+r to Restart"), f_redraw_status = true; } if (!f_lost) { // Game movements - if (nc_event.id == NCKEY_LEFT || nc_event.id == 'a') f_redrawGameBoard = _2048_move(g_board, _2048_left); - if (nc_event.id == NCKEY_RIGHT || nc_event.id == 'd') f_redrawGameBoard = _2048_move(g_board, _2048_right); - if (nc_event.id == NCKEY_UP || nc_event.id == 'w') f_redrawGameBoard = _2048_move(g_board, _2048_up); - if (nc_event.id == NCKEY_DOWN || nc_event.id == 's') f_redrawGameBoard = _2048_move(g_board, _2048_down); + if (nc_event.id == NCKEY_LEFT || nc_event.id == 'a') _2048_move(g_board, _2048_left); + if (nc_event.id == NCKEY_RIGHT || nc_event.id == 'd') _2048_move(g_board, _2048_right); + if (nc_event.id == NCKEY_UP || nc_event.id == 'w') _2048_move(g_board, _2048_up); + if (nc_event.id == NCKEY_DOWN || nc_event.id == 's') _2048_move(g_board, _2048_down); + f_redraw_game_board = true; } } @@ -172,19 +151,19 @@ int main(void) { ncplane_putwc_yx(p_status, 1, x, 0x2500); } - f_redrawGameBoard = true; + f_redraw_game_board = true; } } - if (f_redrawGameBoard) { + if (f_redraw_game_board) { ncplane_erase(p_game); _2048_draw_board(g_board, p_game, pgy, pgx); - g_topscore = 0; + g_score = 0; for (uint y = 0; y < 4; y++) for (uint x = 0; x < 4; x++) if (g_board->cells[y][x]->level) - g_topscore += 1 << g_board->cells[y][x]->level; + g_score += 1 << g_board->cells[y][x]->level; enum _2048_gameState state = _2048_determineState(g_board); @@ -192,35 +171,35 @@ int main(void) { case _2048_normal: break; case _2048_reached: if (!f_seen2048) { - snprintf(status_text, 64, "Congrats! You reached 2048"); + snprintf(status_text, 64, "You reached 2048!"); f_seen2048 = true; + f_redraw_status = true; } break; case _2048_cant_move: if (!f_lost) { if (f_seen2048) - snprintf(status_text, 64, "Game over. Congrats on reaching 2048. Press shift+r to Restart"); + snprintf(status_text, 64, "You reached 2048! Press shift+r to Restart"); else - snprintf(status_text, 64, "Game over. You have lost. Press shift+r to Restart"); - + snprintf(status_text, 64, "You have lost. Press shift+r to Restart"); f_lost = true; + f_redraw_status = true; } - + break; } - f_redrawStatus = true; - f_redrawGameBoard = false; + f_redraw_game_board = false; } - if (f_redrawStatus) { + if (f_redraw_status) { ncplane_erase_region(p_status, 0, 0, 1, psx); ncplane_putstr_yx(p_status, 0, 1, "2048 in the terminal"); - ncplane_putstr_yx(p_status, 0, (psx / 2) - (strnlen(status_text, 64) / 2), status_text); + ncplane_putstr_yx(p_status, 0, (psx / 2) - (strlen(status_text) / 2), status_text); ncplane_putstr_yx(p_status, 0, psx - 14, "SCORE "); - ncplane_printf(p_status, "%i", g_topscore); + ncplane_printf(p_status, "%i", g_score); - f_redrawStatus = false; + f_redraw_status = false; } notcurses_render(g_nc); @@ -236,8 +215,11 @@ void cleanup(int sig) { printf("[INFO] Caught signal %i\n", sig); else if (sig == 1) printf("[INFO] Cleaning up after error\n"); + else + printf("[INFO] Exiting."); - g_stop_nc(); + if (g_nc != NULL) + g_stop_nc(); if (g_board) _2048_destroy_board(g_board); diff --git a/src/utils.c b/src/utils.c index acecde1..95def43 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,44 +1,15 @@ #include "utils.h" +#include #include -struct string *pad_int(int n, uint pad, uint maxsize) { - struct string *str = (struct string *)malloc(sizeof (struct string) + 1); - maxsize++; - - str->s = (char *)malloc((sizeof(char) * maxsize) + 1); - str->length = 0; - memset(str->s, 0, maxsize); +// int_string +struct string *int_string(uint n) { + uint l = log10(n) + 1; // length + struct string *str = alloc_string(l); - // Figure out how many place values are in n - int _n = abs(n); - uint places = 1; - - for (uint i = 0; i < pad; i++) { - if (_n >= 10) { - _n /= 10; - places++; - } - else break; - } - - char *padding = (char *)malloc((sizeof(char) * pad) + 1); - for (uint i = 0; i < pad; i++) - padding[i] = '0'; - - uint required_padding = pad - places; - if (required_padding > pad) required_padding = 0; - - snprintf(str->s, maxsize, "%*.*s%i%c", 0, required_padding, padding, abs(n), 0); - free(padding); - - for (uint i = 0; i < maxsize + 1; i++) { - if (str->s[i] == 0) { - str->length = i; - break; - } - } + snprintf(str->s, l, "%i", n); return str; } @@ -60,8 +31,24 @@ void free_string(struct string *s) { free(s); } -uint load_highscore() { - char *home = getenv("HOME"); - allocm(struct stat, s); - stat("", s); +// create_plane +// Programmer beware: this can and will return an erronous plane -- up to you to error handle +struct ncplane *create_plane(struct notcurses *nc, char *name, uint cols, uint rows, uint y, uint x, uint flags) { + struct ncplane *stdp = notcurses_stdplane(nc); + + allocm(struct ncplane_options, opt); + opt->name = name; + opt->cols = cols; + opt->rows = rows; + opt->y = y; + opt->x = x; + opt->flags = flags; + opt->margin_b = 0; + opt->margin_r = 0; + + struct ncplane *plane = ncplane_create(stdp, opt); + + free(opt); + + return plane; } diff --git a/src/utils.h b/src/utils.h index 5b4a044..d042c11 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,9 +1,11 @@ #ifndef __UTILS_H__ #define __UTILS_H__ +#include + +#include #include #include -#include #include @@ -16,10 +18,9 @@ struct string { uint length; }; -struct string *pad_int(int n, uint pad, uint maxsize); +struct string *int_string(uint n); struct string *alloc_string(uint l); void free_string(struct string *s); - -uint load_highscore(); +struct ncplane *create_plane(struct notcurses *nc, char *name, uint cols, uint rows, uint y, uint x, uint flags); #endif