diff --git a/Makefile b/Makefile index c853a73..62ed8cd 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CC := clang -CFLAGS += -O2 -Wall -Wextra +CFLAGS += -g -Wall -Wextra LFLAGS += -DEFINES := -DVT100 +DEFINES := -DVT100 -D_REENTRANT -I/usr/include/SDL2 PROGRAM := 2048 C_FILES := $(wildcard src/*.c) @@ -15,6 +15,9 @@ curses: $(O_FILES) vt100: $(O_FILES) $(CC) $(filter-out obj/gfx%.o, $(O_FILES)) obj/gfx_terminal.o -o $(PROGRAM) +sdl: $(O_FILES) + $(CC) $(filter-out obj/gfx%.o, $(O_FILES)) obj/gfx_sdl.o -o $(PROGRAM) -lSDL2 -lSDL2_ttf + obj/%.o: src/%.c $(CC) $(DEFINES) $(CFLAGS) -c -o $@ $< diff --git a/README.md b/README.md index 71e3392..1621d4a 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,25 @@ #2048-cli -A cli version of the game [2048](https://github.com/gabrielecirulli/2048) for your Linux +A cli version/engine of the game [2048](https://github.com/gabrielecirulli/2048) for your Linux terminal. -#####2048_curses.c ![Screenshot](http://i.imgur.com/QU7t5mH.png) -#####2048_no_curses.c -![Screenshot](http://i.imgur.com/fwZEvdh.png) - -## Installation -This requires the ncurses library to link against during compilation. It is available -in most package managers. The program creates and uses a file name `.hs2048g` in the -working directory. Any file with this name will be modified and replaced. +There currently are 3 versions that can be run. These include a straight-forward terminal +based, and two using the ncurses and SDL libraries. To add a new graphical version, simply +create a .c file which implements all the functions in gfx.h and add a Makefile entry. ### Get git clone https://github.com/Tiehuis/2048-cli.git - make + + make vt100 +or + make curses +or + make sdl + ### Run - ./2048 + ./2048 ## Options -s Set the grid border length @@ -27,5 +28,7 @@ working directory. Any file with this name will be modified and replaced. -c Enables color support (ncurses version only) -C Disables color support (ncurses version only) +Fonts used in SDL version can be found [here](openfontlibrary.org). + ## License This code is licensed under the [MIT License](https://github.com/Tiehuis/2048-cli/blob/master/LICENSE). diff --git a/res/Anonymous Pro.ttf b/res/Anonymous Pro.ttf new file mode 100644 index 0000000..06aafc0 Binary files /dev/null and b/res/Anonymous Pro.ttf differ diff --git a/res/NotCourierSans.ttf b/res/NotCourierSans.ttf new file mode 100644 index 0000000..9fff7fd Binary files /dev/null and b/res/NotCourierSans.ttf differ diff --git a/src/engine.c b/src/engine.c index 9d4f715..ae2192d 100644 --- a/src/engine.c +++ b/src/engine.c @@ -270,7 +270,6 @@ int gamestate_tick(struct gfx_state *s, struct gamestate *g, int d, void (*callb { /* Reset move. Altered by gravitate and merge if we do move */ g->moved = 0; - printf("%d\n", d); gravitate(s, g, d, callback); merge(s, g, d, callback); gravitate(s, g, d, callback); diff --git a/src/gfx_sdl.c b/src/gfx_sdl.c new file mode 100644 index 0000000..c3b11cd --- /dev/null +++ b/src/gfx_sdl.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include "gfx.h" + +/* Side length of a 'pixel' in pixels */ +#define TTF_FONT_PATH "res/Anonymous Pro.ttf" +#define TTF_FONT_PT 32 + +#define iterate(n, expression)\ + do {\ + int i;\ + for (i = 0; i < n; ++i) { expression; }\ + } while (0) + +struct gfx_state { + SDL_Window *window; + SDL_Surface *surface; + TTF_Font *font; + int side_length; + int window_height; + int window_width; +}; + +struct gfx_state* gfx_init(struct gamestate *g) +{ + struct gfx_state *s = malloc(sizeof(struct gfx_state)); + + SDL_Init(SDL_INIT_VIDEO); + TTF_Init(); + s->font = TTF_OpenFont(TTF_FONT_PATH, TTF_FONT_PT); + + s->side_length = TTF_FontLineSkip(s->font); + s->window_height = g->opts->grid_height * (g->print_width + 2) + 3; + s->window_width = g->opts->grid_width * (g->print_width + 2) + 1; + s->window = SDL_CreateWindow( + "2048", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + + /* Spacing is inconsistent right now. Need to find width and + * height spacing to accurately resize window */ + s->window_width * TTF_FONT_PT * 0.7, + s->window_height * s->side_length * 0.35, + SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI + ); + + s->surface = SDL_GetWindowSurface(s->window); + + return s; +} + +#define rect_set(r, xv, yv, wv, hv)\ + do {\ + r.x = xv; r.y = yv; r.w = wv; r.h = hv;\ + } while (0) + + +void gfx_draw(struct gfx_state *s, struct gamestate *g) +{ + /* This shouldn't ever overflow. Max width effectively is determined by the size + * of two integers' text representation */ + const int buffer_length = 64; + char string_buffer[buffer_length]; + + /* Set up text object so we can write to sdl window */ + SDL_Surface *text; + SDL_Color text_color = {255, 255, 255, 0}; + SDL_Rect rect; + + /* Clear screen */ + SDL_FillRect(s->surface, NULL, SDL_MapRGB(s->surface->format, 0, 0, 0)); + + if (g->score_last) + snprintf(string_buffer, buffer_length, "Score: %ld (+%ld)", g->score, g->score_last); + else + snprintf(string_buffer, buffer_length, "Score: %ld", g->score); + + rect_set(rect, 0, 0, 0, 0); + text = TTF_RenderText_Solid(s->font, string_buffer, text_color); + SDL_BlitSurface(text, NULL, s->surface, &rect); + + snprintf(string_buffer, buffer_length, " Hi: %ld", g->score_high); + rect_set(rect, 0, s->side_length * 1, 0, 0); + text = TTF_RenderText_Solid(s->font, string_buffer, text_color); + SDL_BlitSurface(text, NULL, s->surface, &rect); + + int i; + for (i = 0; i < g->opts->grid_width * (g->print_width + 2) + 1; ++i) + string_buffer[i] = '-'; + string_buffer[i] = '\0'; + + rect_set(rect, 0, s->side_length * 2, 0, 0); + text = TTF_RenderText_Solid(s->font, string_buffer, text_color); + SDL_BlitSurface(text, NULL, s->surface, &rect); + + int x, y; + char line_buffer[buffer_length]; + for (y = 0; y < g->opts->grid_height; ++y) { + int line_index = 0; + line_buffer[line_index++] = '|'; + + for (x = 0; x < g->opts->grid_width; ++x) { + if (g->grid[x][y]) { + snprintf(string_buffer, buffer_length, "%*ld |", g->print_width, g->grid[x][y]); + strncpy(line_buffer + line_index, string_buffer, buffer_length - line_index); + line_index += strlen(string_buffer); + } + else { + snprintf(string_buffer, buffer_length, "%*s |", g->print_width, ""); + strncpy(line_buffer + line_index, string_buffer, buffer_length - line_index); + line_index += strlen(string_buffer); + } + } + + line_buffer[line_index] = 0; + rect_set(rect, 0, s->side_length * (y + 3), 0, 0); + text = TTF_RenderText_Solid(s->font, line_buffer, text_color); + SDL_BlitSurface(text, NULL, s->surface, &rect); + } + + for (i = 0; i < g->opts->grid_height * (g->print_width + 2) + 1; ++i) + string_buffer[i] = '-'; + string_buffer[i] = '\0'; + + rect_set(rect, 0, s->side_length * (y + 3), 0, 0); + text = TTF_RenderText_Solid(s->font, string_buffer, text_color); + SDL_BlitSurface(text, NULL, s->surface, &rect); + SDL_UpdateWindowSurface(s->window); +} + +/* This getch we parse here, and we just return when we get an appropriate + * event */ +int gfx_getch(struct gfx_state *s) +{ + (void)s; /* Supress unused warning */ + SDL_Event event; + + while (1) { + while (SDL_WaitEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + return 'q'; + case SDL_KEYDOWN: + switch (event.key.keysym.sym) { + case SDLK_q: + return 'q'; + case SDLK_UP: + case SDLK_w: + return 'w'; + case SDLK_DOWN: + case SDLK_s: + return 's'; + case SDLK_LEFT: + case SDLK_a: + return 'a'; + case SDLK_RIGHT: + case SDLK_d: + return 'd'; + default: + break; + } + break; + default: + break; + } + } + } +} + +void gfx_sleep(int ms) +{ + SDL_Delay(ms); +} + +void gfx_destroy(struct gfx_state *s) +{ + SDL_DestroyWindow(s->window); + TTF_Quit(); + SDL_Quit(); +}