gol

Implementation of Conway's Game of Life writen in C
git clone git://git.dimitrijedobrota.com/gol.git
Log | Files | Refs | README

commit c3e7299cb951690a1f9db33d1d6b83d1efb43b5d
parent 39fe329a074e151723156bcc186d6d8d15e0da58
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Sun, 19 Jun 2022 19:29:56 +0200

General improvement

- Better error checking
- Better program termination
- Specify default game values at runtime
- Windows bug fixing

Diffstat:
Minclude/display.h | 4++--
Minclude/file.h | 2+-
Minclude/pattern.h | 2+-
Minclude/utils.h | 27++++++++++++++++-----------
Msrc/display.c | 20++++++++------------
Msrc/file.c | 30++++++++++++++++--------------
Msrc/game.c | 42++++++++++++++++++++++++++++--------------
Msrc/main.c | 26++++++--------------------
8 files changed, 78 insertions(+), 75 deletions(-)

diff --git a/include/display.h b/include/display.h @@ -77,8 +77,8 @@ struct imenu_T { int input(WINDOW *win, char *buffer, int size, input_f crit); -int display_start(void); -int display_stop(void); +void display_start(void); +void display_stop(void); void display_menu(window_T wind, char *name, struct menu_T *items, int size, int title); diff --git a/include/file.h b/include/file.h @@ -10,7 +10,7 @@ #include "display.h" -int file_setup(void); +void file_setup(void); void load_files(void); void free_files(void); int file_select_extension(char *ext, char ***buffer); diff --git a/include/pattern.h b/include/pattern.h @@ -8,7 +8,7 @@ #ifndef PATTERN_H #define PATTERN_H -#include "stdlib.h" +#include <stdlib.h> /** * @brief Structure representing one cell pattern to be used in the help menu diff --git a/include/utils.h b/include/utils.h @@ -8,7 +8,10 @@ #ifndef UTILS_H #define UTILS_H -#include "display.h" +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <curses.h> /// maximum of the two numbers #define MAX(a, b) ((a > b) ? a : b) @@ -32,20 +35,22 @@ #define UNICODE 1 #endif +static void err(char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(1); +} + /// Check for memory error, and abort #define MEM_CHECK(x) \ - if ((x) == NULL) { \ - display_stop(); \ - printf("MEM ERROR"); \ - abort(); \ - } + if ((x) == NULL) \ + err("MEMORY ERROR"); /// Check for file error, and abort #define FILE_CHECK(x) \ - if ((x) == NULL) { \ - display_stop(); \ - printf("FILE ERROR"); \ - abort(); \ - } + if ((x) == NULL) \ + err("MEMORY ERROR"); #endif diff --git a/src/display.c b/src/display.c @@ -296,10 +296,14 @@ redraw:; * selected while compiling */ void curses_start(void) { - initscr(); + if (initscr() == NULL) + err("Fatal Error: initscr()"); + window_settings(stdscr); - start_color(); + if (!has_colors() || start_color() != OK) + err("Fatal Error: Terminal does not support colors"); + use_default_colors(); curs_set(0); @@ -357,25 +361,17 @@ void handle_winch(int sig) { /** * @brief Start ncurses display and export MAIN_w */ -int display_start(void) { +void display_start(void) { curses_start(); MAIN_w = window_init(window_new()); - -#ifdef _WIN32 - resize_term(0, 0); - handle_winch(10); -#endif - - return 1; } /** * @brief Stop ncurses display and cleanup */ -int display_stop(void) { +void display_stop(void) { window_free(MAIN_w); endwin(); - return 1; } /** diff --git a/src/file.c b/src/file.c @@ -5,10 +5,10 @@ * @brief This file contains functions for handling save files * * This file aims to provide a simple interface for interacting with the - * file system on Linux and Windows systems. After proper game directory has been - * selected, functions implemented here will read the list of files, filter them - * based on the extension, create new files for storing a whole game or just a - * pattern. + * file system on Linux and Windows systems. After proper game directory has + * been selected, functions implemented here will read the list of files, filter + * them based on the extension, create new files for storing a whole game or + * just a pattern. */ #include <dirent.h> @@ -46,7 +46,7 @@ int DirectoryExists(const char *path) { * @brief Try to change the directory to SETTINGS_DIR, if it doesn't exist * create it. Return 0 on failure. */ -int file_setup(void) { +void file_setup(void) { char *dir; MEM_CHECK(dir = malloc(PATH_MAX * sizeof(char))); @@ -59,17 +59,17 @@ int file_setup(void) { if (!DirectoryExists(dir)) { printf("Directory %s does not exists; Trying to create it...\n", dir); - if (MAKE_DIR(dir) != 0) { - printf("Cannot create the directory\n"); - return 0; - } + if (MAKE_DIR(dir) != 0) + err("Cannot create the directory %s", dir); } if (chdir(dir) != 0) { printf("Cannot change the directory\n"); - return 0; + if (MAKE_DIR(dir) != 0) + err("Cannot change directory to %s", dir); } - return 1; + + free(dir); } typedef struct file_T *file_T; @@ -95,8 +95,10 @@ file_T file_new(char *name) { file_T f; MEM_CHECK(f = calloc(1, sizeof(*f))); - if (name != NULL) - MEM_CHECK(f->name = strdup(name)); + if (name != NULL) { + MEM_CHECK(f->name = malloc((strlen(name) + 1) * sizeof(char))); + strcpy(f->name, name); + } return f; } @@ -275,7 +277,7 @@ void file_save_pattern(char *name, int index) { FILE *f; char *fname; - MEM_CHECK(fname = malloc((strlen(name) + 5) * sizeof(char))); + MEM_CHECK(fname = malloc((strlen(name) + 6) * sizeof(char))); sprintf(fname, "%s.part", name); FILE_CHECK(f = fopen(fname, "w")); diff --git a/src/game.c b/src/game.c @@ -20,6 +20,11 @@ #include "utils.h" #include "window.h" +#define DEF_GEN_STEP 1 +#define DEF_SCREEN_STEP 1 +#define DEF_TIME_CONST 100 +#define DEF_TIME_STEP 1 + #ifdef _WIN32 #define TIME_MOD 1 #else @@ -58,8 +63,10 @@ int height, width; static int win_height, win_width; static int screen_offset_x, screen_offset_y; static int cursor_offset_x, cursor_offset_y; -static int wrap, gen = 0, gen_step = 1; -static int play = 0, time_const = 100, time_step = 1; +static int wrap, gen_step, screen_step; +static int play, time_const, time_step; + +static unsigned gen; #define y_at(y) y, screen_offset_y, height #define x_at(x) x, screen_offset_x, width @@ -183,10 +190,10 @@ void display_status(window_T wind) { wmove(win, 1, 1); wprintw(win, " %5s | ", play ? "play" : "pause"); - wprintw(win, wrap ? "Size: %dx%d | " : "Size: unlimited | ", height, width); - wprintw(win, "Generation: %10d(+%d) | ", gen, gen_step); + wprintw(win, wrap ? "Size: %9dx%9d | " : "Size: unlimited | ", height, width); + wprintw(win, "Generation: %10u(+%d) | ", gen, gen_step); wprintw(win, "dt: %4dms | ", time_const); - wprintw(win, "Cursor: %4dx%-4d | ", cord(y_at(cursor_offset_y)), + wprintw(win, "Cursor: %10dx%10d | ", cord(y_at(cursor_offset_y)), cord(x_at(cursor_offset_x))); wrefresh(win); } @@ -288,6 +295,7 @@ int display_select(window_T wind) { case 'q': case 'Q': flushinp(); + ret_value = 1; goto end; } flushinp(); @@ -299,8 +307,11 @@ int display_select(window_T wind) { overlay(win, new); } end:; + + if (UNICODE) + delwin(new); + nodelay(stdscr, 1); - delwin(new); return ret_value; } @@ -349,6 +360,10 @@ void game(int s_h, int s_w, int mode_index) { window_T status_w, screen_w, game_w; + gen = 0; + gen_step = DEF_GEN_STEP, time_const = DEF_TIME_CONST; + time_step = DEF_TIME_STEP, screen_step = DEF_SCREEN_STEP; + reset_screen: status_w = window_split(menu_w, 1, 3, 0, "Status", "Game"); screen_w = window_sibiling(status_w); @@ -536,7 +551,6 @@ redraw:; save_state(); goto reset_screen; - break; // lead pattern case 'l': @@ -544,27 +558,27 @@ redraw:; window_unsplit(menu_w); setPosition(cord(y_at(cursor_offset_y)), cord(x_at(cursor_offset_x))); load_pattern(); + save_state(); goto reset_screen; - break; // save game case 'o': case 'O': window_unsplit(menu_w); save(); + save_state(); goto reset_screen; - break; // help menu case 'h': case 'H': window_unsplit(menu_w); display_patterns(menu_w); + save_state(); goto reset_screen; - break; // redraw screen case 'r': @@ -581,7 +595,7 @@ redraw:; case KEY_END: case KEY_HOME: case KEY_LEFT: - screen_offset_x--; + screen_offset_x -= screen_step; screen_change = 1; break; case KEY_A3: @@ -589,7 +603,7 @@ redraw:; case KEY_NPAGE: case KEY_PPAGE: case KEY_RIGHT: - screen_offset_x++; + screen_offset_x += screen_step; screen_change = 1; break; } @@ -600,7 +614,7 @@ redraw:; case KEY_HOME: case KEY_PPAGE: case KEY_UP: - screen_offset_y--; + screen_offset_y -= screen_step; screen_change = 1; break; case KEY_C1: @@ -608,7 +622,7 @@ redraw:; case KEY_DOWN: case KEY_END: case KEY_NPAGE: - screen_offset_y++; + screen_offset_y += screen_step; screen_change = 1; } diff --git a/src/main.c b/src/main.c @@ -17,8 +17,8 @@ window_T menu_w; void settings(char *pass, int index) { struct imenu_T imenu_items[] = { - { "Number of rows", 6, isdigit, NULL}, - {"Number of columns", 6, isdigit, NULL}, + { "Number of rows", 9, isdigit, NULL}, + {"Number of columns", 9, isdigit, NULL}, }; int imenu_items_s = sizeof(imenu_items) / sizeof(struct imenu_T); @@ -133,10 +133,7 @@ void help(char *pass, int index) { display_patterns(menu_w); } -void exitp(char *pass, int index) { - display_stop(); - exit(0); -} +void exitp(char *pass, int index) { exit(0); } struct menu_T menu_items[] = { {mode_select, "Start"}, @@ -149,26 +146,15 @@ int menu_items_s = sizeof(menu_items) / sizeof(struct menu_T); int main(void) { setlocale(LC_ALL, ""); + atexit(display_stop); - if (!file_setup()) { - printf("File setup error\n"); - abort(); - } - - if (!display_start()) { - printf("Couldn't start the display!\n"); - abort(); - } + file_setup(); + display_start(); menu_w = MAIN_w; while (TRUE) { display_menu(menu_w, "Main menu", menu_items, menu_items_s, 1); } - if (!display_stop()) { - printf("Couldn't stop the display!\n"); - abort(); - } - return 0; }