diff --git a/.gitignore b/.gitignore index a216239..3fceecb 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,11 @@ a.out *.a +# debugging files +gdb* +*.ini +*.log + # cpp check *cppcheck-build-dir *.cppcheck diff --git a/.gitmodules b/.gitmodules index c95af8c..8c1344b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "extern/Unity"] path = extern/Unity url = git@github.com:ThrowTheSwitch/Unity.git +[submodule "extern/inih"] + path = extern/inih + url = git@github.com:benhoyt/inih.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 8218fc7..e70228f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ set(TARGET_GROUP production CACHE STRING "Group to build") # add_subdirectory(module_a) if(TARGET_GROUP STREQUAL production) + add_subdirectory(extern) include_directories(include) add_subdirectory(src) elseif(TARGET_GROUP STREQUAL test) diff --git a/README.md b/README.md index 95d27ae..16e8fdf 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,14 @@ This game engine is part of a larger project where I'm implementing a version of The game was branched off to it's own repository because the embedded project source tree only needs `tetris.h` and `tetris.c`, but all the other driver and testing files take up a decent amount of space - so while i'm developing it, it'll live here. ### TODO: -* Finish tetris game logic +* [ ] Finish tetris game logic * [X] Set up CI/CD with GH actions -* fix gameover state not detected -* fix full lines not being removed -* fix duplicate colors +* [ ] fix gameover state not detected +* [ ] fix full lines not being removed +* [ ] fix duplicate colors +* [ ] fix crash on trying to clear line +* figure out what's causing a bunch of out-of-bounds numbers to show up in row 1 of board + ### Project Goals diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 8199907..3923e47 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -10,3 +10,11 @@ target_include_directories(Unity PUBLIC Unity/src ) +add_library(ini STATIC + inih/ini.c + inih/ini.h +) + +target_include_directories(ini PUBLIC + inih +) diff --git a/extern/inih b/extern/inih new file mode 160000 index 0000000..5cc5e2c --- /dev/null +++ b/extern/inih @@ -0,0 +1 @@ +Subproject commit 5cc5e2c24642513aaa5b19126aad42d0e4e0923e diff --git a/include/display.h b/include/display.h index 5fbbff7..a86ed8c 100644 --- a/include/display.h +++ b/include/display.h @@ -3,6 +3,7 @@ #include #include +#include "tetris.h" #ifdef __linux__ #include @@ -12,5 +13,9 @@ void nc_init_colors(WINDOW *w); +void print_board_state(TetrisBoard tb, FILE *file); +void save_game_state(TetrisGame *tg); + +void save_board_to_file(FILE *file, TetrisBoard tb); #endif \ No newline at end of file diff --git a/include/driver_tetris.h b/include/driver_tetris.h index 03a704c..1144bea 100644 --- a/include/driver_tetris.h +++ b/include/driver_tetris.h @@ -13,6 +13,8 @@ #include #include +#include "ini.h" + #include "tetris.h" #include "display.h" @@ -45,7 +47,6 @@ void update_score(WINDOW *w, TetrisGame *tg); void print_keypress(enum player_move move); void sleep_millis(uint16_t millis); -void print_board_state(TetrisBoard tb); // void refresh_debug_var_window(WINDOW *w, enum player_move move); void refresh_debug_var_window(WINDOW *w); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 94e4d90..daa02ca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,5 +13,13 @@ target_include_directories(tetris_driver PUBLIC ${PROJECT_SOURCE_DIR}/include) # message("building driver with proj source dir ${PROJECT_SOURCE_DIR}") find_library(NCURSES_FOUND ncurses REQUIRED) -target_link_libraries(tetris_driver ncurses tetris) + +# find ini library (needed for saving game state to disk) +OPTION(INI_LIB_INCLUDE_OPTION "Include inih library for saving game state to disk" ON) +IF(INI_LIB_INCLUDE_OPTION) + # find_library(INIH_FOUND ${PROJECT_SOURCE_DIR}/extern/inih/ini.c REQUIRED) + target_link_libraries(tetris_driver ncurses tetris ini) +ELSE() + target_link_libraries(tetris_driver ncurses tetris) +ENDIF() diff --git a/src/display.c b/src/display.c index d133f4b..6a7085c 100644 --- a/src/display.c +++ b/src/display.c @@ -25,5 +25,124 @@ void nc_init_colors(WINDOW *w) { -void update_score(WINDOW *w, TetrisGame *g); +/** + * This function helps with debugging edge cases. + * Will save the board to a file `gamestate.ini` +*/ +void save_game_state(TetrisGame *tg) { + FILE *savefile; + savefile = fopen("gamestate.ini", "w+"); + + + // pretty-print board state to file for ease of differentiation + fprintf(savefile, "[BOARD_IMAGE]\n"); + print_board_state(tg->active_board, savefile); + + // save TetrisGame state + fprintf(savefile, "\n\n[TETRIS_GAME_STRUCT]\n"); + fprintf(savefile, "active_board = "); + save_board_to_file(savefile, tg->active_board); + fprintf(savefile, "active_board.highest_occupied_cell = %d\n", tg->active_board.highest_occupied_cell); + fprintf(savefile, "\nboard = "); + save_board_to_file(savefile, tg->board); + fprintf(savefile, "board.highest_occupied_cell = %d\n", tg->board.highest_occupied_cell); + fprintf(savefile, "\n"); + fflush(savefile); + + fprintf(savefile, "game_over = %d\n", tg->game_over); + fprintf(savefile, "score = %d\n", tg->score); + fprintf(savefile, "level = %d\n", tg->level); + fprintf(savefile, "gravity_tick_rate_usec = %d\n", tg->gravity_tick_rate_usec); + fprintf(savefile, "last_gravity_tick_usec = {%ld,%ld}\n", tg->last_gravity_tick_usec.tv_sec, tg->last_gravity_tick_usec.tv_usec); + + + fprintf(savefile, "\n[ACTIVE_PIECE]\n"); + fprintf(savefile, "ptype = %d\n", tg->active_piece.ptype); + fprintf(savefile, "loc_row = %d\n", tg->active_piece.loc.row); + fprintf(savefile, "loc_col = %d\n", tg->active_piece.loc.col); + fprintf(savefile, "orientation = %d\n", tg->active_piece.orientation); + fprintf(savefile, "falling = %d\n", tg->active_piece.falling); + + printf("game state saved!\n"); + + fclose(savefile); + +} + +/** + * Print current board state to console + * @param TetrisBoard + * @param *file to print to - NULL for default +*/ +void print_board_state(TetrisBoard tb, FILE *file) { + if (file == NULL) + file = gamelog; + // draw existing pieces on board + fprintf(file, "Highest occupied cell: %d\n ", tb.highest_occupied_cell); + fprintf(file, " "); + for (int i = 0; i < TETRIS_COLS; i++) + fprintf(file, "%-2d ",i); + fprintf(file, "\n "); + + for (int i = 0; i < TETRIS_COLS; i++) + fprintf(file, "----"); + fprintf(file, "----\n"); + + for (int i = 0; i < TETRIS_ROWS; i++) { + fprintf(file, "%-3d| ", i); + for (int j = 0; j < TETRIS_COLS; j++) { + if (tb.board[i][j] >= 0) { + fprintf(file, "%-3d ", tb.board[i][j]); + } + else { + fprintf(file, " "); + } + } + fprintf(file, "|\n"); + } + fflush(file); +} + + +/** + * Handler for reading .ini file +*/ +// static int handler(void* user, const char* section, const char* name, +// const char* value) +// { +// TetrisGame *tg = (TetrisGame*)user; + +// #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 +// if (MATCH("protocol", "version")) { +// pconfig->version = atoi(value); +// } else if (MATCH("user", "name")) { +// pconfig->name = strdup(value); +// } else if (MATCH("user", "email")) { +// pconfig->email = strdup(value); +// } else { +// return 0; /* unknown section/name, error */ +// } +// return 1; +// } + + + +void save_board_to_file(FILE *file, TetrisBoard tb) { + fprintf(file, "{"); + + for (int i = 0; i < TETRIS_ROWS; i++) { + // fprintf(file, "%-3d| ", i); + fprintf(file, "{"); + for (int j = 0; j < TETRIS_COLS - 1; j++) { + fprintf(file, "%d,", tb.board[i][j]); + } + fprintf(file, "%d}", tb.board[i][TETRIS_COLS - 1]); + + // dont print comma on last row + if (i < TETRIS_ROWS - 1) + fprintf(file,", "); + } + fprintf(file, "}\n"); + fflush(file); +} diff --git a/src/driver_tetris.c b/src/driver_tetris.c index f8ffe8e..e597bd7 100644 --- a/src/driver_tetris.c +++ b/src/driver_tetris.c @@ -74,23 +74,19 @@ int main(void) { fprintf(gamelog, "========================================\n"); fflush(gamelog); #endif + bool gamestate = true; // while game is running and player hasn't tried to quit while (!tg->game_over && move != T_QUIT) { - tg_tick(tg, move); + gamestate = tg_tick(tg, move); + // IMPLEMENT WHAT HAPPENS ON GAME OVER!! // display board display_board(g_win, &tg->active_board); update_score(s_win, tg); - -// TWO ISSUES 4:30PM 3/17: -// gravity tick not working -// board update without move not working - - switch(getch()) { case KEY_UP: move = T_UP; @@ -110,6 +106,19 @@ int main(void) { case 'q': move = T_QUIT; break; + // save game to disk + case 'p': + move = T_NONE; + save_game_state(tg); + mvwprintw(s_win, 4,1, "GAME STATE SAVED\n"); + fprintf(gamelog, "game state saved to file gamestate.ini\n"); + wnoutrefresh(s_win); + + break; + // load game from disk + case 'l': + move = T_NONE; + break; default: move = T_NONE; } @@ -167,7 +176,7 @@ void display_board(WINDOW *w, TetrisBoard *tb) { wrefresh(w); #ifdef DEBUG_T fprintf(gamelog, "display_board()\n"); - // print_board_state(*tb); + // print_board_state(*tb, NULL); fflush(gamelog); #endif @@ -184,13 +193,8 @@ void display_board(WINDOW *w, TetrisBoard *tb) { // fprintf(gamelog, "last update timer: %ld ; curr_time = %ld, last_update=%ld\n", \ // curr_time_usec.tv_usec - last_update.tv_usec, curr_time_usec.tv_usec, last_update.tv_usec); // #endif - // } - if (curr_time_usec.tv_sec - last_update.tv_sec > 1) { - - } - -} + } // void draw_piece(WINDOW *w, TetrisBoard *tb) @@ -239,8 +243,10 @@ void refresh_debug_var_window(WINDOW *w) { wmove(w, 1, 1); TetrisPiece tp = tg->active_piece; char const* piece_str[] = {"S_PIECE", "Z_PIECE", "T_PIECE", "L_PIECE", "J_PIECE", "SQ_PIECE", "I_PIECE"}; + /* char const* move_str[] = {"T_NONE", "T_UP", "T_DOWN", "T_LEFT", \ "T_RIGHT", "T_PLAYPAUSE", "T_QUIT"}; + */ mvwprintw(w, 1,1, "DEBUG INFO:\n"); // mvwprintw(w, 2,1, "Current move: %s\n", move_str[move]); @@ -256,34 +262,3 @@ void refresh_debug_var_window(WINDOW *w) { } - -/** - * Print current board state to console - * (duplicate from tetris_test_helpers) -*/ -void print_board_state(TetrisBoard tb) { - // draw existing pieces on board - fprintf(gamelog, "Highest occupied cell: %d\n ", tb.highest_occupied_cell); - fprintf(gamelog, " "); - for (int i = 0; i < TETRIS_COLS; i++) - fprintf(gamelog, "%-2d ",i); - fprintf(gamelog, "\n "); - - for (int i = 0; i < TETRIS_COLS; i++) - fprintf(gamelog, "----"); - fprintf(gamelog, "----\n"); - - for (int i = 0; i < TETRIS_ROWS; i++) { - fprintf(gamelog, "%-3d| ", i); - for (int j = 0; j < TETRIS_COLS; j++) { - if (tb.board[i][j] >= 0) { - fprintf(gamelog, "%-3d ", tb.board[i][j]); - } - else { - fprintf(gamelog, " "); - } - } - fprintf(gamelog, "|\n"); - } - fflush(gamelog); -} \ No newline at end of file diff --git a/test/suite_1.c b/test/suite_1.c index 2802ec0..7d585f6 100644 --- a/test/suite_1.c +++ b/test/suite_1.c @@ -198,6 +198,9 @@ void test_clearRows(void) { for (int i = 25; i < TETRIS_ROWS; i++) { TEST_ASSERT_FALSE(check_filled_row(tg, i)); } +} + +void test_clearRowsDumpedGame(void) { } @@ -245,8 +248,6 @@ void test_checkSpawnNewPiece(void) { // test piece stopped falling invalid case - - // test piece stopped falling game over condition } @@ -261,10 +262,44 @@ void test_getElapsedUs(void) { gettimeofday(&after, NULL); elapsed_us = get_elapsed_us(before, after); // printf("elapsed_us: %d\n", elapsed_us); - TEST_ASSERT_TRUE_MESSAGE(elapsed_us > ms_in_1s - 100 && elapsed_us < ms_in_1s + 100, \ + TEST_ASSERT_TRUE_MESSAGE(elapsed_us > ms_in_1s - 150 && elapsed_us < ms_in_1s + 150, \ "delay function returned incorrect range"); } +/** + * Test smallest_in_arr helper func +*/ +void test_arr_helpers(void) { + // test int16_to_uint8_arr() + int16_t arr1[5] = {5,2,1,4,3}; + uint8_t out_arr1[5]; + uint8_t exp_arr1[5] = {5,2,1,4,3}; + int16_to_uint8_arr(arr1, out_arr1, 5); + TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(exp_arr1, \ + out_arr1, 5, "Array conversion to uint8 incorrect"); + + TEST_ASSERT_EQUAL_UINT8_MESSAGE(1, (uint8_t)smallest_in_arr(arr1, 5), \ + "smallest_in_arr failed on all positive values"); + + int16_t arr2[6] = {INT8_MAX,1,2,255,4,0}; + uint8_t out_arr2[6]; + uint8_t exp_arr2[6] = {INT8_MAX,1,2,255,4,0}; + int16_to_uint8_arr(arr2, out_arr2, 6); + TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(exp_arr2, \ + out_arr2, 6, "arr2 conversion to uint8 incorrect"); + + TEST_ASSERT_EQUAL_UINT8_MESSAGE(0, (uint8_t)smallest_in_arr(arr2, 6), \ + "smallest_in_arr failed on all positive values"); + + // test smallest in arr + int16_t arr4[5] = {5,2,1,4,3}; + TEST_ASSERT_EQUAL_INT16(1, smallest_in_arr(arr4, 5)); + int8_t arr5[6] = {2,1,2,-2,4,5}; + // TEST_ASSERT_EQUAL_INT16(1, smallest_in_arr(arr5, 6)); + int16_t arr6[6] = {INT16_MAX,-1,2,-2,4,5}; + TEST_ASSERT_EQUAL_INT16(-2, smallest_in_arr(arr6, 6)); +} + int main(void) { UNITY_BEGIN(); @@ -276,7 +311,7 @@ int main(void) RUN_TEST(test_clearRows); RUN_TEST(test_checkSpawnNewPiece); RUN_TEST(test_getElapsedUs); - printf("tests completed run\n"); + RUN_TEST(test_arr_helpers); return UNITY_END(); diff --git a/test/tetris_test_helpers.h b/test/tetris_test_helpers.h index 9f3d546..5911c23 100644 --- a/test/tetris_test_helpers.h +++ b/test/tetris_test_helpers.h @@ -23,4 +23,45 @@ void print_piece(const tetris_location t[4][4]); void sleep_millis(uint16_t millis); + + +// when including this get multiple defn error for some reason :/ +// const int8_t almost_full_board[TETRIS_ROWS][TETRIS_COLS] = +// { +// {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1}, +// {-1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1} +// }; + + + #endif \ No newline at end of file diff --git a/tetris/tetris.c b/tetris/tetris.c index 9e9c01f..939887b 100644 --- a/tetris/tetris.c +++ b/tetris/tetris.c @@ -90,20 +90,20 @@ void end_game(TetrisGame *tg) { * Process a single game tick * @param TetrisGame *tg * @param player_move move + * @returns true if game is still going, false when game_over */ -void tg_tick(TetrisGame *tg, enum player_move move) { +bool tg_tick(TetrisGame *tg, enum player_move move) { // handle gravity, input, cleared lines, adjusting score, checking game over check_do_piece_gravity(tg); check_and_spawn_new_piece(tg); + if (! check_game_over(tg)) // not fully implemented yet + return false; render_active_board(tg); - #ifdef DEBUG_T - // fprintf(gamelog, "."); - // fflush(gamelog); - #endif + switch (move) { case T_NONE: @@ -145,7 +145,7 @@ void tg_tick(TetrisGame *tg, enum player_move move) { break; } - + return true; } @@ -176,8 +176,12 @@ TetrisBoard render_active_board(TetrisGame *tg) { #endif // starting at tetris_location fill cells using relative locations from TETROMINOS - for(int i = 0; i < 4; i++) { + for(int i = 0; i < NUM_CELLS_IN_TETROMINO; i++) { tetris_location curr_offset = tp_cells[i]; + + // saw a crash here, so lets check for values - issue should be fixed but still + assert(curr_offset.col < TETRIS_COLS && curr_offset.row < TETRIS_ROWS && "curr_offset out of bounds"); + // update board to reflect placement of piece gameboard.board[tp.loc.row + curr_offset.row] \ [tp.loc.col + curr_offset.col] = tp.ptype; @@ -236,10 +240,10 @@ TetrisPiece create_rand_piece(TetrisGame *tg) { TetrisPiece new_piece; - new_piece.ptype = rand() % NUM_TETROMINOS + 1; + new_piece.ptype = rand() % NUM_TETROMINOS; new_piece.orientation = 0; new_piece.loc.col = TETRIS_COLS / 2; - new_piece.loc.row = 2; // CHANGE TO 1 ONCE DONE!!!!! + new_piece.loc.row = 1; new_piece.falling = true; tg->active_piece = new_piece; @@ -385,9 +389,10 @@ bool test_piece_rotate(TetrisBoard *tb, const TetrisPiece tp) { testing_loc.col > -1 && testing_loc.col < TETRIS_COLS) result = result && true; else - result = false; + return false; } + // this could just be return true but whatever return result; } @@ -501,19 +506,20 @@ bool check_and_spawn_new_piece(TetrisGame *tg) { // if we're here, we must have landed TetrisPiece tp = tg->active_piece; // last active piece #ifdef DEBUG_T - fprintf(gamelog, "Piece stopped falling at loc row=%d, col=%d", tp.loc.row, tp.loc.col); + fprintf(gamelog, "Piece stopped falling at loc row=%d, col=%d\n", tp.loc.row, tp.loc.col); + fflush(gamelog); #endif // set up active piece and array of offsets from current loc tetris_location tp_cells[NUM_CELLS_IN_TETROMINO]; - memcpy(tp_cells, TETROMINOS[tp.ptype][tp.orientation], sizeof(tp_cells)); - - // put global piece locations in tp_cells - for(int i = 0; i < NUM_CELLS_IN_TETROMINO; i++) { + for (int i = 0; i < NUM_CELLS_IN_TETROMINO; i++) { + tp_cells[i] = TETROMINOS[tp.ptype][tp.orientation][i]; + // put global piece locations in tp_cells tp_cells[i].row += tp.loc.row; tp_cells[i].col += tp.loc.col; } + // add piece to board for(int i = 0; i < NUM_CELLS_IN_TETROMINO; i++) { // set global locations on board equal to piece color @@ -522,18 +528,20 @@ bool check_and_spawn_new_piece(TetrisGame *tg) { - // some slight duplicate work here but implementation of - // not doing dupliate work was complex enough that this is prob better - // in short, this only checks rows where piece ended up, but will - // check all 4 rows; which might mean checking the same row twice - int16_t rows_to_clear[4]; + /* some slight duplicate work here but implementation of + * not doing dupliate work was complex enough that this is prob better. + * + * in short, this only checks rows where piece ended up, but will + * check all 4 rows; which might mean checking the same row twice + */ + uint8_t rows_to_clear[4]; uint8_t rows_idx = 0; - int16_t row_with_offset; - int16_t new_max_row = TETRIS_ROWS; + uint8_t row_with_offset; + uint8_t new_max_row = TETRIS_ROWS; for(int i = 0; i < NUM_CELLS_IN_TETROMINO; i++) { row_with_offset = tp_cells[i].row; - assert(row_with_offset < TETRIS_ROWS && "global row out of bounds"); + assert(row_with_offset < TETRIS_ROWS && row_with_offset > -1 && "global row out of bounds"); // if row is full, add it to list of rows to clear if(check_filled_row(tg, row_with_offset)) { @@ -554,7 +562,7 @@ bool check_and_spawn_new_piece(TetrisGame *tg) { // if we have rows to clear: if (rows_idx > 0) { // find top row in rows_to_clear and clear rows - int16_t top_row = smallest_in_arr(rows_to_clear, rows_idx + 1); + uint8_t top_row = (uint8_t) smallest_in_arr(rows_to_clear, rows_idx + 1); #ifdef DEBUG_T fprintf(gamelog, "clearing %d rows with top_row=%d\n", rows_idx + 1, top_row); fflush(gamelog); @@ -581,6 +589,14 @@ bool check_game_over(TetrisGame *tg) { // game over condition. it keeps adding pieces on top of each other, // whcih is a mess. + + + // NOT FINISHED BEING IMPL YET + + // if we get to 1 as a highest occupied cell, stop + if (tg->board.highest_occupied_cell == 1) + return true; + return false; } @@ -600,16 +616,27 @@ bool check_game_over(TetrisGame *tg) { //} /** - * Very simple helper function to return smallest value in array - * (uint8_t only, used for row clearing operation) + * Simple helper function to return smallest value in array + * (int16_t only, used for row clearing operation) */ -int16_t smallest_in_arr(int16_t arr[], const size_t arr_size) { +int16_t smallest_in_arr(int16_t arr[], uint8_t arr_size) { int smallestVal = INT16_MAX; for (int i = 0; i < arr_size; i++) { if (arr[i] < smallestVal) { smallestVal = arr[i]; } } + + #ifdef DEBUG_T + + fprintf(gamelog, "smallest value in array {"); + for (int i = 0; i < arr_size; i++) { + fprintf(gamelog, "%d, ", arr[i]); + } + fprintf(gamelog, "} is %d!\n", smallestVal); + + #endif + return smallestVal; } @@ -633,6 +660,18 @@ inline int32_t get_elapsed_us(struct timeval before, struct timeval after) { return elapsed_us; } +/** + * convert input array of int16_t to uint8_t +*/ +void int16_to_uint8_arr(int16_t *in_arr, uint8_t *out_arr, uint8_t arr_size) { + uint8_t new_value; + for (int i = 0; i < arr_size; i++) { + // out_arr[i] = (uint8_t) ((in_arr[i]) >> 8); + assert(in_arr[i] < 256 && in_arr[i] > -1 && "Can't cast OOB int16 to uint8!"); + out_arr[i] = (uint8_t) (in_arr[i]); + } +} + /** * a "Tetromino", or piece on the tetris board. diff --git a/tetris/tetris.h b/tetris/tetris.h index 5d4e666..1681503 100644 --- a/tetris/tetris.h +++ b/tetris/tetris.h @@ -122,7 +122,7 @@ typedef struct TetrisGame { TetrisGame* create_game(void); void end_game(TetrisGame *tg); -void tg_tick(TetrisGame *tg, enum player_move move); +bool tg_tick(TetrisGame *tg, enum player_move move); bool check_valid_move(TetrisGame *tg, uint8_t player_move); @@ -141,8 +141,10 @@ bool check_filled_row(TetrisGame *tg, uint8_t row); bool check_and_spawn_new_piece(TetrisGame *tg); void clear_rows(TetrisGame *tg, uint8_t top_row, uint8_t num_rows); +bool check_game_over(TetrisGame *tg); int32_t get_elapsed_us(struct timeval before, struct timeval after); -int16_t smallest_in_arr(int16_t arr[], const size_t arr_size); +int16_t smallest_in_arr(int16_t arr[], const uint8_t arr_size); +void int16_to_uint8_arr(int16_t *in_arr, uint8_t *out_arr, uint8_t arr_size); #endif \ No newline at end of file