commit 4c83da5de9856f5b83d30567244a718f9b56fa4d
parent 7681e2df2437464549b9e6144beb5d7bd837671b
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Mon, 23 May 2022 18: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:
10 files changed, 292 insertions(+), 200 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) {