gol

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

commit 39fe329a074e151723156bcc186d6d8d15e0da58
parent 6049c8151ba0a9ce5ceb2e63e244c3d5df673cc2
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Sun, 19 Jun 2022 15:02:09 +0200

Documentation and general improvement

- Update README
- Update Doxygen settings
- Documentation for .h files
- Fixed spelling

Diffstat:
MDoxyfile | 2+-
MREADME.md | 28+++++++++++++++-------------
Minclude/display.h | 45+++++++++++++++++++++++++++++++++++++--------
Minclude/file.h | 7+++++++
Minclude/game.h | 7+++++++
Minclude/main.h | 3---
Minclude/pattern.h | 21+++++++++++++++++++--
Minclude/utils.h | 45+++++++++++++++++++++++----------------------
Minclude/window.h | 11+++++++++--
Msrc/display.c | 6+++---
Msrc/file.c | 2+-
Msrc/game.c | 13++++++-------
Msrc/window.c | 28++++++++++++++--------------
13 files changed, 142 insertions(+), 76 deletions(-)

diff --git a/Doxyfile b/Doxyfile @@ -2196,7 +2196,7 @@ SEARCH_INCLUDES = YES # preprocessor. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = +INCLUDE_PATH = ./include # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the diff --git a/README.md b/README.md @@ -1,36 +1,32 @@ - ____ __ _ _ __ - / ___| __ _ _ __ ___ ___ ___ / _| | | (_)/ _| ___ -| | _ / _` | '_ ` _ \ / _ \ / _ \| |_ | | | | |_ / _ \ -| |_| | (_| | | | | | | __/ | (_) | _| | |___| | _| __/ - \____|\__,_|_| |_| |_|\___| \___/|_| |_____|_|_| \___| - - # Game of Life Simulation This is an implementation of Conway's Game of Life in C for a university project + ## About the Project -This project is command line application written in C using the ncruses library +This project is command line application written in C using the ncurses library to display GUI in the terminal. Project works both on Unix and Windows systems. Features: -- Responsive: - - Every menu and graphic is completely responsive +- Graphics: + - Every menu is completely responsive + - Unicode and No Unicode mode - Multiple game modes: - Normal - Coexistence - Predator - - Virus - Unknown + - Virus - Game: - Custom display interval - Custom speed - Movable cursor - Movable screen - - Status line with basic information abut the game + - Optional mouse support in game + - Status line with information about the game - Visual select mode - Save/Load system: @@ -45,12 +41,18 @@ Features: - Information about the game - Cheatsheet with common cell patterns +- Documentation: + - html documentation using doxygen + - pdf documentation using doxygen and pdflatex + ## Prerequisites - C compiler - Make - ncurses (pdcurses on Windows) +- doxygen and pdflatex (if you want to build documentation) + ## How to build @@ -77,9 +79,9 @@ To then run the program, type in in the project directory. To compile the debugging version of the executable, run `make DEBUG=Y`. - You can also give the optional parameter `NO_UNICODE=Y`, to build the executable without Unicode support. + ## Running the project In the `./bin` directory you will find the executable, and you can run it with diff --git a/include/display.h b/include/display.h @@ -1,14 +1,43 @@ +/** + * @file display.h + * @author Dimitrije Dobrota + * @date 19 June 2022 + * @brief Methods and types for displaying UI + */ + #ifndef DISPLAY_H #define DISPLAY_H #include "window.h" -#define CHAR_BLANK " " +#ifdef _WIN32 +#define is_term_resized(a, b) is_termresized() +#define HANDLE_RESIZE \ + { \ + resize_term(0, 0); \ + handle_winch(10); \ + } +#else +#define HANDLE_RESIZE \ + { handle_winch(10); } +#endif + +/// Character representing dead cell +#define CHAR_BLANK " " + +/// Character representing cursor #define CHAR_CURSOR "<>" + +/// Character representing a living cell with a circle #define CHAR_CIRCLE "\u26AB" + +/// Character representing a living cell with a square #define CHAR_SQUARE "\u2B1B" + +/// Active representation of a living cell #define CHAR_ACTIVE CHAR_CIRCLE +/// Way of printing a cell based on Unicode support #ifndef NO_UNICODE #define print_cell(win, blank) \ if (val) \ @@ -24,8 +53,8 @@ extern mmask_t mbitmask; typedef int (*input_f)(int); -/* - * @breaf A item in a menu +/** + * @brief A item in a menu * * It's intended to be used with display_menu(). */ @@ -34,16 +63,16 @@ struct menu_T { char *name; ///< name of the menu item }; -/* - * @breaf A item in a interactive menu +/** + * @brief A item in a interactive menu * * It's intended to be used with display_imenu(). */ struct imenu_T { char *message; ///< prompt for the user - int size; ///< max number of characters required - input_f crit; ///< function that check the validity of a character - char *buffer; ///< place where read character are stored + int size; ///< max number of characters required + input_f crit; ///< function that check the validity of a character + char *buffer; ///< place where read character are stored }; int input(WINDOW *win, char *buffer, int size, input_f crit); diff --git a/include/file.h b/include/file.h @@ -1,3 +1,10 @@ +/** + * @file file.h + * @author Dimitrije Dobrota + * @date 19 June 2022 + * @brief File interface + */ + #ifndef FILE_H #define FILE_H diff --git a/include/game.h b/include/game.h @@ -1,3 +1,10 @@ +/** + * @file game.h + * @author Dimitrije Dobrota + * @date 19 June 2022 + * @brief Game interface + */ + #ifndef GAME_H #define GAME_H diff --git a/include/main.h b/include/main.h @@ -1,9 +1,6 @@ #ifndef MAIN_H #define MAIN_H - -extern struct window_T *menu_w; - void save(void); void load_pattern(void); void save_pattern(void); diff --git a/include/pattern.h b/include/pattern.h @@ -1,10 +1,17 @@ +/** + * @file pattern.h + * @author Dimitrije Dobrota + * @date 19 June 2022 + * @brief Pattern interface and types + */ + #ifndef PATTERN_H #define PATTERN_H #include "stdlib.h" /** - * @brief Structure representing one cell pattern to be used in a help menu + * @brief Structure representing one cell pattern to be used in the help menu */ typedef struct pattern_T { char *cells; ///< string where 1 represents a living cell and 0 a dead one, @@ -14,6 +21,7 @@ typedef struct pattern_T { int width; ///< pattern width , used for cache, no need to fill manually } * pattern_T; +/// Game title using cells struct pattern_T title = { "011111000000000000000000000000000000000000000000010000000000000000000000 " "100000100011000100001011111100000111100111111000010000000101111110111111 " @@ -27,6 +35,7 @@ struct pattern_T title = { 0, }; +/// Game keybindings struct pattern_T games[] = { { "", "Game", 0, 0}, { "p", "play/pause", 0, 0}, @@ -47,12 +56,13 @@ struct pattern_T games[] = { { "q", "cancel", 0, 0}, }; +/// Still cell patterns struct pattern_T stills[] = { { "11 11", "block", 0, 0}, { "1011 1101", "Snake", 0, 0}, { "010 101 010", "Tub", 0, 0}, { "110 101 010", "Boat", 0, 0}, - { "0110 1001 0110", "Behive", 0, 0}, + { "0110 1001 0110", "Beehive", 0, 0}, { "1100 1001 0011", "Carrier", 0, 0}, { "11000 10011 01101", "Shillelagh", 0, 0}, { "110 1001 1001 0110", "Pond", 0, 0}, @@ -62,6 +72,7 @@ struct pattern_T stills[] = { {"00100 01010 01010 11011", "Hat", 0, 0}, }; +/// Oscillator cell patterns struct pattern_T oscillators[] = { { "0111 1110", "Toad", 0, 0}, { "010 010 010", "Blinker", 0, 0}, @@ -72,6 +83,7 @@ struct pattern_T oscillators[] = { {"11000011 10100101 00100100 10100101 11000011", "Spark coil", 0, 0}, }; +/// Spaceship cell patterns struct pattern_T spaceships[] = { { "010 001 111", "Glider", 0}, { "01001 10000 10001 11110", "Lightweight", 0}, @@ -79,6 +91,7 @@ struct pattern_T spaceships[] = { {"0001100 0100001 1000000 1000001 1111110", "Heavyweight", 0}, }; +/// Methuselah cell patterns struct pattern_T methuselahs[] = { { "111 101 101", "Piheptomino", 0}, { "1011 1110 0100", "B-Heptomino", 0}, @@ -97,6 +110,7 @@ typedef struct pattern_group_T { int size; ///< number of elements in a group } pattern_group_T; +/// Pattern group containing all of the patterns for the help menu struct pattern_group_T pattern_groups[] = { {"Key bindings", games, sizeof(games) / sizeof(struct pattern_T)}, { "Still", stills, sizeof(stills) / sizeof(struct pattern_T)}, @@ -105,8 +119,10 @@ struct pattern_group_T pattern_groups[] = { { "Methuselah", methuselahs, sizeof(methuselahs) / sizeof(struct pattern_T)}, }; +/// Size of pattern_groups int pattern_groups_s = sizeof(pattern_groups) / sizeof(pattern_group_T); +/// Get the pattern height int pattern_height(pattern_T pattern) { int count = 0; for (char *p = pattern->cells; *p != '\0'; p++) @@ -116,6 +132,7 @@ int pattern_height(pattern_T pattern) { return count; } +/// Get the pattern width int pattern_width(pattern_T pattern) { int count = 0; for (char *p = pattern->cells; *p != ' ' && *p != '\0'; p++) diff --git a/include/utils.h b/include/utils.h @@ -1,38 +1,38 @@ +/** + * @file utils.h + * @author Dimitrije Dobrota + * @date 19 June 2022 + * @brief Common macros for use in other modules + */ + #ifndef UTILS_H #define UTILS_H -#include <curses.h> - #include "display.h" -#define MAX(a, b) ((a > b) ? a : b) -#define MIN(a, b) ((a < b) ? a : b) -#define CLAMP(a, x, y) ((a) = (MAX(x, MIN(a, y)))) +/// maximum of the two numbers +#define MAX(a, b) ((a > b) ? a : b) + +/// minimum of the two numbers +#define MIN(a, b) ((a < b) ? a : b) + +/// clamp the number a between x and y +#define CLAMP(a, x, y) ((a) = (MAX(x, MIN(a, y)))) + +/// clamp the value of a between x and y without assignment #define ACLAMP(a, x, y) (MAX(x, MIN(a, y))) -#define WCLAMP(a, x) ((a + x) % x) - -#ifdef _WIN32 -#define is_term_resized(a, b) is_termresized() -#define usleep(a) _sleep(0.5) -#define HANDLE_RESIZE \ - { \ - resize_term(0, 0); \ - handle_winch(10); \ - } -#else -#define HANDLE_RESIZE \ - { handle_winch(10); } -#endif +/// clamp with wrapping +#define WCLAMP(a, x) ((a + x) % x) + +/// Check if compiled with Unicode support #ifdef NO_UNICODE #define UNICODE 0 #else #define UNICODE 1 #endif -#define MAX_SCREEN_H 512 -#define MAX_SCREEN_W 1888 - +/// Check for memory error, and abort #define MEM_CHECK(x) \ if ((x) == NULL) { \ display_stop(); \ @@ -40,6 +40,7 @@ abort(); \ } +/// Check for file error, and abort #define FILE_CHECK(x) \ if ((x) == NULL) { \ display_stop(); \ diff --git a/include/window.h b/include/window.h @@ -1,3 +1,10 @@ +/** + * @file window.h + * @author Dimitrije Dobrota + * @date 19 June 2022 + * @brief Window interface + */ + #ifndef WINDOW_H #define WINDOW_H @@ -23,14 +30,14 @@ void window_settings(WINDOW *win); void window_unsplit(T self); void window_update_children(T self); -// Setters and Gettern +// Setters and Getters T window_sibiling(T self); WINDOW *window_win(T self); int window_height(T self); -int window_wight(T self); +int window_width(T self); int window_x(T self); int window_y(T self); void window_set_title(T self, char *title); diff --git a/src/display.c b/src/display.c @@ -7,7 +7,7 @@ * This file is the main link with ncurses interface. It is responsible for * starting and stopping ncurses library as well as managing it's resources and * handling terminal resize by rebuilding window_T binary tree. It exports - * window_T MAIN_w as the entery point for other windows and disiplay calls. It + * window_T MAIN_w as the entry point for other windows and display calls. It * handles the display of menus and user input. */ #include <curses.h> @@ -180,7 +180,7 @@ void display_title(window_T wind, int y) { title.height = (!title.height) ? pattern_height(&title) : title.height; title.width = (!title.width) ? pattern_width(&title) : title.width; - int max_w = window_wight(wind); + int max_w = window_width(wind); if (title.width * 2 < max_w) print_pattern(win, &title, &y, (max_w - title.width * 2) / 2); wrefresh(win); @@ -391,7 +391,7 @@ void display_patterns(window_T wind) { redraw:; int CLINES = LINES, CCOLS = COLS; WINDOW *win = window_win(wind); - int ph = window_height(wind), pw = window_wight(wind); + int ph = window_height(wind), pw = window_width(wind); int y, x, maxi, max_x; x = x_start; diff --git a/src/file.c b/src/file.c @@ -5,7 +5,7 @@ * @brief This file contains functions for handling save files * * This file aims to provide a simple interface for interacting with the - * filesystem on Linux and Windows systems. After proper game directory has been + * 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. diff --git a/src/game.c b/src/game.c @@ -219,7 +219,7 @@ int display_select(window_T wind) { new = win; } - int ph = window_height(wind), pw = window_wight(wind) / 2; + int ph = window_height(wind), pw = window_width(wind) / 2; nodelay(stdscr, 0); while (TRUE) { int start_i = MIN(cursor_offset_y, current_offset_y); @@ -276,7 +276,7 @@ int display_select(window_T wind) { toggleAt(cord(y_at(i)), cord(x_at(j))); goto end; - // confirm and save slection + // confirm and save selection case '\n': for_each_cell(start_i, end_i + 1, start_j, end_j + 1) saveCell(cord(y_at(i)), cord(x_at(j))); @@ -328,7 +328,7 @@ end:; * This function is interactive: * - Use p to play/pause the simulation * - Use the arrow keys to move the screen around - * - Use -/+ to decrease or increase the nums of evolutions before displaying + * - Use -/+ to decrease or increase the numbs of evolutions before displaying * change * - Use [/] to decrease or increase time wait before update * - Use q or esc to return to the main menu @@ -377,11 +377,11 @@ redraw:; if (!wrap) { game_w = window_center(screen_w, window_height(screen_w), - window_wight(screen_w), mode_name); + window_width(screen_w), mode_name); } WINDOW *game_W = window_win(game_w); - win_height = window_height(game_w), win_width = window_wight(game_w) / 2; + win_height = window_height(game_w), win_width = window_width(game_w) / 2; window_clear(menu_w); window_clear(screen_w); @@ -466,7 +466,7 @@ redraw:; gen_step--; break; - // change refreshrate + // change refresh rate case ']': time_const += time_step; break; @@ -626,7 +626,6 @@ redraw:; } end_t = clock(); } - flushinp(); } end:; window_unsplit(menu_w); diff --git a/src/window.c b/src/window.c @@ -8,7 +8,7 @@ * tree of windows used for recalculating the ncurses WINDOW position in * dimension when the terminal has been resized. It's possible to "split" the * current window in two using a given ration or fixed size, as well as create - * a centered subwindow. It makes some of the tree data accessible via function + * a centered sub-window. It makes some of the tree data accessible via function * so that other functions can know about the sizes and adapt to them. */ @@ -47,9 +47,9 @@ struct window_T { }; /** - * @breif Apply settings, draw a border, print title for a give window + * @brief Apply settings, draw a border, print title for a give window * - * @param title: title to be prinded unless NULL + * @param title: title to be printed unless NULL */ void WINDOW_init(WINDOW *win, char *title) { window_settings(win); @@ -86,8 +86,8 @@ T window_sibiling(T self) { return self->sibling; } /// Getter: return the height of the current window int window_height(T self) { return H(self) - 2; } -/// Getter: return the width of a current window -int window_wight(T self) { return W(self) - 2; } +/// Getter: return the of a current window +int window_width(T self) { return W(self) - 2; } /// Getter: return the x coordinate of the current window int window_x(T self) { return X(self); } @@ -104,7 +104,7 @@ void window_set_title(T self, char *title) { self->title = title; } /** * @brief create new ncurses WINDOW to the specification with default settings * - * New ncruses WINDOW is created with newwin and it should be deleted with + * New ncurses WINDOW is created with newwin and it should be deleted with * delwin. Setting are applied with the call to WINDOW_INIT */ WINDOW *window_win_new(T self) { @@ -148,7 +148,7 @@ void window_free(T self) { } /** - * @brief Delete all children and subchildren of a window + * @brief Delete all children and sub-children of a window */ void window_unsplit(T self) { window_free(self->c1); @@ -162,7 +162,7 @@ void window_unsplit(T self) { * children * * This function takes care of calculation for two windows in a given ration or - * fixed size, and single subwindow. + * fixed size, and single sub-window. * If a given windows has 2 children then they are split with the following * rule: * - If mod[0] is 0, two created windows will be side by side @@ -234,7 +234,7 @@ void window_calc_children(T self) { /** * @brief Split the window in a given ration into two windows with a name * - * @param hor: should the split line be horizontal or verical + * @param hor: should the split line be horizontal or vertical * @param a: ration of the first window * @param b: ration of the second window * @param name1: name of the first window @@ -270,11 +270,11 @@ T window_split(T self, int hor, int a, int b, char *name1, char *name2) { } /** - * @brief Create a subwindow that will be of a given size or made to fit it's + * @brief Create a sub-window that will be of a given size or made to fit it's * parent * * @param h: desired height of a new window_T - * @param w: desired width of a new window_T + * @param w: desired of a new window_T * @param name: name of the new window_T */ T window_center(T self, int h, int w, char *name) { @@ -294,7 +294,7 @@ T window_center(T self, int h, int w, char *name) { } /** - * @brief Recursevly recalculate the size of each child and make new ncurses + * @brief Recursively recalculate the size of each child and make new ncurses * WINDOW */ void window_update_children(T self) { @@ -343,7 +343,7 @@ void window_settings(WINDOW *win) { } /** - * @brief Check if cordinates are in the window + * @brief Check if coordinates are in the window */ int window_clicked(T self, int y, int x) { if (x <= X(self) || y <= Y(self)) @@ -360,7 +360,7 @@ int window_clicked(T self, int y, int x) { * text of len n will be centered * * @param y: y position - * @n lenfth of the text to be centered + * @n length of the text to be centered */ void wcenter_horizontal(T window, int y, int n) { wmove(window->win, y, (W(window) - n - 2) / 2 + 1);