2048/src/main.c

229 lines
5.8 KiB
C
Raw Normal View History

2022-07-27 01:16:32 +05:30
#include "main.h"
#include "2048.h"
#include "utils.h"
#include <curses.h>
#include <notcurses/nckeys.h>
#include <notcurses/notcurses.h>
#include <locale.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct _2048_board *g_board;
struct notcurses *g_nc;
struct ncplane *p_game;
struct ncplane *p_status;
2023-10-19 04:12:43 +05:30
uint g_score;
2022-07-27 01:16:32 +05:30
#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
allocm(struct notcurses_options, opt);
opt->flags = NCOPTION_SUPPRESS_BANNERS | NCOPTION_NO_QUIT_SIGHANDLERS;
g_nc = notcurses_core_init(opt, NULL);
free(opt);
2023-10-19 04:12:43 +05:30
nocbreak();
noecho();
2022-07-27 01:16:32 +05:30
}
g_board = _2048_init_board();
2023-10-19 04:12:43 +05:30
g_score = 0;
// Create planes
{
// Game plane
p_game = create_plane(g_nc, (char *)"Game window", COLS, LINES - 2, 2, 0, NCPLANE_OPTION_FIXED);
2022-07-27 01:16:32 +05:30
if (p_game == 0) {
printf("Game plane failed to init\n");
cleanup(1);
}
2023-10-19 04:12:43 +05:30
// Status bar plane
p_status = create_plane(g_nc, (char *)"Status bar", COLS, 2, 0, 0, NCPLANE_OPTION_FIXED);
2022-07-27 01:16:32 +05:30
if (p_status == 0) {
printf("Status bar plane failed to init\n");
cleanup(1);
}
2023-10-19 04:12:43 +05:30
// Line
2022-07-27 01:16:32 +05:30
for (uint x = 0; x < COLS + 0U; x++)
ncplane_putwc_yx(p_status, 1, x, 0x2500);
}
2023-10-19 04:12:43 +05:30
2022-07-27 01:16:32 +05:30
// Make sure everything is cleaned up when program is quit
2023-10-19 04:12:43 +05:30
{
signal(SIGINT, cleanup);
signal(SIGQUIT, cleanup);
signal(SIGTERM, cleanup);
signal(SIGSEGV, cleanup);
}
2022-07-27 01:16:32 +05:30
// Status bar text
char *status_text = (char *)malloc(65);
memset(status_text, 0, 64);
snprintf(status_text, 64, "Press h for help");
// plane dimensions
2023-10-19 04:12:43 +05:30
uint psy, psx, pgy, pgx; // ps = Status plane // pg = Game plane
2022-07-27 01:16:32 +05:30
ncplane_dim_yx(p_status, &psy, &psx);
ncplane_dim_yx(p_game, &pgy, &pgx);
_2048_place_random(g_board);
2023-10-19 04:12:43 +05:30
bool f_redraw_game_board = true;
bool f_redraw_status = true;
2022-07-27 01:16:32 +05:30
bool f_seen2048 = false;
2023-10-19 04:12:43 +05:30
bool f_lost = false; // "Game over", used to stop handling movements
2022-07-27 01:16:32 +05:30
// 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) {
printf("[FATAL] Error occured while processing input\n");
cleanup(1);
}
if (nc_event.evtype == NCTYPE_PRESS) {
// Handle shortcuts
{
if (nc_event.id == 'Q')
g_board->state = 0;
if (nc_event.id == 'R') {
_2048_destroy_board(g_board);
g_board = _2048_init_board();
_2048_place_random(g_board);
snprintf(status_text, 64, "Board cleared");
f_seen2048 = false;
f_lost = false;
2023-10-19 04:12:43 +05:30
f_redraw_status = true;
f_redraw_game_board = true;
2022-07-27 01:16:32 +05:30
}
2023-10-19 04:12:43 +05:30
if (nc_event.id == 'h') snprintf(status_text, 64, "shift+q to Quit, shift+r to Restart"), f_redraw_status = true;
2022-07-27 01:16:32 +05:30
}
if (!f_lost) { // Game movements
2023-10-19 04:12:43 +05:30
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;
2022-07-27 01:16:32 +05:30
}
}
// Handle terminal resize
if (nc_event.id == NCKEY_RESIZE) {
uint nx, ny;
notcurses_refresh(g_nc, &ny, &nx);
{ // Resize planes
ncplane_resize_simple(p_status, 2, nx);
ncplane_resize_simple(p_game, ny - 2, nx);
}
{ // Update variables
ncplane_dim_yx(p_status, &psy, &psx);
ncplane_dim_yx(p_game, &pgy, &pgx);
}
{ // Recreate line seperator on status bar
for (uint x = 0; x < psx; x++)
ncplane_putwc_yx(p_status, 1, x, 0x2500);
}
2023-10-19 04:12:43 +05:30
f_redraw_game_board = true;
2022-07-27 01:16:32 +05:30
}
}
2023-10-19 04:12:43 +05:30
if (f_redraw_game_board) {
2022-07-27 01:16:32 +05:30
ncplane_erase(p_game);
_2048_draw_board(g_board, p_game, pgy, pgx);
2023-10-19 04:12:43 +05:30
g_score = 0;
2022-07-27 01:16:32 +05:30
for (uint y = 0; y < 4; y++)
for (uint x = 0; x < 4; x++)
if (g_board->cells[y][x]->level)
2023-10-19 04:12:43 +05:30
g_score += 1 << g_board->cells[y][x]->level;
2022-07-27 01:16:32 +05:30
enum _2048_gameState state = _2048_determineState(g_board);
switch (state) {
case _2048_normal: break;
case _2048_reached:
if (!f_seen2048) {
2023-10-19 04:12:43 +05:30
snprintf(status_text, 64, "You reached 2048!");
2022-07-27 01:16:32 +05:30
f_seen2048 = true;
2023-10-19 04:12:43 +05:30
f_redraw_status = true;
2022-07-27 01:16:32 +05:30
}
break;
case _2048_cant_move:
if (!f_lost) {
if (f_seen2048)
2023-10-19 04:12:43 +05:30
snprintf(status_text, 64, "You reached 2048! Press shift+r to Restart");
2022-07-27 01:16:32 +05:30
else
2023-10-19 04:12:43 +05:30
snprintf(status_text, 64, "You have lost. Press shift+r to Restart");
2022-07-27 01:16:32 +05:30
f_lost = true;
2023-10-19 04:12:43 +05:30
f_redraw_status = true;
2022-07-27 01:16:32 +05:30
}
2023-10-19 04:12:43 +05:30
break;
2022-07-27 01:16:32 +05:30
}
2023-10-19 04:12:43 +05:30
f_redraw_game_board = false;
2022-07-27 01:16:32 +05:30
}
2023-10-19 04:12:43 +05:30
if (f_redraw_status) {
2022-07-27 01:16:32 +05:30
ncplane_erase_region(p_status, 0, 0, 1, psx);
ncplane_putstr_yx(p_status, 0, 1, "2048 in the terminal");
2023-10-19 04:12:43 +05:30
ncplane_putstr_yx(p_status, 0, (psx / 2) - (strlen(status_text) / 2), status_text);
2022-07-27 01:16:32 +05:30
ncplane_putstr_yx(p_status, 0, psx - 14, "SCORE ");
2023-10-19 04:12:43 +05:30
ncplane_printf(p_status, "%i", g_score);
2022-07-27 01:16:32 +05:30
2023-10-19 04:12:43 +05:30
f_redraw_status = false;
2022-07-27 01:16:32 +05:30
}
notcurses_render(g_nc);
}
cleanup(0);
return EXIT_SUCCESS;
}
void cleanup(int sig) {
if (sig != 0 && sig != 1)
printf("[INFO] Caught signal %i\n", sig);
else if (sig == 1)
printf("[INFO] Cleaning up after error\n");
2023-10-19 04:12:43 +05:30
else
printf("[INFO] Exiting.");
2022-07-27 01:16:32 +05:30
2023-10-19 04:12:43 +05:30
if (g_nc != NULL)
g_stop_nc();
2022-07-27 01:16:32 +05:30
if (g_board)
_2048_destroy_board(g_board);
exit(sig);
}