gol

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

commit dd16dc66dd4b3fccc27c3f9c4f71808eeaec0b7d
parent 9cb39cdd7dcd991479341a26b6659adb9e21c0a9
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Sun, 15 May 2022 23:01:44 +0200

God give us health!

This is a quick proof of concept

Diffstat:
A.gitignore | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AMakefile | 25+++++++++++++++++++++++++
Abin/dir.info | 0
Ainclude/dir.info | 0
Aobj/dir.info | 0
Asrc/dir.info | 0
Asrc/main.c | 469+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 553 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,59 @@ +.ccls-cache/ +.ccls +.clang-format + +bin/* +!bin/dir.info + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/Makefile b/Makefile @@ -0,0 +1,25 @@ +CFLAGS = -I include +LDFLAGS = -lncursesw + +SRC = src +OBJ = obj +BINDIR = bin + +BIN = bin/gol +SRCS=$(wildcard $(SRC)/*.c) +OBJS=$(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SRCS)) + +CFLAGS +=-ggdb -Wall + +all: ${BIN} + +$(BIN): $(OBJS) + ${CC} $^ $(CFLAGS) $(LDFLAGS) -o $@ + +$(OBJ)/%.o: $(SRC)/%.c + ${CC} -c $< -o $@ $(CFLAGS) + +clean: + rm -r $(BINDIR)/* $(OBJ)/* + +.PHONY: all clean diff --git a/bin/dir.info b/bin/dir.info diff --git a/include/dir.info b/include/dir.info diff --git a/obj/dir.info b/obj/dir.info diff --git a/src/dir.info b/src/dir.info diff --git a/src/main.c b/src/main.c @@ -0,0 +1,469 @@ +// This is not a final version of the code +// This is just a quick proof of concept +// Many things need to be addressed later + +#include <ctype.h> +#include <curses.h> +#include <locale.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include <dirent.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +WINDOW *game_w, *board_w, *status_w; +int top_space = 5; + +typedef struct file_T *file_T; +struct file_T { + file_T next; + char *name; +}; + +file_T file_new(char *name) { + file_T f; + f = malloc(sizeof(*f)); + f->next = NULL; + if (name == NULL) { + f->name = NULL; + return f; + } + + f->name = malloc((strlen(name) + 1) * sizeof(char)); + strcpy(f->name, name); + return f; +} + +void file_free(file_T self) { + if (self == NULL) + return; + file_free(self->next); + free(self->name); + free(self); +} + +file_T file_add(file_T self, char *name) { + for (; self->next != NULL; self = self->next) + ; + self->next = file_new(name); + return self->next; +} + +void file_list(file_T self) { + for (; self != NULL; self = self->next) + printw("%s ", self->name); +} + +int DirectoryExists(const char *path) { + struct stat stats; + stat(path, &stats); + + return S_ISDIR(stats.st_mode); +} + +file_T file_fromDirectory(void) { + file_T base = file_new(NULL); + + struct dirent *de; + DIR *dr = opendir("."); + if (dr == NULL) + return NULL; + + move(0, 0); + while ((de = readdir(dr)) != NULL) + file_add(base, de->d_name); + + file_T n = base->next; + + free(base); + free(dr); + + return n; +} + +file_T file_find(file_T self, char *name) { + for (; self != NULL; self = self->next) { + if (!strcmp(self->name, name)) + return self; + } + return NULL; +} + +struct extension_T { + char *ext; + file_T list; +}; + +void file_filter(file_T self, struct extension_T *ext, int ext_s) { + file_T *tmp = malloc(ext_s * sizeof(file_T)); + for (int i = 0; i < ext_s; i++) { + tmp[i] = ext[i].list = file_new(NULL); + } + + move(10, 0); + for (; self != NULL; self = self->next) { + char *dot = strrchr(self->name, '.'); + if (dot == NULL) + continue; + for (int i = 0; i < ext_s; i++) { + if (!strcmp(ext[i].ext, dot)) { + tmp[i] = tmp[i]->next = file_new(self->name); + break; + } + } + } + + for (int i = 0; i < ext_s; i++) { + printw("\nExtension %s: ", ext[i].ext); + file_T n = ext[i].list->next; + free(ext[i].list); + file_list(ext[i].list = n); + } + free(tmp); +} + +#ifdef _WIN32 +#define WINDOWS 1 +#define UNIX 0 +#define SETTINGS_DIR "C:\\GoL" // without trailing '\' +#define MAKE_DIR(dir) (mkdir(dir)) +#else +#define WINDOWS 0 +#define UNIX 1 +#define SETTINGS_DIR "/home/magaknuto/GoL" +#define MAKE_DIR(dir) (mkdir(dir, 0777)) +#endif // _WIN32 + +void setup(char *pass) { + if (!DirectoryExists(SETTINGS_DIR)) { + printw("%s does not exist, trying to create it....\n", SETTINGS_DIR); + if (MAKE_DIR(SETTINGS_DIR) != 0) { + printw("Unfortunately, can't create a directory: %s", SETTINGS_DIR); + getch(); + exit(1); + } + printw("Success!\n"); + } else + printw("%s already exists\n", SETTINGS_DIR); + + if (chdir(SETTINGS_DIR) != 0) { + printw("chdir(%s): not successfull!\n", SETTINGS_DIR); + getch(); + exit(1); + } else + printw("Directory has successfully been changed to %s!\n", SETTINGS_DIR); +} + +#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))) + +void cursor_offset(int oy, int ox) { + int y, x; + getyx(stdscr, y, x); + move(y + oy, x + ox); +} + +typedef int (*input_f)(int); +void input(char *buffer, int size, input_f crit) { + int ch; + int read = 0; + while ((ch = getch()) != '\n') { + switch (ch) { + case KEY_BACKSPACE: + if (read > 0) { + cursor_offset(0, -1); + delch(); + read--; + } + default: + if (read < size && crit && crit(ch)) { + printw("%c", ch); + buffer[read++] = ch; + } + } + } + printw("\n"); + buffer[read] = '\0'; +} + +void wcenter_horizontal(WINDOW *win, int y, int n) { + int mx, my; + getmaxyx(win, my, mx); + wmove(win, y, (mx - n) / 2); +} + +int wcenter_vertical(WINDOW *win, int n) { + int mx, my; + getmaxyx(win, my, mx); + return (my - n) / 2; +} +#define center_vertical(n) wcenter_vertical(stdscr, n); +#define center_horizontal(y, n) wcenter_horizontal(stdscr, y, n); + +void show(WINDOW *win, unsigned **univ, int w, int h, int y, int x) { + attrset(COLOR_PAIR(0)); + int ph = h, pw = w, mh, mw; + getmaxyx(win, mh, mw); + CLAMP(ph, 0, mh); + CLAMP(pw, 0, mw); + int top = wcenter_vertical(win, ph); + + for (int i = 0; i < ph; i++) { + wcenter_horizontal(win, top + i, pw); + for (int j = 0; j < pw; j++) { +#ifdef _WIN32 + wattrset(win, + COLOR_PAIR((univ[(i + y + h) % h][(j + x + w) % w]) ? 2 : 3)); + wprintw(win, " "); +#else + /* if (univ[(i + y + h) % h][(j + x + w) % w]) */ + /* wprintw(win, "%lc", 0x2B1B); */ + /* else */ + /* wprintw(win, " ", 0x2B1C); */ + if (univ[(i + y + h) % h][(j + x + w) % w]) + wprintw(win, "%lc", (wchar_t)L'\u2B1B'); + else + wprintw(win, " ", (wchar_t)L'\u2B1C'); +#endif + } + } + wrefresh(win); +} + +void evolve(unsigned **univ, unsigned **new, int w, int h, int step) { + do { + for (int i = 0; i < h; i++) + for (int j = 0; j < w; j++) { + int n = 0; + for (int y1 = i - 1; y1 <= i + 1; y1++) + for (int x1 = j - 1; x1 <= j + 1; x1++) + if (univ[(y1 + h) % h][(x1 + w) % w]) + n++; + + if (univ[i][j]) + n--; + new[i][j] = (n == 3 || (n == 2 && univ[i][j])); + } + unsigned **t = univ; + univ = new; + new = t; + } while (--step); + unsigned **t = univ; + univ = new; + new = t; + + for (int i = 0; i < h; i++) + for (int j = 0; j < w; j++) + univ[i][j] = new[i][j]; +} + +void game(int w, int h) { + clear(); + box(stdscr, ACS_VLINE, ACS_HLINE); + refresh(); + + box(game_w, ACS_VLINE, ACS_HLINE); + wrefresh(game_w); + + box(status_w, ACS_VLINE, ACS_HLINE); + wbkgd(status_w, '.'); + wrefresh(status_w); + + if (!w || !h) { + getmaxyx(board_w, h, w); + w /= 2; + } + + unsigned **univ; + univ = malloc(h * sizeof(unsigned *)); + for (int i = 0; i < h; i++) { + univ[i] = malloc(w * sizeof(unsigned)); + for (int j = 0; j < w; j++) + univ[i][j] = rand() < RAND_MAX / 7 ? 1 : 0; + } + + unsigned **new = malloc(h * sizeof(unsigned *)); + for (int i = 0; i < h; i++) { + new[i] = malloc(w * sizeof(unsigned)); + } + + nodelay(status_w, 1); + keypad(status_w, TRUE); + int oy = 0, ox = 0; + int in = 3; + int gen_step = 1; + while (1) { + show(board_w, univ, w, h, oy, ox); + evolve(univ, new, w, h, gen_step); + for (int i = 0; i < in; i++) { + int c = wgetch(status_w); + switch (c) { + case KEY_A1: + case KEY_C1: + case KEY_END: + case KEY_HOME: + case KEY_LEFT: + ox--; + break; + case KEY_A3: + case KEY_C3: + case KEY_NPAGE: + case KEY_PPAGE: + case KEY_RIGHT: + ox++; + break; + } + switch (c) { + case KEY_A1: + case KEY_A3: + case KEY_HOME: + case KEY_PPAGE: + case KEY_UP: + oy--; + break; + case KEY_C1: + case KEY_C3: + case KEY_DOWN: + case KEY_END: + case KEY_NPAGE: + oy++; + break; + } + } + ox = (ox + w) % w; + oy = (oy + h) % h; + flushinp(); + usleep(150000); + } +} + +void start(char *pass) { + char buffer[100]; + int row, column; + printw("Unesi broj redova: "); + input(buffer, 4, isdigit); + row = atoi(buffer); + printw("Unesi broj kolona: "); + input(buffer, 4, isdigit); + column = atoi(buffer); + game(row, column); + refresh(); +} + +void load(char *pass) { + printw("Load\n"); + getch(); +} + +void exitp(char *pass) { + endwin(); + exit(0); +} + +struct extension_T ext[] = { + { ".c", NULL}, + { ".h", NULL}, + {".part", NULL}, + { ".all", NULL} +}; +int ext_s = sizeof(ext) / sizeof(struct extension_T); + +void list(char *pass) { + file_T files = file_fromDirectory(); + file_filter(files, ext, ext_s); + for (int i = 0; i < ext_s; i++) + file_free(ext[i].list); + file_free(files); +} + +struct menu_T { + void (*callback)(char *); + char *name; +}; + +struct menu_T menu_items[] = { + {start, "Start"}, + {setup, "Setup"}, + { list, "List"}, + { load, "Load"}, + {exitp, "Exit"} +}; +int menu_items_s = sizeof(menu_items) / sizeof(struct menu_T); + +void display_menu(struct menu_T *items, int size) { + int current = 0; + + int *lens = malloc(size * sizeof(int)); + for (int i = 0; i < size; i++) + lens[i] = strlen(items[i].name); + + int y_offset = center_vertical(size * 2); + while (TRUE) { + CLAMP(current, 0, size - 1); + for (int i = 0; i < size; i++) { + attrset(COLOR_PAIR(i == current ? 1 : 0)); + center_horizontal(y_offset + i * 2, lens[i]); + printw("%s", items[i].name); + } + refresh(); + switch (getch()) { + case 'k': + case KEY_UP: + current--; + break; + case KEY_DOWN: + case 'j': + current++; + break; + case '\n': + move(16, 0); + attrset(COLOR_PAIR(0)); + items[current].callback(NULL); + } + } + free(lens); +} + +void ncurses_window_setup(void) { + game_w = newwin(LINES - 2 - top_space, COLS - 4, 1 + top_space, 2); + board_w = newwin(LINES - 4 - top_space, COLS - 6, 2 + top_space, 3); + status_w = newwin(top_space, COLS - 4, 1, 2); +} + +void ncurses_window_end(void) { + delwin(game_w); + delwin(board_w); + delwin(status_w); +} + +void ncurses_start(void) { + initscr(); + start_color(); + use_default_colors(); + curs_set(0); + noecho(); + keypad(stdscr, TRUE); + init_pair(0, COLOR_WHITE, -1); + init_pair(1, COLOR_RED, -1); + + init_pair(2, COLOR_WHITE, COLOR_WHITE); + init_pair(3, COLOR_BLACK, -1); +} + +int main(void) { + setlocale(LC_ALL, ""); + ncurses_start(); + ncurses_window_setup(); + + display_menu(menu_items, menu_items_s); + getch(); + + endwin(); + + return 0; +}