Cleanup code

This commit is contained in:
0xf8 2023-10-18 18:42:43 -04:00
parent 4fce5388b8
commit cba0ea616b
Signed by: 0xf8
GPG Key ID: 446580D758689584
6 changed files with 119 additions and 148 deletions

View File

@ -1,13 +1,13 @@
OBJ = $(patsubst src/%.c,%.o,$(wildcard src/*.c)) OBJ = $(patsubst src/%.c,%.o,$(wildcard src/*.c))
HEADERS = $(wildcard src/*.h) HEADERS = $(wildcard src/*.h)
FLAGS = -std=c17 -pthread -O2 CFLAGS += -std=c17 -pthread -O2
LIB = -lncurses -lnotcurses-core LIB = -lncurses -lnotcurses-core -lm
%.o: src/%.c $(HEADERS) %.o: src/%.c $(HEADERS)
$(CC) $(FLAGS) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
2048: $(OBJ) 2048: $(OBJ)
$(CC) $(LIB) $(FLAGS) $(CFLAGS) $^ -o $@ $(CC) $(LIB) $(CFLAGS) $^ -o $@
.PHONY: clean .PHONY: clean

View File

@ -40,15 +40,15 @@ void _2048_draw_cell(struct _2048_cell *cell, struct ncplane *plane, uint y, uin
// Draw cell in appropriate place by // Draw cell in appropriate place by
// manipulating the starting x draw points // manipulating the starting x draw points
switch (cell->x) { switch (cell->x) {
case 0: case 0: // -24
drawx -= 12; drawx -= 12;
case 1: case 1: // -12
drawx -= 12; drawx -= 12;
break; break;
case 3: case 3: // +12
drawx += 12; drawx += 12;
case 2: case 2: // 0
drawx += 0; drawx += 0;
break; break;
@ -58,36 +58,40 @@ void _2048_draw_cell(struct _2048_cell *cell, struct ncplane *plane, uint y, uin
// Same for y // Same for y
switch (cell->y) { switch (cell->y) {
case 0: case 0: // -8
drawy -= 6; drawy -= 6;
case 1: case 1: // -2
drawy -= 2; drawy -= 2;
break; break;
case 3: case 3: // +10
drawy += 6; drawy += 6;
case 2: case 2: // +4
drawy += 4; drawy += 4;
break; break;
default: break; default:
break;
} }
// Put cursor at starting draw position // Put cursor at starting draw position
ncplane_cursor_move_yx(plane, drawy, drawx); ncplane_cursor_move_yx(plane, drawy, drawx);
// Top + Bottom sides
for (uint xd = 1; xd < 12u; xd++) { for (uint xd = 1; xd < 12u; xd++) {
ncplane_putwc_yx(plane, drawy, drawx + xd, 0x2500); ncplane_putwc_yx(plane, drawy, drawx + xd, 0x2500);
ncplane_putwc_yx(plane, drawy + 6, drawx + xd, 0x2500); ncplane_putwc_yx(plane, drawy + 6, drawx + xd, 0x2500);
} }
// Left + Right sides
for (uint yd = 1; yd < 6u; yd++) { for (uint yd = 1; yd < 6u; yd++) {
ncplane_putwc_yx(plane, drawy + yd, drawx, 0x2502); ncplane_putwc_yx(plane, drawy + yd, drawx, 0x2502);
ncplane_putwc_yx(plane, drawy + yd, drawx + 12, 0x2502); ncplane_putwc_yx(plane, drawy + yd, drawx + 12, 0x2502);
} }
// Put level inside square
if (cell->level) { 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); ncplane_putstr_yx(plane, drawy + 3, drawx + 6 - (level->length / 2), level->s);
free_string(level); 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) { 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 y = 0; y < 4; y++)
for (uint x = 0; x < 4; x++) for (uint x = 0; x < 4; x++)
if (!board->cells[y][x]->level) 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; uint x = rand() % 4, y = rand() % 4;
while (board->cells[y][x]->level) { while (board->cells[y][x]->level) {
@ -151,11 +155,12 @@ void _2048_place_random(struct _2048_board *board) {
y = rand() % 4; 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); 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]; uint temp_board[4][4];
for (uint y = 0; y < 4; y++) 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]; 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]; uint temp_board[4][4];
for (uint y = 0; y < 4; y++) for (uint y = 0; y < 4; y++)
@ -221,30 +226,26 @@ bool _2048_merge(struct _2048_board *board) {
return boardChanged; 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) { switch (dir) {
case _2048_left: break; case _2048_left: break;
case _2048_right: reverse(board); break; case _2048_right: _2048_reverse(board); break;
case _2048_up: transpose(board); break; case _2048_up: _2048_transpose(board); break;
case _2048_down: transpose(board); reverse(board); break; case _2048_down: _2048_transpose(board); _2048_reverse(board); break;
} }
bool changed = false; _2048_compress(board);
changed |= _2048_compress(board); _2048_merge(board);
changed |= _2048_merge(board); _2048_compress(board);
changed |= _2048_compress(board);
switch(dir) { switch(dir) {
case _2048_left: break; case _2048_left: break;
case _2048_right: reverse(board); break; case _2048_right: _2048_reverse(board); break;
case _2048_up: transpose(board); break; case _2048_up: _2048_transpose(board); break;
case _2048_down: reverse(board); transpose(board); break; case _2048_down: _2048_reverse(board); _2048_transpose(board); break;
} }
_2048_place_random(board); _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) { 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; return _2048_normal;
// If all else, you lose. // If all else fails, you lose.
return _2048_cant_move; return _2048_cant_move;
} }

View File

@ -10,7 +10,7 @@
typedef unsigned int uint; typedef unsigned int uint;
struct _2048_cell { struct _2048_cell {
// posiiton // position
uint y; uint y;
uint x; uint x;
@ -21,7 +21,7 @@ struct _2048_cell {
struct _2048_board { struct _2048_board {
struct _2048_cell *cells[4][4]; struct _2048_cell *cells[4][4];
// 1 = running; 0 = not running; // "is running?"
bool state; bool state;
}; };
@ -49,7 +49,7 @@ void _2048_place_random(struct _2048_board *board);
bool _2048_compress(struct _2048_board *board); bool _2048_compress(struct _2048_board *board);
bool _2048_merge(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); enum _2048_gameState _2048_determineState(struct _2048_board *board);

View File

@ -14,16 +14,14 @@
struct _2048_board *g_board; struct _2048_board *g_board;
struct notcurses *g_nc; struct notcurses *g_nc;
struct ncplane *g_stdp;
struct ncplane *p_game; struct ncplane *p_game;
struct ncplane *p_status; struct ncplane *p_status;
uint g_topscore; uint g_score;
#define g_stop_nc() notcurses_stop(g_nc); g_nc == NULL #define g_stop_nc() notcurses_stop(g_nc); g_nc == NULL
int main(void) { int main(void) {
setlocale(LC_ALL, "en_US.UTF-8"); setlocale(LC_ALL, "en_US.UTF-8");
srand(time(NULL)); srand(time(NULL));
{ // Create notcurses instance { // Create notcurses instance
@ -31,63 +29,45 @@ int main(void) {
opt->flags = NCOPTION_SUPPRESS_BANNERS | NCOPTION_NO_QUIT_SIGHANDLERS; opt->flags = NCOPTION_SUPPRESS_BANNERS | NCOPTION_NO_QUIT_SIGHANDLERS;
g_nc = notcurses_core_init(opt, NULL); g_nc = notcurses_core_init(opt, NULL);
free(opt); free(opt);
}
g_board = _2048_init_board();
g_stdp = notcurses_stdplane(g_nc);
g_topscore = 0;
nocbreak(); nocbreak();
noecho(); noecho();
}
{ // Create game plane g_board = _2048_init_board();
allocm(struct ncplane_options, opt); g_score = 0;
opt->name = (char *)"Game window";
opt->cols = COLS; // Create planes
opt->rows = LINES - 2; {
opt->x = 0; // Game plane
opt->y = 2; p_game = create_plane(g_nc, (char *)"Game window", COLS, LINES - 2, 2, 0, NCPLANE_OPTION_FIXED);
opt->flags = NCPLANE_OPTION_FIXED;
opt->margin_b = 0;
opt->margin_r = 0;
p_game = ncplane_create(g_stdp, opt);
free(opt);
if (p_game == 0) { if (p_game == 0) {
g_stop_nc();
printf("Game plane failed to init\n"); printf("Game plane failed to init\n");
cleanup(1); cleanup(1);
} }
}
{ // Create statusbar plane // Status bar plane
allocm(struct ncplane_options, opt); p_status = create_plane(g_nc, (char *)"Status bar", COLS, 2, 0, 0, NCPLANE_OPTION_FIXED);
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);
if (p_status == 0) { if (p_status == 0) {
g_stop_nc();
printf("Status bar plane failed to init\n"); printf("Status bar plane failed to init\n");
cleanup(1); cleanup(1);
} }
// Line
for (uint x = 0; x < COLS + 0U; x++) for (uint x = 0; x < COLS + 0U; x++)
ncplane_putwc_yx(p_status, 1, x, 0x2500); ncplane_putwc_yx(p_status, 1, x, 0x2500);
} }
// Make sure everything is cleaned up when program is quit // Make sure everything is cleaned up when program is quit
{
signal(SIGINT, cleanup); signal(SIGINT, cleanup);
signal(SIGQUIT, cleanup); signal(SIGQUIT, cleanup);
signal(SIGTERM, cleanup); signal(SIGTERM, cleanup);
signal(SIGSEGV, cleanup); signal(SIGSEGV, cleanup);
}
// Status bar text // Status bar text
char *status_text = (char *)malloc(65); char *status_text = (char *)malloc(65);
@ -95,27 +75,25 @@ int main(void) {
snprintf(status_text, 64, "Press h for help"); snprintf(status_text, 64, "Press h for help");
// plane dimensions // 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_status, &psy, &psx);
ncplane_dim_yx(p_game, &pgy, &pgx); ncplane_dim_yx(p_game, &pgy, &pgx);
_2048_place_random(g_board); _2048_place_random(g_board);
bool f_redrawGameBoard = true; bool f_redraw_game_board = true;
bool f_redrawStatus = true; bool f_redraw_status = true;
bool f_seen2048 = false; bool f_seen2048 = false;
bool f_lost = false; bool f_lost = false; // "Game over", used to stop handling movements
// Game and event loop // Game and event loop
while (g_board->state) { while (g_board->state) {
// Event processing // Event processing
ncinput nc_event; ncinput nc_event;
uint32_t nc_input; uint32_t nc_input;
if ((nc_input = notcurses_get_nblock(g_nc, &nc_event))) { if ((nc_input = notcurses_get_nblock(g_nc, &nc_event))) {
if (nc_input == (uint32_t)-1) { if (nc_input == (uint32_t)-1) {
g_stop_nc();
printf("[FATAL] Error occured while processing input\n"); printf("[FATAL] Error occured while processing input\n");
cleanup(1); cleanup(1);
} }
@ -136,18 +114,19 @@ int main(void) {
f_seen2048 = false; f_seen2048 = false;
f_lost = false; f_lost = false;
f_redrawStatus = true; f_redraw_status = true;
f_redrawGameBoard = 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 (!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_LEFT || nc_event.id == 'a') _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_RIGHT || nc_event.id == 'd') _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_UP || nc_event.id == 'w') _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_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); 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); ncplane_erase(p_game);
_2048_draw_board(g_board, p_game, pgy, pgx); _2048_draw_board(g_board, p_game, pgy, pgx);
g_topscore = 0; g_score = 0;
for (uint y = 0; y < 4; y++) for (uint y = 0; y < 4; y++)
for (uint x = 0; x < 4; x++) for (uint x = 0; x < 4; x++)
if (g_board->cells[y][x]->level) 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); enum _2048_gameState state = _2048_determineState(g_board);
@ -192,35 +171,35 @@ int main(void) {
case _2048_normal: break; case _2048_normal: break;
case _2048_reached: case _2048_reached:
if (!f_seen2048) { if (!f_seen2048) {
snprintf(status_text, 64, "Congrats! You reached 2048"); snprintf(status_text, 64, "You reached 2048!");
f_seen2048 = true; f_seen2048 = true;
f_redraw_status = true;
} }
break; break;
case _2048_cant_move: case _2048_cant_move:
if (!f_lost) { if (!f_lost) {
if (f_seen2048) 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 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_lost = true;
f_redraw_status = true;
}
break;
} }
f_redraw_game_board = false;
} }
f_redrawStatus = true; if (f_redraw_status) {
f_redrawGameBoard = false;
}
if (f_redrawStatus) {
ncplane_erase_region(p_status, 0, 0, 1, psx); 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, 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_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); notcurses_render(g_nc);
@ -236,7 +215,10 @@ void cleanup(int sig) {
printf("[INFO] Caught signal %i\n", sig); printf("[INFO] Caught signal %i\n", sig);
else if (sig == 1) else if (sig == 1)
printf("[INFO] Cleaning up after error\n"); printf("[INFO] Cleaning up after error\n");
else
printf("[INFO] Exiting.");
if (g_nc != NULL)
g_stop_nc(); g_stop_nc();
if (g_board) if (g_board)

View File

@ -1,44 +1,15 @@
#include "utils.h" #include "utils.h"
#include <math.h>
#include <sys/stat.h> #include <sys/stat.h>
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); // int_string
str->length = 0; struct string *int_string(uint n) {
uint l = log10(n) + 1; // length
struct string *str = alloc_string(l);
memset(str->s, 0, maxsize); snprintf(str->s, l, "%i", n);
// 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;
}
}
return str; return str;
} }
@ -60,8 +31,24 @@ void free_string(struct string *s) {
free(s); free(s);
} }
uint load_highscore() { // create_plane
char *home = getenv("HOME"); // Programmer beware: this can and will return an erronous plane -- up to you to error handle
allocm(struct stat, s); struct ncplane *create_plane(struct notcurses *nc, char *name, uint cols, uint rows, uint y, uint x, uint flags) {
stat("", s); 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;
} }

View File

@ -1,9 +1,11 @@
#ifndef __UTILS_H__ #ifndef __UTILS_H__
#define __UTILS_H__ #define __UTILS_H__
#include <notcurses/notcurses.h>
#include <memory.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <memory.h>
#include <string.h> #include <string.h>
@ -16,10 +18,9 @@ struct string {
uint length; uint length;
}; };
struct string *pad_int(int n, uint pad, uint maxsize); struct string *int_string(uint n);
struct string *alloc_string(uint l); struct string *alloc_string(uint l);
void free_string(struct string *s); void free_string(struct string *s);
struct ncplane *create_plane(struct notcurses *nc, char *name, uint cols, uint rows, uint y, uint x, uint flags);
uint load_highscore();
#endif #endif