From dc753ff2db5234dcfbac0eea4250e68d4455aef0 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Tue, 22 Oct 2019 12:56:52 -0400 Subject: greatly imporve memory usage of a game instance (5704 -> 788 bytes) --- frontends/curses/curses_ui.c | 31 ++++++------ frontends/gl/main.c | 8 ++-- lib/cetris.h | 110 +++++++++++++++++++++---------------------- lib/test.h | 6 ++- meson.build | 6 ++- meson_options.txt | 2 +- test/memory_test.c | 13 +++++ test/meson.build | 5 ++ 8 files changed, 99 insertions(+), 82 deletions(-) create mode 100644 test/memory_test.c create mode 100644 test/meson.build diff --git a/frontends/curses/curses_ui.c b/frontends/curses/curses_ui.c index 5776af3..94b6141 100644 --- a/frontends/curses/curses_ui.c +++ b/frontends/curses/curses_ui.c @@ -91,14 +91,13 @@ void curses_init() { #endif start_color(); - init_pair(COLOR_NONE, COLOR_BLACK, COLOR_BLACK); - init_pair(COLOR_O, COLOR_YELLOW, COLOR_BLACK); - init_pair(COLOR_Z, COLOR_RED, COLOR_BLACK); - init_pair(COLOR_S, COLOR_GREEN, COLOR_BLACK); - init_pair(COLOR_T, COLOR_MAGENTA, COLOR_BLACK); - init_pair(COLOR_L, COLOR_WHITE, COLOR_BLACK); // should be orange - init_pair(COLOR_I, COLOR_CYAN, COLOR_BLACK); - init_pair(COLOR_J, COLOR_BLUE, COLOR_BLACK); + init_pair(MINO_O, COLOR_YELLOW, COLOR_BLACK); + init_pair(MINO_Z, COLOR_RED, COLOR_BLACK); + init_pair(MINO_S, COLOR_GREEN, COLOR_BLACK); + init_pair(MINO_T, COLOR_MAGENTA, COLOR_BLACK); + init_pair(MINO_L, COLOR_WHITE, COLOR_BLACK); // should be orange + init_pair(MINO_I, COLOR_CYAN, COLOR_BLACK); + init_pair(MINO_J, COLOR_BLUE, COLOR_BLACK); clear(); } @@ -127,30 +126,30 @@ void draw_board() { for (int y = CETRIS_BOARD_VISABLE; y < CETRIS_BOARD_Y; y++) { int draw_y = y - CETRIS_BOARD_VISABLE + Y_OFFSET; int draw_x = x * 2 + X_OFFSET; - if (game.board[x][y].ghost) { + if (game.board[x][y] & SLOT_GHOST) { attron(A_DIM); mvaddstr(draw_y, draw_x, BLOCK); attroff(A_DIM); } - if (game.board[x][y].occupied) { - attron(COLOR_PAIR(game.board[x][y].c) | A_BOLD); - if (game.board[0][y].remove_tick) { + if (game.board[x][y] & SLOT_OCCUPIED) { + attron(COLOR_PAIR(game.board[x][y] >> 5) | A_BOLD); + if (game.line_remove_tick[y]) { if (game.tick % 2 == 0) { mvaddstr(draw_y, draw_x, BLOCK); } } else { mvaddstr(draw_y, draw_x, BLOCK); } - attroff(COLOR_PAIR(game.board[x][y].c) | A_BOLD); + attroff(COLOR_PAIR(game.board[x][y] >> 5) | A_BOLD); } } int index = game.current_index; - attron(COLOR_PAIR(game.piece_queue[index].c)); + attron(COLOR_PAIR(game.piece_queue[index].t)); for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { if (game.piece_queue[index].m[y][x]) { - if (game.piece_queue[index].t == I || game.piece_queue[index].t == O) { + if (game.piece_queue[index].t == MINO_I || game.piece_queue[index].t == MINO_O) { mvaddstr(6 + y, (x * 2) + 36, BLOCK); } else { mvaddstr(6 + y, (x * 2) + 37, BLOCK); @@ -158,7 +157,7 @@ void draw_board() { } } } - attroff(COLOR_PAIR(game.piece_queue[index].c)); + attroff(COLOR_PAIR(game.piece_queue[index].t)); attron(A_BOLD); diff --git a/frontends/gl/main.c b/frontends/gl/main.c index a9edd35..c1c6c2d 100644 --- a/frontends/gl/main.c +++ b/frontends/gl/main.c @@ -56,8 +56,8 @@ GLuint indices[] = { 1, 2, 3 // second triangle }; -rbg_color colors[8] = { - {0.0f, 0.0f, 0.0f}, // Black +rbg_color colors[7] = { + //{0.0f, 0.0f, 0.0f}, // Black {0.127f,0.219f,0.255f}, // Aqua {0.61f,0.153f,0.112f}, // Olive {0.177f,0.13f,0.201f}, // Purple @@ -236,8 +236,8 @@ int main(void) { glBindVertexArray(block.vao); for (int x = 0; x < CETRIS_BOARD_X; x++) { for (int y = 0; y < CETRIS_BOARD_Y; y++) { - if (cetris.board[x][y].occupied) { - update_block(&block, x, y, cetris.board[x][y].c); + if (cetris.board[x][y].flags & SLOT_OCCUPIED) { + update_block(&block, x, y, cetris.board[x][y].color); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); } } diff --git a/lib/cetris.h b/lib/cetris.h index b867977..fb3ab1f 100644 --- a/lib/cetris.h +++ b/lib/cetris.h @@ -28,46 +28,40 @@ #define CETRIS_STARTING_LEVEL 1 typedef struct { - int x; - int y; + int8_t x; + int8_t y; } vec2; -typedef int piece_matrix[4][4]; +typedef uint8_t piece_matrix[4][4]; -typedef enum { O, I, S, Z, L, J, T } type; +enum { + MINO_O, + MINO_I, + MINO_S, + MINO_Z, + MINO_L, + MINO_J, + MINO_T +}; -typedef enum { - COLOR_NONE, - COLOR_O, // yellow - COLOR_I, // cyan - COLOR_S, // green - COLOR_Z, // red - COLOR_L, // orange - COLOR_J, // blue - COLOR_T // purple -} color; +enum { + SLOT_OCCUPIED = 1, + SLOT_GHOST = 1 << 1, + SLOT_CONSTANT = 1 << 2 +}; typedef enum { INIT, ONCE_RIGHT, ONCE_LEFT, TWICE } srs_state; typedef struct { - type t; - srs_state r; - color c; + uint8_t t; vec2 pos; + srs_state r; piece_matrix m; - int ghost_y; - int lock_tick; + uint8_t ghost_y; + uint16_t lock_tick; bool locked; } tetrimino; -typedef struct { - bool occupied; - bool ghost; - bool constant; - int remove_tick; - color c; -} slot; - typedef enum { DOWN = 1, USER_DOWN = 2, @@ -80,7 +74,7 @@ typedef enum { typedef struct { /* playfield represented by a 2d array */ - slot board[CETRIS_BOARD_X][CETRIS_BOARD_Y]; + uint8_t board[CETRIS_BOARD_X][CETRIS_BOARD_Y]; /* constant queue of all 7 possible tetrimino */ tetrimino piece_queue[7]; @@ -89,17 +83,18 @@ typedef struct { tetrimino current; tetrimino held; bool piece_held; - int current_index; + uint8_t current_index; /* internal game tick */ - int tick; - int next_drop_tick; - int next_piece_tick; - int down_move_tick; + uint16_t tick; + uint16_t next_drop_tick; + uint16_t next_piece_tick; + uint16_t down_move_tick; + uint16_t line_remove_tick[CETRIS_BOARD_Y]; /* progress trackers */ - int lines; - int level; + uint8_t lines; + uint8_t level; bool game_over; /* scoring flags */ @@ -107,7 +102,7 @@ typedef struct { bool mini_tspin; /* score counter */ - int score; + uint16_t score; } cetris_game; static piece_matrix default_matrices[7] = { @@ -171,10 +166,8 @@ CETRIS_EXPORT void hold_piece(cetris_game *g); static void init_piece_queue(cetris_game *g) { for (int i = 0; i < 7; i++) { g->piece_queue[i].t = i; - g->piece_queue[i].c = i + 1; g->piece_queue[i].r = INIT; g->piece_queue[i].lock_tick = 0; - //g->piece_queue[i].locked = false; g->piece_queue[i].ghost_y = 0; memcpy(g->piece_queue[i].m, default_matrices[i], sizeof(piece_matrix)); @@ -182,7 +175,7 @@ static void init_piece_queue(cetris_game *g) { * tick the bottom row will show. Values here are adjusted * for the default 4x4 matricies for each piece */ g->piece_queue[i].pos.x = 3; - g->piece_queue[i].pos.y = (i == I) ? 17 : 16; + g->piece_queue[i].pos.y = (i == MINO_I) ? 17 : 16; } } @@ -206,7 +199,8 @@ static int check_matrix(cetris_game *g, piece_matrix *m) { return 0; if (r.y >= CETRIS_BOARD_Y) return -1; - if (g->board[r.x][r.y].occupied && g->board[r.x][r.y].constant) + if ((g->board[r.x][r.y] & SLOT_OCCUPIED) + && (g->board[r.x][r.y] & SLOT_CONSTANT)) return -1; } } @@ -220,14 +214,14 @@ static void set_matrix(cetris_game *g, piece_matrix *m) { if ((*m)[y][x]) { vec2 r = (vec2){x + g->current.pos.x, y + g->current.pos.y}; if (r.y >= 0) { - if (!g->board[r.x][r.y].occupied) { - g->board[r.x][r.y].occupied = true; - g->board[r.x][r.y].c = g->current.c; + if (!(g->board[r.x][r.y] & SLOT_OCCUPIED)) { + g->board[r.x][r.y] |= SLOT_OCCUPIED; + g->board[r.x][r.y] |= g->current.t << 5; } } if (g->current.ghost_y + y >= 0) if (r.y != (g->current.ghost_y + y)) - g->board[r.x][g->current.ghost_y + y].ghost = true; + g->board[r.x][g->current.ghost_y + y] |= SLOT_GHOST; } } } @@ -294,7 +288,7 @@ static void make_ghosts(cetris_game *g) { static void reset_tetrimino(tetrimino *t) { t->r = INIT; t->pos.x = 3; - t->pos.y = (t->t == I) ? 17 : 16; + t->pos.y = (t->t == MINO_I) ? 17 : 16; t->ghost_y = 0; } @@ -349,8 +343,8 @@ static void lock_current(cetris_game *g) { g->current.locked = true; for (int x = 0; x < CETRIS_BOARD_X; x++) { for (int y = 0; y < CETRIS_BOARD_Y; y++) { - if (g->board[x][y].occupied) - g->board[x][y].constant = 1; + if (g->board[x][y] & SLOT_OCCUPIED) + g->board[x][y] |= SLOT_CONSTANT; } } update_board(g); @@ -381,7 +375,7 @@ static void rotate_matrix(cetris_game *g, piece_matrix *m, bool clockwise) { int new_x = (clockwise) ? 1 - (y - 2) : 1 + (y - 2); int new_y = (clockwise) ? 2 + (x - 1) : 2 - (x - 1); - if (g->current.t == I) { + if (g->current.t == MINO_I) { if (clockwise) new_y--; else @@ -397,7 +391,7 @@ static void rotate_matrix(cetris_game *g, piece_matrix *m, bool clockwise) { static void rotate_piece(cetris_game *g, bool clockwise) { if (g->game_over || g->next_piece_tick) return; - if (g->current.t == O) + if (g->current.t == MINO_O) return; srs_state next = 0; @@ -450,7 +444,7 @@ static void rotate_piece(cetris_game *g, bool clockwise) { bool set_current = false; bool did_kick = false; for (int i = 0; i < 5; i++) { - if (g->current.t == I) { + if (g->current.t == MINO_I) { kick = srs_wall_kicks_i[wall_kick][i]; } else { kick = srs_wall_kicks[wall_kick][i]; @@ -473,7 +467,7 @@ static void rotate_piece(cetris_game *g, bool clockwise) { if (set_current) { /* check for tspin */ - if (g->current.t == T) { + if (g->current.t == MINO_T) { bool did_tspin = true; for (int i = 1; i < 5; i++) { g->current.pos.x += basic_movements[i].x; @@ -508,16 +502,18 @@ void update_board(cetris_game *g) { for (int y = 0; y < CETRIS_BOARD_Y; y++) { bool clear_line = true; for (int x = 0; x < CETRIS_BOARD_X; x++) { - if (!g->board[x][y].constant) { - memset(&g->board[x][y], 0, sizeof(slot)); + if (!(g->board[x][y] & SLOT_CONSTANT)) { + g->board[x][y] = 0; } - if (!g->board[x][y].occupied || g->board[0][y].remove_tick > 0) { + if (!(g->board[x][y] & SLOT_OCCUPIED) + || g->line_remove_tick[y] > 0) { clear_line = false; } } // remove tick only tracked on first block of line - if (g->board[0][y].remove_tick && g->board[0][y].remove_tick <= g->tick) { + if (g->line_remove_tick[y] && g->line_remove_tick[y] <= g->tick) { + g->line_remove_tick[y] = 0; for (int s = y - 1; s >= 0; s--) { for (int x = 0; x < CETRIS_BOARD_X; x++) { g->board[x][s + 1] = g->board[x][s]; @@ -525,7 +521,7 @@ void update_board(cetris_game *g) { } } if (clear_line) { - g->board[0][y].remove_tick = g->tick + CETRIS_LINE_CLEAR_DELAY; + g->line_remove_tick[y] = g->tick + CETRIS_LINE_CLEAR_DELAY; lines_cleared++; } } @@ -597,7 +593,7 @@ CETRIS_EXPORT void init_game(cetris_game *g) { srand(time(NULL)); #ifdef BUILD_TESTS - apply_test_board(g, TSPIN_NO_LINES); + //apply_test_board(g, TSPIN_NO_LINES); #endif memset(g, 0, sizeof(cetris_game)); diff --git a/lib/test.h b/lib/test.h index 3e41971..cc7d44a 100644 --- a/lib/test.h +++ b/lib/test.h @@ -5,7 +5,7 @@ typedef enum { TSPIN_NO_LINES } test; -u8 tspin_board[20][10] = { +int tspin_board[20][10] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, @@ -28,7 +28,7 @@ u8 tspin_board[20][10] = { { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 } }; -u8 tspin_no_lines_board[20][10] = { +int tspin_no_lines_board[20][10] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, @@ -51,6 +51,7 @@ u8 tspin_no_lines_board[20][10] = { { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 } }; +/* void apply_test_board(cetris_game* g, test t) { u8 (*board)[20][10]; switch (t) { @@ -73,3 +74,4 @@ void apply_test_board(cetris_game* g, test t) { } } } +*/ diff --git a/meson.build b/meson.build index 1140d1f..5bcff0f 100644 --- a/meson.build +++ b/meson.build @@ -4,15 +4,17 @@ project('cetris', 'c', compiler = meson.get_compiler('c') +cetris_inc = include_directories('lib') + if get_option('debug') == true if host_machine.system() == 'windows' add_global_arguments('/DBUILD_TESTS', language : 'c') else add_global_arguments('-DBUILD_TESTS', language : 'c') endif -endif -cetris_inc = include_directories('lib') + subdir('test') +endif if get_option('curses-frontend') == true subdir('frontends/curses') diff --git a/meson_options.txt b/meson_options.txt index 43b497a..b57b6ff 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,2 +1,2 @@ option('curses-frontend', type : 'boolean', value : true) -option('gl-frontend', type : 'boolean', value : true) +option('gl-frontend', type : 'boolean', value : false) diff --git a/test/memory_test.c b/test/memory_test.c new file mode 100644 index 0000000..7a61f6a --- /dev/null +++ b/test/memory_test.c @@ -0,0 +1,13 @@ +#include + +#include "cetris.h" + +int main(void) { + cetris_game game; + + init_game(&game); + + printf("memory usage of game instance: %li\n", sizeof(game)); + + return 0; +} diff --git a/test/meson.build b/test/meson.build new file mode 100644 index 0000000..351be02 --- /dev/null +++ b/test/meson.build @@ -0,0 +1,5 @@ +mem_test = ['memory_test.c'] + +executable('mem_test', mem_test, + include_directories: cetris_inc, + install: false) -- cgit v1.2.3-101-g0448