Skip to content

Commit

Permalink
debugging, and added functionality for saving game state to disk
Browse files Browse the repository at this point in the history
  • Loading branch information
0xjmux committed Mar 20, 2024
1 parent a787ef0 commit 34efdc6
Show file tree
Hide file tree
Showing 15 changed files with 331 additions and 85 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ a.out
*.a


# debugging files
gdb*
*.ini
*.log

# cpp check
*cppcheck-build-dir
*.cppcheck
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions extern/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
1 change: 1 addition & 0 deletions extern/inih
Submodule inih added at 5cc5e2
5 changes: 5 additions & 0 deletions include/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <stdbool.h>
#include <stdint.h>
#include "tetris.h"

#ifdef __linux__
#include <stdio.h>
Expand All @@ -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
3 changes: 2 additions & 1 deletion include/driver_tetris.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <assert.h>
#include <ncurses.h>

#include "ini.h"

#include "tetris.h"
#include "display.h"

Expand Down Expand Up @@ -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);
Expand Down
10 changes: 9 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()

121 changes: 120 additions & 1 deletion src/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
65 changes: 20 additions & 45 deletions src/driver_tetris.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -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]);
Expand All @@ -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);
}
Loading

0 comments on commit 34efdc6

Please sign in to comment.