gol

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

commit4c83da5de9856f5b83d30567244a718f9b56fa4d
parent7681e2df2437464549b9e6144beb5d7bd837671b
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateMon, 23 May 2022 16:44:19 +0200

Integration with the logic module - All game state is handled by logic.c - Improved gameplay: - Pause/Play - Quit - Speed control - Cell toggle with a movable cursor - Improved game view: - Player area is now outlined - Unicode characters work on Windows Terminal - Self centering and resizeing subwindow - Improved game framerate: - Constant time between each update - Smoother movement

Diffstat:
Minclude/display.h|++++++--
Minclude/file.h|+-
Minclude/logic.h|++++--
Minclude/utils.h|+++++++--
Minclude/window.h|+
Msrc/display.c|+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/file.c|+-
Msrc/logic.c|++++++++-----
Msrc/main.c|+++++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/window.c|+++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------

10 files changed, 292 insertions(+), 198 deletions(-)


diff --git a/include/display.h b/include/display.h

@@ -2,13 +2,14 @@

#define DISPLAY_H
#include "window.h"
#include "logic.h"
extern window_T MAIN_w;
typedef int (*input_f)(int);
struct menu_T {
void (*callback)(window_T, char *);
void (*callback)(window_T, char *, int);
char *name;
};

@@ -25,7 +26,10 @@ int display_start(void);

int display_stop(void);
void display_menu(window_T win, struct menu_T *items, int size);
int display_imenu(window_T wind, struct imenu_T *items, int size);
int display_imenu(window_T wind, struct imenu_T *items, int size);
void display_game(window_T wind, cell **mat, int w, int h, int screen_offset_y,
int screen_offset_x, int *cursor_offset_y,
int *cursor_offset_x);
void handle_winch(int sig);

diff --git a/include/file.h b/include/file.h

@@ -8,6 +8,6 @@ void load_files(void);

void free_files(void);
int file_select(char *ext, char ***buffer);
void load_file(window_T win, char *pass);
void load_file(window_T win, char *pass, int index);
#endif

diff --git a/include/logic.h b/include/logic.h

@@ -4,11 +4,13 @@

#define cell unsigned char
extern cell **mat;
extern char **evolution_names[];
extern char *evolution_names[];
extern int evolution_cells[];
extern int evolution_size;
int logic_init(int w, int h);
int evolution_init(int index);
void do_evolution(int steps);
int logic_free(void);
#endif
\ No newline at end of file
#endif

diff --git a/include/utils.h b/include/utils.h

@@ -5,7 +5,8 @@

#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)))
#define CLAMP(a, x, y) ((a) = (MAX(x, MIN(a, y))))
#define ACLAMP(a, x, y) (MAX(x, MIN(a, y)))
#ifdef _WIN32
#define is_term_resized(a, b) is_termresized()

@@ -17,6 +18,10 @@

goto redraw; \
}
#else
#define HANDLE_RESIZE goto redraw;
#define HANDLE_RESIZE \
{ \
handle_winch(10); \
goto redraw; \
}
#endif
#endif

diff --git a/include/window.h b/include/window.h

@@ -10,6 +10,7 @@ T window_new(void);

void window_free(T self);
T window_init(T self);
T window_split(T self, int hor, int a, int b);
T window_center(T self, int tmp, int h, int w);
void window_update_children(T self);
T window_sibiling(T self);

diff --git a/src/display.c b/src/display.c

@@ -4,12 +4,18 @@

#include <string.h>
#include "display.h"
#include "logic.h"
#include "utils.h"
#include "window.h"
#define center_vertical(n) wcenter_vertical(MAIN_W, n);
#define center_horizontal(y, n) wcenter_horizontal(MAIN_W, y, n);
#define CHAR_CIRCLE L'\u26AB'
#define CHAR_SQUARE L'\u2B1B'
#define CHAR_SQUARED L'\u2B1C'
#define CHAR_CIRCLE_DOT L'\u2299'
window_T MAIN_w = NULL;
int input(WINDOW *win, char *buffer, int size, input_f crit) {

@@ -59,7 +65,8 @@ int display_imenu(window_T wind, struct imenu_T *items, int size) {

int current = 0;
for (int i = 0; i < size; i++)
items[i].buffer = calloc(5, sizeof(char));
if (!items[i].buffer)
items[i].buffer = calloc(5, sizeof(char));
int maxi = 0, len = 0;
for (int i = 0; i < size; i++)

@@ -146,7 +153,7 @@ redraw:;

} else if (c == '\n') {
wattrset(win, COLOR_PAIR(0));
window_clear(wind);
items[current].callback(wind, items[current].name);
items[current].callback(wind, items[current].name, current);
/* window_clear(wind); */
return;
} else if (c == 27)

@@ -159,8 +166,56 @@ redraw:;

}
}
#define y_at(y) (y + screen_offset_y + h) % h + 1
#define x_at(x) (x + screen_offset_x + w) % w + 1
void display_game(window_T wind, cell **mat, int w, int h, int screen_offset_y,
int screen_offset_x, int *cursor_offset_y,
int *cursor_offset_x) {
WINDOW *win = window_win(wind);
wattrset(win, COLOR_PAIR(0));
int ph = window_height(wind), pw = window_wight(wind) / 2;
#ifdef _WIN32
window_clear(wind);
#endif
for (int i = 0; i < ph; i++) {
wmove(win, i + 1, 1);
for (int j = 0; j < pw; j++) {
wattrset(win, COLOR_PAIR((mat[y_at(i)][x_at(j)]) + 2));
if (mat[y_at(i)][x_at(j)])
waddstr(win, "\u26AB");
else
waddstr(win, " ");
}
}
CLAMP(*cursor_offset_y, 0, ph - 1);
CLAMP(*cursor_offset_x, 0, pw - 1);
wmove(win, *cursor_offset_y + 1, *cursor_offset_x * 2 + 1);
wattrset(win, COLOR_PAIR(
(mat[y_at(*cursor_offset_y)][x_at(*cursor_offset_x)]) + 5));
if (mat[y_at(*cursor_offset_y)][x_at(*cursor_offset_x)])
waddstr(win, "\u26AB");
else
waddstr(win, " ");
wrefresh(win);
}
#define LMAX 8
#define LEFT 0
#define CENTER 1
#define RIGHT 2
void curses_start(void) {
initscr();
window_settings(stdscr);
start_color();
use_default_colors();

@@ -168,13 +223,16 @@ void curses_start(void) {

noecho();
nodelay(stdscr, 1);
window_settings(stdscr);
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);
init_pair(2, COLOR_WHITE, -1);
init_pair(3, COLOR_WHITE, -1);
init_pair(4, COLOR_RED, -1);
init_pair(5, COLOR_WHITE, COLOR_CYAN);
init_pair(6, COLOR_WHITE, COLOR_CYAN);
init_pair(7, COLOR_RED, COLOR_CYAN);
}
void curses_stop(void) {

@@ -187,15 +245,14 @@ void handle_winch(int sig) {

refresh();
clear();
refresh();
window_init(MAIN_w);
window_update_children(MAIN_w);
}
int display_start(void) {
#ifndef _WIN32
signal(SIGWINCH, handle_winch);
#endif
/* #ifndef _WIN32 */
/* signal(SIGWINCH, handle_winch); */
/* #endif */
curses_start();
MAIN_w = window_init(window_new());

diff --git a/src/file.c b/src/file.c

@@ -132,4 +132,4 @@ int file_select(char *ext, char ***buffer) {

void load_files(void) { loaded_files = file_fromDirectory(); }
void free_files(void) { file_free(loaded_files); }
void load_file(window_T win, char *pass) { (void)3; }
void load_file(window_T win, char *pass, int index) { (void)3; }

diff --git a/src/logic.c b/src/logic.c

@@ -5,11 +5,11 @@

#include "utils.h"
cell **mat;
char **evolution_names[] = {"Normal", "CoExsistance", "Predator", "Virus",
"Unknown"};
char *evolution_names[] = {"Normal", "CoExsistance", "Predator", "Virus",
"Unknown"};
int evolution_cells[] = {2, 3, 3, 3, 3};
int evolution_size = 5;
static void (*evolution_modes[])() = {
evolveNormal, evolveCoExist, evolvePredator, evolveVirus, evolveUnknown};
static void (*evolve)(void);
static int height, width;
static int mod;

@@ -225,6 +225,9 @@ int logic_init(int w, int h) {

return 1;
}
static void (*evolution_modes[])() = {
evolveNormal, evolveCoExist, evolvePredator, evolveVirus, evolveUnknown};
int evolution_init(int index) {
evolve = evolution_modes[index];
return 1;

@@ -235,4 +238,4 @@ int logic_free(void) {

free(mat[i]);
free(mat);
return 1;
}
\ No newline at end of file
}

diff --git a/src/main.c b/src/main.c

@@ -1,7 +1,3 @@

// This is not a final version of the code
// While I've cleaned this file up,
// all of the game logic is still here
#include <ctype.h>
#include <curses.h>
#include <locale.h>

@@ -10,154 +6,142 @@

#include <unistd.h>
#include <wchar.h>
#include <time.h>
#include "display.h"
#include "file.h"
#include "logic.h"
#include "utils.h"
#include "window.h"
extern window_T MAIN_w;
WINDOW *game_w, *board_w, *status_w;
int top_space = 5;
void show(window_T wind, unsigned **univ, int w, int h, int y, int x) {
WINDOW *win = window_win(wind);
wattrset(win, COLOR_PAIR(0));
int ph = h, pw = w, mh, mw;
mh = window_height(wind);
mw = window_wight(wind) / 2;
CLAMP(ph, 0, mh);
CLAMP(pw, 0, mw);
int top = wcenter_vertical(wind, ph);
for (int i = 0; i < ph; i++) {
wcenter_horizontal(wind, top + i, pw * 2);
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, " ");
#define TIME_CONST 100
#else
if (univ[(i + y + h) % h][(j + x + w) % w])
wprintw(win, "%lc ", (wchar_t)L'\u23FA');
else
wprintw(win, " ");
/* wprintw(win, "%lc", (wchar_t)L'\u2B1B'); */
/* wprintw(win, " ", (wchar_t)L'\u2B1C'); */
#define TIME_CONST 100000
#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(window_T wind, int w, int h) {
if (w <= 0 || h <= 0) {
h = window_height(wind);
w = window_wight(wind);
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;
}
extern window_T MAIN_w;
extern cell **mat;
extern char *evolution_names[];
extern int evolution_cells[];
extern int evolution_size;
unsigned **new = malloc(h * sizeof(unsigned *));
for (int i = 0; i < h; i++) {
new[i] = malloc(w * sizeof(unsigned));
}
window_T status_w, screen_w, game_w;
int top_space = 5;
int oy = 0, ox = 0;
void game(window_T wind, int w, int h, int ncells) {
int screen_offset_y = 0, screen_offset_x = 0;
int cursor_offset_y = 0, cursor_offset_x = 0;
int in = 3;
int gen_step = 1;
int play = 0;
int gen = 1;
window_clear(wind);
game_w = window_center(wind, 0, h, w * 2);
clock_t start_t, end_t = 0, total_t;
redraw:;
/* window_clear(wind); */
int CLINES = LINES, CCOLS = COLS;
while (TRUE) {
show(wind, univ, w, h, oy, ox);
evolve(univ, new, w, h, gen_step);
for (int i = 0; i < in; i++) {
start_t = clock();
display_game(game_w, mat, w, h, screen_offset_y, screen_offset_x,
&cursor_offset_y, &cursor_offset_x);
if (play) {
do_evolution(gen_step);
gen++;
}
while ((total_t = (long int)(end_t - start_t)) < TIME_CONST) {
refresh();
int c = getch();
switch (c) {
case 'p':
case 'P':
play = !play;
break;
case 27:
return;
case '+':
gen_step++;
break;
case '-':
gen_step--;
break;
case KEY_A1:
case KEY_C1:
case KEY_END:
case KEY_HOME:
case KEY_LEFT:
ox--;
screen_offset_x--;
break;
case KEY_A3:
case KEY_C3:
case KEY_NPAGE:
case KEY_PPAGE:
case KEY_RIGHT:
ox++;
screen_offset_x++;
break;
}
switch (c) {
case KEY_A1:
case KEY_A3:
case KEY_HOME:
case KEY_PPAGE:
case KEY_UP:
oy--;
screen_offset_y--;
break;
case KEY_C1:
case KEY_C3:
case KEY_DOWN:
case KEY_END:
case KEY_NPAGE:
oy++;
screen_offset_y++;
}
break;
switch (c) {
case 27:
return;
if (!play) {
switch (c) {
case 'w':
case 'W':
cursor_offset_y--;
break;
case 's':
case 'S':
cursor_offset_y++;
break;
case 'a':
case 'A':
cursor_offset_x--;
break;
case 'd':
case 'D':
cursor_offset_x++;
break;
case ' ':
mat[(cursor_offset_y + screen_offset_y + h) % h + 1]
[(cursor_offset_x + screen_offset_x + w) % w + 1] =
(mat[(cursor_offset_y + screen_offset_y + h) % h + 1]
[(cursor_offset_x + screen_offset_x + w) % w + 1] +
1) %
ncells;
break;
}
}
screen_offset_x = (screen_offset_x + w) % w;
screen_offset_y = (screen_offset_y + h) % h;
CLAMP(gen_step, 1, 10);
/* usleep(100000); */
if (is_term_resized(CLINES, CCOLS)) {
flushinp();
HANDLE_RESIZE;
goto redraw;
}
end_t = clock(); // clock stopped
}
ox = (ox + w) % w;
oy = (oy + h) % h;
flushinp();
usleep(200000);
if (is_term_resized(CLINES, CCOLS)) {
HANDLE_RESIZE;
goto redraw;
}
}
}

@@ -167,18 +151,43 @@ struct imenu_T imenu_items[] = {

};
int imenu_items_s = sizeof(imenu_items) / sizeof(struct imenu_T);
void settings(window_T wind, char *pass) {
if (display_imenu(wind, imenu_items, imenu_items_s)) {
int row = atoi(imenu_items[0].buffer);
int column = atoi(imenu_items[1].buffer);
game(wind, row, column);
void settings(window_T wind, char *pass, int index) {
while (TRUE) {
if (display_imenu(wind, imenu_items, imenu_items_s)) {
int row = atoi(imenu_items[0].buffer);
int column = atoi(imenu_items[1].buffer);
if (!row || !column)
continue;
logic_init(column, row);
evolution_init(index);
game(wind, row, column, evolution_cells[index]);
break;
} else {
break;
}
}
for (int i = 0; i < imenu_items_s; i++)
for (int i = 0; i < imenu_items_s; i++) {
free(imenu_items[i].buffer);
imenu_items[i].buffer = NULL;
}
}
void mode_select(window_T wind, char *pass, int index) {
struct menu_T *mode_items = malloc(evolution_size * sizeof(struct menu_T));
for (int i = 0; i < evolution_size; i++) {
mode_items[i].name = evolution_names[i];
mode_items[i].callback = settings;
}
display_menu(wind, mode_items, evolution_size);
free(mode_items);
}
void load(window_T wind, char *pass) {
void load(window_T wind, char *pass, int index) {
char **buffer;
int n;

@@ -193,55 +202,44 @@ void load(window_T wind, char *pass) {

free(buffer);
display_menu(wind, file_items, n);
game(wind, 0, 0);
mode_select(wind, "nothing", 0);
free(file_items);
free_files();
}
void exitp(window_T wind, char *pass) {
void exitp(window_T wind, char *pass, int index) {
display_stop();
exit(0);
}
struct menu_T mode_items[] = {
{settings, "Normalan"},
{settings, "Koegzistencija"},
{settings, "Predator i pljen"},
{settings, "Virus"},
{settings, "Nepoznanost"},
};
int mode_items_s = sizeof(mode_items) / sizeof(struct menu_T);
void mode_select(window_T wind, char *pass) {
display_menu(wind, mode_items, mode_items_s);
}
struct menu_T menu_items[] = {
{mode_select, "Start"},
{ load, "Load"},
{ exitp, "Exit"}
};
int menu_items_s = sizeof(menu_items) / sizeof(struct menu_T);
int state = 0;
int main(void) {
setlocale(LC_ALL, "");
if (!display_start()) {
printf("Couldn't start the display!\n");
}
if (!file_setup()) {
printf("File setup error\n");
}
T t1, t2;
t1 = window_split(MAIN_w, 1, 5, 0);
t2 = window_sibiling(t1);
status_w = window_split(MAIN_w, 1, 3, 0);
screen_w = window_sibiling(status_w);
while (TRUE) {
display_menu(t2, menu_items, menu_items_s);
window_clear(t2);
display_menu(screen_w, menu_items, menu_items_s);
window_clear(screen_w);
}
if (!display_stop()) {

diff --git a/src/window.c b/src/window.c

@@ -2,6 +2,7 @@

#include <stdlib.h>
#include <string.h>
#include "utils.h"
#include "window.h"
#define H(c) c->param[0]

@@ -52,6 +53,7 @@ int window_wight(T self) { return W(self) - 2; }

WINDOW *window_win(T self) { return self->win; }
T window_init(T self) {
self->win = stdscr;
box(self->win, ACS_VLINE, ACS_HLINE);
wrefresh(self->win);

@@ -69,54 +71,61 @@ void window_free(T self) {

free(self);
}
void window_calc(T self) {
void window_calc_children(T self) {
T c1, c2, f, nf;
c1 = self->c1;
c2 = self->c2;
int fixed = 0, *fv;
int hor = self->mod[0], a = self->mod[1], b = self->mod[2];
if (a == 0 && b == 0)
return;
if (c2 != NULL) {
if (a == 0 && b == 0)
return;
if (a == 0) {
f = c2;
nf = c1;
fv = &b;
fixed = 1;
} else if (b == 0) {
f = c1;
nf = c2;
fv = &a;
fixed = 1;
}
if (a == 0) {
f = c2;
nf = c1;
fv = &b;
fixed = 1;
} else if (b == 0) {
f = c1;
nf = c2;
fv = &a;
fixed = 1;
}
if (hor) {
W(c1) = W(c2) = W(self) - 2;
if (hor) {
W(c1) = W(c2) = W(self) - 2;
if (!fixed) {
H(c1) = ((H(self) - 2) / (a + b)) * a;
} else {
H(f) = *fv;
}
H(nf) = (H(self) - 2) - H(f);
if (!fixed) {
H(c1) = ((H(self) - 2) / (a + b)) * a;
X(c1) = X(c2) = X(self) + 1;
Y(c1) = Y(self) + 1;
Y(c2) = Y(self) + H(c1) + 1;
} else {
H(f) = *fv;
H(c1) = H(c2) = H(self) - 2;
if (!fixed) {
W(c1) = ((W(self) - 2) / (a + b)) * a;
} else {
W(f) = *fv;
}
W(nf) = (W(self) - 2) - W(f);
Y(c1) = Y(c2) = Y(self) + 1;
X(c1) = X(self) + 1;
X(c2) = X(self) + W(c1) + 1;
}
H(nf) = (H(self) - 2) - H(f);
X(c1) = X(c2) = X(self) + 1;
Y(c1) = Y(self) + 1;
Y(c2) = Y(self) + H(c1) + 1;
} else {
H(c1) = H(c2) = H(self) - 2;
if (!fixed) {
W(c1) = ((W(self) - 2) / (a + b)) * a;
} else {
W(f) = *fv;
}
W(nf) = (W(self) - 2) - W(f);
Y(c1) = Y(c2) = Y(self) + 1;
X(c1) = X(self) + 1;
X(c2) = X(self) + W(c1) + 1;
H(c1) = ACLAMP(a + 2, 0, H(self));
W(c1) = ACLAMP(b + 2, 0, W(self));
Y(c1) = Y(self) + (H(self) - H(c1)) / 2;
X(c1) = X(self) + (W(self) - W(c1)) / 2;
}
}

@@ -124,9 +133,6 @@ T window_split(T self, int hor, int a, int b) {

self->c1 = window_new();
self->c2 = window_new();
self->c1 = window_new();
self->c2 = window_new();
self->c1->sibiling = self->c2;
self->c2->sibiling = self->c1;

@@ -134,7 +140,7 @@ T window_split(T self, int hor, int a, int b) {

self->mod[1] = a;
self->mod[2] = b;
window_calc(self);
window_calc_children(self);
WINDOW_new(self->c1);
WINDOW_new(self->c2);

@@ -142,17 +148,35 @@ T window_split(T self, int hor, int a, int b) {

return self->c1;
}
T window_center(T self, int tmp, int h, int w) {
self->c1 = window_new();
self->c2 = NULL;
self->mod[0] = tmp;
self->mod[1] = h;
self->mod[2] = w;
window_calc_children(self);
WINDOW_new(self->c1);
return self->c1;
}
void window_update_children(T self) {
if (self == NULL || self->c1 == NULL || self->c2 == NULL)
if (self == NULL || self->c1 == NULL)
return;
window_calc_children(self);
delwin(self->c1->win);
delwin(self->c2->win);
window_calc(self);
WINDOW_new(self->c1);
WINDOW_new(self->c2);
window_update_children(self->c1);
window_update_children(self->c2);
if (self->c2 != NULL) {
delwin(self->c2->win);
WINDOW_new(self->c2);
window_update_children(self->c2);
}
}
void window_clear(T self) {