Initial commit
This commit is contained in:
commit
ed73a543e1
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
2048
|
||||||
|
*.o
|
15
Makefile
Normal file
15
Makefile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
OBJ = $(patsubst src/%.c,%.o,$(wildcard src/*.c))
|
||||||
|
HEADERS = $(wildcard src/*.h)
|
||||||
|
FLAGS = -std=c17 -pthread -O2
|
||||||
|
LIB = -lncurses -lnotcurses-core
|
||||||
|
|
||||||
|
%.o: src/%.c $(HEADERS)
|
||||||
|
$(CC) $(FLAGS) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
2048: $(OBJ)
|
||||||
|
$(CC) $(LIB) $(FLAGS) $(CFLAGS) $^ -o $@
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
$(RM) $(OBJ)
|
279
src/2048.c
Normal file
279
src/2048.c
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
#include "2048.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <notcurses/notcurses.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct _2048_board *_2048_init_board() {
|
||||||
|
allocm(struct _2048_board, board);
|
||||||
|
|
||||||
|
board->state = true;
|
||||||
|
|
||||||
|
for (uint y = 0; y < 4; y++)
|
||||||
|
for (uint x = 0; x < 4; x++)
|
||||||
|
board->cells[y][x] = _2048_init_cell(y, x);
|
||||||
|
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _2048_cell *_2048_init_cell(uint y, uint x) {
|
||||||
|
allocm(struct _2048_cell, cell);
|
||||||
|
|
||||||
|
cell->x = x;
|
||||||
|
cell->y = y;
|
||||||
|
cell->level = 0;
|
||||||
|
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _2048_destroy_board(struct _2048_board *board) {
|
||||||
|
for (uint y = 0; y < 4; y++)
|
||||||
|
for (uint x = 0; x < 4; x++)
|
||||||
|
free(board->cells[y][x]);
|
||||||
|
|
||||||
|
free(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _2048_draw_cell(struct _2048_cell *cell, struct ncplane *plane, uint y, uint x) {
|
||||||
|
uint drawx = x / 2, drawy = y / 2;
|
||||||
|
|
||||||
|
// Draw cell in appropriate place by
|
||||||
|
// manipulating the starting x draw points
|
||||||
|
switch (cell->x) {
|
||||||
|
case 0:
|
||||||
|
drawx -= 12;
|
||||||
|
case 1:
|
||||||
|
drawx -= 12;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
drawx += 12;
|
||||||
|
case 2:
|
||||||
|
drawx += 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same for y
|
||||||
|
switch (cell->y) {
|
||||||
|
case 0:
|
||||||
|
drawy -= 6;
|
||||||
|
case 1:
|
||||||
|
drawy -= 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
drawy += 6;
|
||||||
|
case 2:
|
||||||
|
drawy += 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put cursor at starting draw position
|
||||||
|
ncplane_cursor_move_yx(plane, drawy, drawx);
|
||||||
|
|
||||||
|
for (uint xd = 1; xd < 12u; xd++) {
|
||||||
|
ncplane_putwc_yx(plane, drawy, drawx + xd, 0x2500);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 6, drawx + xd, 0x2500);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint yd = 1; yd < 6u; yd++) {
|
||||||
|
ncplane_putwc_yx(plane, drawy + yd, drawx, 0x2502);
|
||||||
|
ncplane_putwc_yx(plane, drawy + yd, drawx + 12, 0x2502);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell->level) {
|
||||||
|
struct string *level = pad_int(1 << cell->level, 0, 10);
|
||||||
|
ncplane_putstr_yx(plane, drawy + 3, drawx + 6 - (level->length / 2), level->s);
|
||||||
|
free_string(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _2048_draw_board(struct _2048_board *board, struct ncplane *plane, uint py, uint px) {
|
||||||
|
py -= 9;
|
||||||
|
for (uint y = 0; y < 4; y++)
|
||||||
|
for (uint x = 0; x < 4; x++)
|
||||||
|
_2048_draw_cell(board->cells[y][x], plane, py, px);
|
||||||
|
|
||||||
|
uint drawx = px / 2, drawy = py / 2;
|
||||||
|
|
||||||
|
// Draw intersections
|
||||||
|
ncplane_putwc_yx(plane, drawy - 8, drawx - 12, 0x252c);
|
||||||
|
ncplane_putwc_yx(plane, drawy - 8, drawx, 0x252c);
|
||||||
|
ncplane_putwc_yx(plane, drawy - 8, drawx + 12, 0x252c);
|
||||||
|
|
||||||
|
ncplane_putwc_yx(plane, drawy - 2, drawx - 24, 0x251c);
|
||||||
|
ncplane_putwc_yx(plane, drawy - 2, drawx - 12, 0x253c);
|
||||||
|
ncplane_putwc_yx(plane, drawy - 2, drawx, 0x253c);
|
||||||
|
ncplane_putwc_yx(plane, drawy - 2, drawx + 12, 0x253c);
|
||||||
|
ncplane_putwc_yx(plane, drawy - 2, drawx + 24, 0x2524);
|
||||||
|
|
||||||
|
ncplane_putwc_yx(plane, drawy + 4, drawx - 24, 0x251c);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 4, drawx - 12, 0x253c);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 4, drawx, 0x253c);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 4, drawx + 12, 0x253c);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 4, drawx + 24, 0x2524);
|
||||||
|
|
||||||
|
ncplane_putwc_yx(plane, drawy + 10, drawx - 24, 0x251c);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 10, drawx - 12, 0x253c);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 10, drawx, 0x253c);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 10, drawx + 12, 0x253c);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 10, drawx + 24, 0x2524);
|
||||||
|
|
||||||
|
ncplane_putwc_yx(plane, drawy + 16, drawx - 12, 0x2534);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 16, drawx, 0x2534);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 16, drawx + 12, 0x2534);
|
||||||
|
|
||||||
|
// Draw corners
|
||||||
|
ncplane_putwc_yx(plane, drawy - 8, drawx - 24, 0x250c);
|
||||||
|
ncplane_putwc_yx(plane, drawy - 8, drawx + 24, 0x2510);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 16, drawx - 24, 0x2514);
|
||||||
|
ncplane_putwc_yx(plane, drawy + 16, drawx + 24, 0x2518);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _2048_place_random(struct _2048_board *board) {
|
||||||
|
bool emptySpotAvailable = false;
|
||||||
|
|
||||||
|
for (uint y = 0; y < 4; y++)
|
||||||
|
for (uint x = 0; x < 4; x++)
|
||||||
|
if (!board->cells[y][x]->level)
|
||||||
|
emptySpotAvailable = true;
|
||||||
|
|
||||||
|
if (emptySpotAvailable) {
|
||||||
|
uint x = rand() % 4, y = rand() % 4;
|
||||||
|
|
||||||
|
while (board->cells[y][x]->level) {
|
||||||
|
x = rand() % 4;
|
||||||
|
y = rand() % 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
board->cells[y][x]->level = (rand() % 4 == 3 ? 2 : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void transpose(struct _2048_board *board) {
|
||||||
|
uint temp_board[4][4];
|
||||||
|
|
||||||
|
for (uint y = 0; y < 4; y++)
|
||||||
|
for (uint x = 0; x < 4; x++)
|
||||||
|
temp_board[y][x] = board->cells[y][x]->level;
|
||||||
|
|
||||||
|
for (uint y = 0; y < 4; y++)
|
||||||
|
for (uint x = 0; x < 4; x++)
|
||||||
|
board->cells[x][y]->level = temp_board[y][x];
|
||||||
|
}
|
||||||
|
|
||||||
|
void reverse(struct _2048_board *board) {
|
||||||
|
uint temp_board[4][4];
|
||||||
|
|
||||||
|
for (uint y = 0; y < 4; y++)
|
||||||
|
for (uint x = 0; x < 4; x++)
|
||||||
|
temp_board[y][x] = board->cells[y][x]->level;
|
||||||
|
|
||||||
|
for (uint y = 0; y < 4; y++)
|
||||||
|
for (uint x = 0; x < 4; x++)
|
||||||
|
board->cells[y][x]->level = temp_board[y][3 - x];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _2048_compress(struct _2048_board *board) {
|
||||||
|
bool boardChanged = false;
|
||||||
|
|
||||||
|
for (uint y = 0; y < 4; y++) {
|
||||||
|
uint p = 0;
|
||||||
|
uint cells[4] = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
for (uint x = 0; x < 4; x++) {
|
||||||
|
if (board->cells[y][x]->level) {
|
||||||
|
cells[p] = board->cells[y][x]->level;
|
||||||
|
|
||||||
|
if (x != p)
|
||||||
|
boardChanged = true;
|
||||||
|
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint x = 0; x < 4; x++)
|
||||||
|
board->cells[y][x]->level = cells[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
return boardChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _2048_merge(struct _2048_board *board) {
|
||||||
|
bool boardChanged = false;
|
||||||
|
|
||||||
|
for (uint y = 0; y < 4; y++) {
|
||||||
|
for (uint x = 0; x < 3; x++) {
|
||||||
|
if (board->cells[y][x]->level == board->cells[y][x + 1]->level && board->cells[y][x]->level) {
|
||||||
|
board->cells[y][x]->level++;
|
||||||
|
board->cells[y][x + 1]->level = 0;
|
||||||
|
|
||||||
|
boardChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return boardChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
changed |= _2048_compress(board);
|
||||||
|
changed |= _2048_merge(board);
|
||||||
|
changed |= _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;
|
||||||
|
}
|
||||||
|
|
||||||
|
_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) {
|
||||||
|
// GG, you got 2048
|
||||||
|
for (uint y = 0; y < 4; y++)
|
||||||
|
for (uint x = 0; x < 4; x++)
|
||||||
|
if (board->cells[y][x]->level > 10) // 2048
|
||||||
|
return _2048_reached;
|
||||||
|
|
||||||
|
// If there is any empty tile, its a normal game
|
||||||
|
for (uint y = 0; y < 4; y++)
|
||||||
|
for (uint x = 0; x < 4; x++)
|
||||||
|
if (!board->cells[y][x]->level)
|
||||||
|
return _2048_normal;
|
||||||
|
|
||||||
|
|
||||||
|
// If any move will create a new empty tile, its a normal game
|
||||||
|
for (uint y = 0; y < 3; y++)
|
||||||
|
for (uint x = 0; x < 3; x++)
|
||||||
|
if (board->cells[y][x]->level == board->cells[y + 1][x]->level ||
|
||||||
|
board->cells[y][x]->level == board->cells[y][x + 1]->level)
|
||||||
|
return _2048_normal;
|
||||||
|
|
||||||
|
for (uint i = 0; i < 3; i++)
|
||||||
|
if (board->cells[3][i]->level == board->cells[3][i + 1]->level ||
|
||||||
|
board->cells[i][3]->level == board->cells[i + 1][3]->level)
|
||||||
|
return _2048_normal;
|
||||||
|
|
||||||
|
|
||||||
|
// If all else, you lose.
|
||||||
|
return _2048_cant_move;
|
||||||
|
}
|
56
src/2048.h
Normal file
56
src/2048.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#ifndef __2048_H__
|
||||||
|
#define __2048_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <curses.h>
|
||||||
|
#include <notcurses/nckeys.h>
|
||||||
|
#include <notcurses/notcurses.h>
|
||||||
|
|
||||||
|
typedef unsigned int uint;
|
||||||
|
|
||||||
|
struct _2048_cell {
|
||||||
|
// posiiton
|
||||||
|
uint y;
|
||||||
|
uint x;
|
||||||
|
|
||||||
|
// 0 = empty
|
||||||
|
uint level;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _2048_board {
|
||||||
|
struct _2048_cell *cells[4][4];
|
||||||
|
|
||||||
|
// 1 = running; 0 = not running;
|
||||||
|
bool state;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum _2048_direction {
|
||||||
|
_2048_left,
|
||||||
|
_2048_right,
|
||||||
|
_2048_up,
|
||||||
|
_2048_down
|
||||||
|
};
|
||||||
|
|
||||||
|
enum _2048_gameState {
|
||||||
|
_2048_normal,
|
||||||
|
_2048_reached,
|
||||||
|
_2048_cant_move
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _2048_board *_2048_init_board();
|
||||||
|
struct _2048_cell *_2048_init_cell(uint y, uint x);
|
||||||
|
void _2048_destroy_board(struct _2048_board *board);
|
||||||
|
|
||||||
|
void _2048_draw_cell(struct _2048_cell *cell, struct ncplane *plane, uint y, uint x);
|
||||||
|
void _2048_draw_board(struct _2048_board *board, struct ncplane *plane, uint py, uint px);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
enum _2048_gameState _2048_determineState(struct _2048_board *board);
|
||||||
|
|
||||||
|
#endif
|
246
src/main.c
Normal file
246
src/main.c
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
#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 *g_stdp;
|
||||||
|
struct ncplane *p_game;
|
||||||
|
struct ncplane *p_status;
|
||||||
|
uint g_topscore;
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_board = _2048_init_board();
|
||||||
|
g_stdp = notcurses_stdplane(g_nc);
|
||||||
|
g_topscore = 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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (p_status == 0) {
|
||||||
|
g_stop_nc();
|
||||||
|
printf("Status bar plane failed to init\n");
|
||||||
|
cleanup(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 psx, psy, pgx, pgy;
|
||||||
|
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_seen2048 = false;
|
||||||
|
bool f_lost = false;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_redrawStatus = true;
|
||||||
|
f_redrawGameBoard = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nc_event.id == 'h') snprintf(status_text, 64, "shift+q to Quit, shift+r to Restart"), f_redrawStatus = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_redrawGameBoard = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f_redrawGameBoard) {
|
||||||
|
ncplane_erase(p_game);
|
||||||
|
_2048_draw_board(g_board, p_game, pgy, pgx);
|
||||||
|
|
||||||
|
g_topscore = 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;
|
||||||
|
|
||||||
|
enum _2048_gameState state = _2048_determineState(g_board);
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case _2048_normal: break;
|
||||||
|
case _2048_reached:
|
||||||
|
if (!f_seen2048) {
|
||||||
|
snprintf(status_text, 64, "Congrats! You reached 2048");
|
||||||
|
f_seen2048 = 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");
|
||||||
|
else
|
||||||
|
snprintf(status_text, 64, "Game over. You have lost. Press shift+r to Restart");
|
||||||
|
|
||||||
|
f_lost = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
f_redrawStatus = true;
|
||||||
|
f_redrawGameBoard = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f_redrawStatus) {
|
||||||
|
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 - 14, "SCORE ");
|
||||||
|
ncplane_printf(p_status, "%i", g_topscore);
|
||||||
|
|
||||||
|
f_redrawStatus = 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");
|
||||||
|
|
||||||
|
g_stop_nc();
|
||||||
|
|
||||||
|
if (g_board)
|
||||||
|
_2048_destroy_board(g_board);
|
||||||
|
|
||||||
|
exit(sig);
|
||||||
|
}
|
10
src/main.h
Normal file
10
src/main.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef __MAIN_H__
|
||||||
|
#define __MAIN_H__
|
||||||
|
|
||||||
|
#include <curses.h>
|
||||||
|
#include <notcurses/nckeys.h>
|
||||||
|
#include <notcurses/notcurses.h>
|
||||||
|
|
||||||
|
void cleanup(int sig);
|
||||||
|
|
||||||
|
#endif
|
67
src/utils.c
Normal file
67
src/utils.c
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include "utils.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);
|
||||||
|
str->length = 0;
|
||||||
|
|
||||||
|
memset(str->s, 0, maxsize);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// alloc_string
|
||||||
|
struct string *alloc_string(uint l) {
|
||||||
|
struct string *s = (struct string *)malloc(sizeof(struct string) + 1);
|
||||||
|
s->s = (char *)malloc(l + 1);
|
||||||
|
s->length = l;
|
||||||
|
|
||||||
|
memset(s->s, 0, l);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// free_string
|
||||||
|
void free_string(struct string *s) {
|
||||||
|
free(s->s);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint load_highscore() {
|
||||||
|
char *home = getenv("HOME");
|
||||||
|
allocm(struct stat, s);
|
||||||
|
stat("", s);
|
||||||
|
}
|
25
src/utils.h
Normal file
25
src/utils.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef __UTILS_H__
|
||||||
|
#define __UTILS_H__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef unsigned int uint;
|
||||||
|
|
||||||
|
#define allocm(type, name) type * name = (type *)malloc(sizeof(type) + 1); memset(name, 0, sizeof(type))
|
||||||
|
|
||||||
|
struct string {
|
||||||
|
char *s;
|
||||||
|
uint length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct string *pad_int(int n, uint pad, uint maxsize);
|
||||||
|
struct string *alloc_string(uint l);
|
||||||
|
void free_string(struct string *s);
|
||||||
|
|
||||||
|
uint load_highscore();
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user