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);
|
|
|
|
}
|