#include "main.h" #include "2048.h" #include "utils.h" #include #include #include #include #include #include #include #include struct _2048_board *g_board; struct notcurses *g_nc; struct ncplane *p_game; struct ncplane *p_status; 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 allocm(struct notcurses_options, opt); 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_score = 0; // 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) { printf("Game plane failed to init\n"); cleanup(1); } // Status bar plane p_status = create_plane(g_nc, (char *)"Status bar", COLS, 2, 0, 0, NCPLANE_OPTION_FIXED); if (p_status == 0) { 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); } // Status bar text char *status_text = (char *)malloc(65); memset(status_text, 0, 64); snprintf(status_text, 64, "Press h for help"); // plane dimensions 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_redraw_game_board = true; bool f_redraw_status = true; bool f_seen2048 = 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) { 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; 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_redraw_status = true; } if (!f_lost) { // Game movements 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; } } // 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); } f_redraw_game_board = true; } } if (f_redraw_game_board) { ncplane_erase(p_game); _2048_draw_board(g_board, p_game, pgy, pgx); 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_score += 1 << g_board->cells[y][x]->level; enum _2048_gameState state = _2048_determineState(g_board); switch (state) { case _2048_normal: break; case _2048_reached: if (!f_seen2048) { 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, "You reached 2048! Press shift+r to Restart"); else snprintf(status_text, 64, "You have lost. Press shift+r to Restart"); f_lost = true; f_redraw_status = true; } break; } f_redraw_game_board = false; } 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) - (strlen(status_text) / 2), status_text); ncplane_putstr_yx(p_status, 0, psx - 14, "SCORE "); ncplane_printf(p_status, "%i", g_score); f_redraw_status = false; } 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"); else printf("[INFO] Exiting."); if (g_nc != NULL) g_stop_nc(); if (g_board) _2048_destroy_board(g_board); exit(sig); }