golImplementation of Conway's Game of Life writen in C |
git clone git://git.dimitrijedobrota.com/gol.git |
Log | Files | Refs | README | |
commit | aea5b3965a13efe4ba9a2679055229aec01ba613 |
parent | 8e6a27421fd714f1ef85cd441400e0172c603347 |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Mon, 6 Jun 2022 14:20:24 +0200 |
Update game and a tons of features - Bug fixing - Game logic split into a separate file - General performance improvement - Help screen - Main menu title - Save/Load works for a whole game or a pattern - Use new hashmap interface - Visual select works in NO_UNICODE mode
Diffstat:M | include/display.h | | | +++++-- |
A | include/game.h | | | ++++++ |
A | include/main.h | | | +++++++++++ |
A | include/pattern.h | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | include/utils.h | | | ++++++++++------ |
M | include/window.h | | | + |
M | src/display.c | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------ |
M | src/file.c | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ |
A | src/game.c | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/main.c | | | ++++++++-------------------------------------------------------------------------- |
D | src/utils.c | | | ---- |
M | src/window.c | | | +++++++++-- |
12 files changed, 761 insertions(+), 366 deletions(-)
diff --git a/include/display.h b/include/display.h
@@ -20,15 +20,16 @@ struct imenu_T {
};
extern int screen_offset_x, screen_offset_y;
extern int wrap;
int input(WINDOW *win, char *buffer, int size, input_f crit);
int display_start(void);
int display_stop(void);
void display_menu(window_T wind, char *name, struct menu_T *items, int size);
void display_menu(window_T wind, char *name, struct menu_T *items, int size, int title);
int display_imenu(window_T wind, struct imenu_T *items, int size);
void display_game(WINDOW *win, int h, int w, int ph, int pw, int wrap);
void display_game(window_T wind, int h, int w, int ph, int pw);
int display_select(window_T wind, int w, int h);
void display_status(window_T wind, unsigned long int generation, int gen_step,
int wrap, int height, int wight, int play, int dt,
@@ -39,4 +40,6 @@ void handle_winch(int sig);
void display_state_set(int i, int j, int val);
void display_patterns(window_T wind);
#endif
diff --git a/include/game.h b/include/game.h
@@ -0,0 +1,6 @@
#ifndef GAME_H
#define GAME_H
void game(int s_h, int s_w, char *mode_name, int ncells, int mode_index);
#endif
diff --git a/include/main.h b/include/main.h
@@ -0,0 +1,11 @@
#ifndef MAIN_H
#define MAIN_H
extern struct window_T *menu_w;
void save(void);
void load_pattern(void);
void save_pattern(void);
#endif
diff --git a/include/pattern.h b/include/pattern.h
@@ -0,0 +1,120 @@
#ifndef PATTERN_H
#define PATTERN_H
#include "stdlib.h"
typedef struct pattern_T {
char *cells;
char *name;
int height;
int width;
} * pattern_T;
struct pattern_T title = {
"011111000000000000000000000000000000000000000000010000000000000000000000 "
"100000100011000100001011111100000111100111111000010000000101111110111111 "
"100000000100100110011010000000001000010100000000010000000101000000100000 "
"100111101000010101101011111000001000010111110000010000000101111100111110 "
"100000101111110100001010000000001000010100000000010000000101000000100000 "
"100000101000010100001010000000001000010100000000010000000101000000100000 "
"011111001000010100001011111100000111100100000000011111110101000000111111 ",
"Title",
0,
0,
};
struct pattern_T games[] = {
{ "", "Game", 0, 0},
{ "p", "play/pause", 0, 0},
{ "wasd", "move cursor", 0, 0},
{"arrows", "move screen", 0, 0},
{ "q", "quit", 0, 0},
{ "space", "toggle cell", 0, 0},
{ "v", "visual select", 0, 0},
{ "-", "decrease generation step", 0, 0},
{ "+", "increase generation step", 0, 0},
{ "[", "decrease dt step", 0, 0},
{ "]", "increase dt step", 0, 0},
{ "", "", 0, 0},
{ "", "Visual select", 0, 0},
{ "enter", "save", 0, 0},
{ "x", "delete", 0, 0},
{ "t", "toggle", 0, 0},
{ "q", "cancel", 0, 0},
};
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},
{ "1100 1001 0011", "Carrier", 0, 0},
{ "11000 10011 01101", "Shillelagh", 0, 0},
{ "110 1001 1001 0110", "Pond", 0, 0},
{ "0010 0101 1010 0100", "Barge", 0, 0},
{ "0010 0101 1010 1100", "Long boat", 0, 0},
{ "0110 1001 0101 0010", "Loaf", 0, 0},
{"00100 01010 01010 11011", "Hat", 0, 0},
};
struct pattern_T oscillators[] = {
{ "0111 1110", "Toad", 0, 0},
{ "010 010 010", "Blinker", 0, 0},
{ "0010 1010 0101 0100", "Clock", 0, 0},
{ "0011 0001 1000 1100", "Beacon", 0, 0},
{ "0010000100 1101111011 0010000100", "Pentadecathlon", 0, 0},
{ "110000 110100 000010 010000 001011 000011", "Figure eight", 0, 0},
{"11000011 10100101 00100100 10100101 11000011", "Spark coil", 0, 0},
};
struct pattern_T spaceships[] = {
{ "010 001 111", "Glider", 0},
{ "01001 10000 10001 11110", "Lightweight", 0},
{ "000100 010001 100000 100001 111110", "Mediumweight", 0},
{"0001100 0100001 1000000 1000001 1111110", "Heavyweight", 0},
};
struct pattern_T methuselahs[] = {
{ "111 101 101", "Piheptomino", 0},
{ "1011 1110 0100", "B-Heptomino", 0},
{ "11001 10001 10011", "Glider by the dozen", 0},
{ "111 000 010 010 010", "Thunderbird", 0},
{ "0100000 0001000 1100111", "Acorn", 0},
{"00000010 11000000 01000111", "Diehard", 0},
};
typedef struct pattern_group_T {
char *name;
pattern_T pattern;
int size;
} pattern_group_T;
struct pattern_group_T pattern_groups[] = {
{"Key bindings", games, sizeof(games) / sizeof(struct pattern_T)},
{ "Still", stills, sizeof(stills) / sizeof(struct pattern_T)},
{ "Oscillator", oscillators, sizeof(oscillators) / sizeof(struct pattern_T)},
{ "Spaceships", spaceships, sizeof(spaceships) / sizeof(struct pattern_T)},
{ "Methuselah", methuselahs, sizeof(methuselahs) / sizeof(struct pattern_T)},
};
int pattern_groups_s = sizeof(pattern_groups) / sizeof(pattern_group_T);
int pattern_height(pattern_T pattern) {
int count = 0;
for (char *p = pattern->cells; *p != '\0'; p++)
if (*p == ' ')
count++;
return count;
}
int pattern_width(pattern_T pattern) {
int count = 0;
for (char *p = pattern->cells; *p != ' ' && *p != '\0'; p++)
count++;
return count;
}
#endif
diff --git a/include/utils.h b/include/utils.h
@@ -3,9 +3,9 @@
#include <curses.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))))
#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 ACLAMP(a, x, y) (MAX(x, MIN(a, y)))
#ifdef _WIN32
@@ -18,9 +18,13 @@
}
#else
#define HANDLE_RESIZE \
{ \
handle_winch(10); \
}
{ handle_winch(10); }
#endif
#ifdef NO_UNICODE
#define UNICODE 0
#else
#define UNICODE 1
#endif
#define MAX_SCREEN_H 512
diff --git a/include/window.h b/include/window.h
@@ -22,6 +22,7 @@ int window_wight(T self);
WINDOW *window_win(T self);
void window_settings(WINDOW *win);
void window_clear(T self);
void window_clear_noRefresh(T self);
void window_set_title(T self, char *title);
diff --git a/src/display.c b/src/display.c
@@ -5,21 +5,22 @@
#include "display.h"
#include "logic.h"
#include "pattern.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_BLANK " "
#define CHAR_CURSOR "<>"
#define CHAR_CIRCLE "\u26AB"
#define CHAR_SQUARE "\u2B1B"
#define CHAR_SQUARED "\u2B1C"
#define CHAR_CIRCLE_DOT "\u2299"
#define CHAR_BLANK " "
#define CHAR_CURSOR "<>"
#define CHAR_CIRCLE "\u26AB"
#define CHAR_SQUARE "\u2B1B"
#define CHAR_ACTIVE CHAR_CIRCLE
extern Cell *hash;
window_T MAIN_w = NULL;
#define y_at(y) y, screen_offset_y, h
@@ -57,12 +58,28 @@ window_T MAIN_w = NULL;
} \
}
void print_pattern(WINDOW *win, pattern_T pattern, int *y, int x, int indent) {
(*y)++;
wmove(win, (*y)++, x + indent);
for (char *c = pattern->cells; *c != '\0'; c++) {
if (*c == ' ') {
wmove(win, (*y)++, x + indent);
continue;
}
int val = *c - '0';
wattrset(win, COLOR_PAIR(val + 2));
print_cell(win, " ");
}
}
int screen_offset_x, screen_offset_y;
int cursor_offset_x, cursor_offset_y;
int wrap;
int (*cord)(int, int, int);
int get_screen_position(int wrap, int value, int screen_offset, int screen_size,
int get_screen_position(int value, int screen_offset, int screen_size,
int board_size) {
int overshoot = screen_offset + screen_size - board_size;
@@ -87,27 +104,26 @@ int get_screen_position(int wrap, int value, int screen_offset, int screen_size,
}
}
void display_game(WINDOW *win, int h, int w, int ph, int pw, int wrap) {
werase(win);
void display_game(window_T wind, int h, int w, int ph, int pw) {
WINDOW *win = window_win(wind);
wattrset(win, COLOR_PAIR(0));
box(win, ACS_VLINE, ACS_HLINE);
window_clear_noRefresh(wind);
int row, col, val;
getNext(&row, &col, &val, 1);
int i = 0;
while (getNext(&row, &col, &val, 0)) {
for (Cell *c = hash; c != NULL; c = c->hh.next) {
wattrset(win, COLOR_PAIR(val + 2));
if ((row = get_screen_position(wrap, row, screen_offset_y, ph, h)) < 0)
continue;
if ((col = get_screen_position(wrap, col, screen_offset_x, pw, w)) < 0)
/* row = get_screen_position(c->row, screen_offset_y, ph, h); */
/* col = get_screen_position(c->col, screen_offset_x, pw, w); */
row = get_screen_position(c->cord.row, screen_offset_y, ph, h);
col = get_screen_position(c->cord.col, screen_offset_x, pw, w);
val = c->val;
if (row < 0 || col < 0)
continue;
mvprint_cell(win, row, col, 2, CHAR_BLANK);
}
wrefresh(win);
}
void display_cursor(WINDOW *win, int h, int w, int ph, int pw) {
@@ -119,7 +135,6 @@ void display_cursor(WINDOW *win, int h, int w, int ph, int pw) {
val = getAt(cord(y_at(cursor_offset_y)), cord(x_at(cursor_offset_x)));
mvprint_cell(win, cursor_offset_y, cursor_offset_x, 5, CHAR_CURSOR);
wrefresh(win);
prev_y = cursor_offset_y;
prev_x = cursor_offset_x;
@@ -130,13 +145,17 @@ int display_select(window_T wind, int w, int h) {
int current_offset_x = cursor_offset_x;
int ret_value = 1;
redraw:;
int CLINES = LINES, CCOLS = COLS;
WINDOW *new = WINDOW_new(wind);
int CLINES = LINES, CCOLS = COLS;
WINDOW *win = window_win(wind);
WINDOW *new;
overlay(win, new);
wrefresh(new);
if (UNICODE) {
new = WINDOW_new(wind);
overlay(win, new);
wrefresh(new);
} else {
new = win;
}
int ph = window_height(wind), pw = window_wight(wind) / 2;
nodelay(stdscr, 0);
@@ -146,16 +165,15 @@ redraw:;
int start_j = MIN(cursor_offset_x, current_offset_x);
int end_j = MAX(cursor_offset_x, current_offset_x);
if (!UNICODE)
display_game(wind, h, w, ph, pw);
print_cells(new, start_i, end_i + 1, start_j, end_j + 1, 8, CHAR_BLANK);
wrefresh(new);
if (is_term_resized(CLINES, CCOLS)) {
flushinp();
delwin(new);
HANDLE_RESIZE;
ph = window_height(wind), pw = window_wight(wind) / 2;
/* display_game(win, w, h, ph, pw); */
goto redraw;
goto end;
}
int c = getch();
@@ -194,6 +212,8 @@ redraw:;
// confirm and save slection
case '\n':
for_each_cell(start_i, end_i + 1, start_j, end_j + 1)
saveCell(cord(y_at(i)), cord(x_at(j)));
ret_value = 100;
goto end;
@@ -202,6 +222,7 @@ redraw:;
case 'q':
case 'Q':
goto end;
defalut:
flushinp();
continue;
@@ -227,10 +248,7 @@ void display_status(window_T wind, unsigned long int gen, int gen_step,
wmove(win, 1, 1);
wprintw(win, " | %5s | ", play ? "play" : "pause");
if(wrap)
wprintw(win, "Size: %dx%d | ", height, wight);
else
wprintw(win, "Size: unlimited | ");
wprintw(win, wrap ? "Size: %dx%d | " : "Size: unlimited | ", height, wight);
wprintw(win, "Generation: %10lu(+%d) | ", gen, gen_step);
wprintw(win, "dt: %4dms | ", dt);
wprintw(win, "Cursor: %4dx%-4d | ", cursor_y, cursor_x);
@@ -293,7 +311,7 @@ int display_imenu(window_T wind, struct imenu_T *items, int size) {
maxi = len;
curs_set(1);
window_clear(wind);
window_clear_noRefresh(wind);
redraw:;
win = window_win(wind);
y_offset = wcenter_vertical(wind, size * 2 - 1);
@@ -338,7 +356,19 @@ redraw:;
curs_set(0);
}
void display_menu(window_T wind, char *name, struct menu_T *items, int size) {
void display_title(window_T wind, int y) {
WINDOW *win = window_win(wind);
title.height = (!title.height) ? pattern_height(&title) : title.height;
title.width = (!title.width) ? pattern_width(&title) : title.width;
int max_w = window_wight(wind);
if (title.width * 2 < max_w)
print_pattern(win, &title, &y, (max_w - title.width * 2) / 2, 0);
wrefresh(win);
}
void display_menu(window_T wind, char *name, struct menu_T *items, int size,
int title) {
WINDOW *win;
int current = 0;
@@ -348,11 +378,16 @@ void display_menu(window_T wind, char *name, struct menu_T *items, int size) {
maxi = len;
window_set_title(wind, name);
window_clear(wind);
window_clear_noRefresh(wind);
redraw:;
win = window_win(wind);
int CLINES = LINES, CCOLS = COLS;
int y_offset = wcenter_vertical(wind, size * 2 - 1);
if (title)
display_title(wind, title);
while (TRUE) {
CLAMP(current, 0, size - 1);
@@ -404,9 +439,9 @@ void curses_start(void) {
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);
init_pair(5, COLOR_WHITE, COLOR_BLUE);
init_pair(6, COLOR_WHITE, COLOR_BLUE);
init_pair(7, COLOR_RED, COLOR_BLUE);
init_pair(8, COLOR_WHITE, COLOR_YELLOW);
init_pair(9, COLOR_WHITE, COLOR_YELLOW);
@@ -416,13 +451,13 @@ void curses_start(void) {
init_pair(3, COLOR_WHITE, COLOR_WHITE);
init_pair(4, COLOR_RED, COLOR_RED);
init_pair(5, COLOR_YELLOW, -1);
init_pair(6, COLOR_YELLOW, COLOR_WHITE);
init_pair(7, COLOR_YELLOW, COLOR_RED);
init_pair(5, COLOR_BLUE, -1);
init_pair(6, COLOR_BLUE, COLOR_WHITE);
init_pair(7, COLOR_BLUE, COLOR_RED);
init_pair(8, COLOR_WHITE, COLOR_YELLOW);
init_pair(9, COLOR_WHITE, COLOR_WHITE);
init_pair(10, COLOR_RED, COLOR_RED);
init_pair(9, COLOR_WHITE, COLOR_BLUE);
init_pair(10, COLOR_RED, COLOR_BLACK);
#endif
}
@@ -460,3 +495,91 @@ int display_stop(void) {
curses_stop();
return 1;
}
void display_patterns(window_T wind) {
int y_start = 2, x_start = 2;
int indent = 2;
window_set_title(wind, "Help");
window_clear_noRefresh(wind);
redraw:;
int CLINES = LINES, CCOLS = COLS;
WINDOW *win = window_win(wind);
int ph = window_height(wind), pw = window_wight(wind);
int y, x, maxi, max_x;
int count;
x = x_start;
for (int i = 0; i < pattern_groups_s; i++) {
wattrset(win, 0);
pattern_group_T group = pattern_groups[i];
y = y_start;
if ((maxi = strlen(group.name)) + x >= pw)
continue;
mvwprintw(win, y++, x, "%s:", group.name);
y++;
for (int j = 0; j < group.size; j++) {
struct pattern_T p = group.pattern[j];
p.height = (!p.height) ? pattern_height(&p) : p.height;
p.width = (!p.width) ? pattern_width(&p) : p.width;
max_x = MAX(p.width * 2, strlen(p.name) + 3);
if (y + p.height + 4 >= ph) {
y = 3;
x += (maxi + 2 + indent * 1);
maxi = max_x;
}
if (x + max_x + 5 + indent >= pw) {
break;
}
wattrset(win, 0);
if (p.height != 0) {
mvwprintw(win, y++, x, "- %s: ", p.name);
print_pattern(win, &p, &y, x, indent);
y += 2;
} else {
if (*p.name) {
if (*p.cells != '\0') {
mvwprintw(win, y, x, " %s: ", p.name);
max_x += 2;
} else
mvwprintw(win, y, x, "%s: ", p.name);
wprintw(win, "%s", p.cells);
}
y++;
}
maxi = MAX(maxi, max_x);
}
x += (maxi + 2 + indent * 2);
wattrset(win, 0);
for (int i = 1; i <= ph; i++)
mvwprintw(win, i, x, "|");
x += 2 * indent;
if (x >= pw)
break;
}
wrefresh(win);
while (true) {
switch (getch()) {
case 27:
case 'q':
case 'Q':
case '\n':
goto end;
}
if (is_term_resized(CLINES, CCOLS)) {
HANDLE_RESIZE;
goto redraw;
}
}
end:;
}
diff --git a/src/file.c b/src/file.c
@@ -7,6 +7,9 @@
#include <unistd.h>
#include "display.h"
#include "game.h"
#include "logic.h"
#include "utils.h"
#ifdef _WIN32
#define SETTINGS_DIR "C:\\GoL" // without trailing '\'
@@ -138,8 +141,83 @@ void load_files(void) {
void free_files(void) { file_free(loaded_files); }
// Comming soon...
void file_load_pattern(char *name, int index) { return; }
void file_save_pattern(char *name, int index) { return; }
void file_load(char *name, int index) { return; }
void file_save(char *name, int index) { return; }
extern Cell **save_cells;
extern int save_cells_s;
extern int pos_y;
extern int pos_x;
extern int WIDTH, HEIGHT;
extern int evolve_index;
void file_load_pattern(char *name, int index) {
char *fname = malloc((strlen(name) + 5) * sizeof(char));
sprintf(fname, "%s.part", name);
FILE *f = fopen(fname, "r");
if (!f)
exit(1);
int row, col, val;
while (fscanf(f, "%d %d %d", &row, &col, &val) != EOF) {
setAt(pos_y + row, pos_x + col, val);
}
}
void file_save_pattern(char *name, int index) {
char *fname = malloc((strlen(name) + 5) * sizeof(char));
sprintf(fname, "%s.part", name);
FILE *f = fopen(fname, "w");
if (!f)
exit(1);
int min_y = save_cells[0]->cord.row;
int min_x = save_cells[0]->cord.col;
for (int i = 0; i < save_cells_s; i++) {
Cell *c = save_cells[i];
min_y = MIN(min_y, c->cord.row);
min_x = MIN(min_x, c->cord.col);
}
for (int i = 0; i < save_cells_s; i++) {
Cell *c = save_cells[i];
fprintf(f, "%d %d %d\n", c->cord.row - min_y, c->cord.col - min_x, c->val);
}
fclose(f);
}
void file_load(char *name, int index) {
char *fname = malloc((strlen(name) + 5) * sizeof(char));
sprintf(fname, "%s.all", name);
FILE *f = fopen(fname, "r");
if (!f)
exit(1);
fscanf(f, "%d %d %d", &HEIGHT, &WIDTH, &evolve_index);
int row, col, val;
while (fscanf(f, "%d %d %d", &row, &col, &val) != EOF) {
setAt(pos_y + row, pos_x + col, val);
}
game(HEIGHT, WIDTH, evolution_names[evolve_index],
evolution_cells[evolve_index], evolve_index);
}
void file_save(char *name, int index) {
char *fname = malloc((strlen(name) + 5) * sizeof(char));
sprintf(fname, "%s.all", name);
FILE *f = fopen(fname, "w");
if (!f)
exit(1);
fprintf(f, "%d %d %d\n", HEIGHT, WIDTH, evolve_index);
for (Cell *c = hash; c != NULL; c = c->hh.next) {
fprintf(f, "%d %d %d\n", c->cord.row, c->cord.col, c->val);
}
fclose(f);
}
diff --git a/src/game.c b/src/game.c
@@ -0,0 +1,319 @@
#include <curses.h>
#include <time.h>
#include "display.h"
#include "utils.h"
#include "window.h"
#include "logic.h"
#include "main.h"
#ifdef _WIN32
#define TIME_MOD 1
#else
#define TIME_MOD 1000
#endif
extern char *evolution_names[];
extern int evolution_cells[];
extern int evolution_size;
extern window_T menu_w;
#define save_state() \
{ \
t_y = cord(y_at(ph / 2)); \
t_x = cord(x_at(pw / 2)); \
ct_y = cord(y_at(cursor_offset_y)); \
ct_x = cord(x_at(cursor_offset_x)); \
}
#define y_at(y) y, screen_offset_y, h
#define x_at(x) x, screen_offset_x, w
typedef int (*coordinate_f)(int, int, int);
int coordinate_nowrap(int val, int offset, int max) { return val + offset; }
int coordinate_wrap(int val, int offset, int max) {
return (val + offset + max) % max;
}
extern coordinate_f cord;
extern int screen_offset_x, screen_offset_y;
extern int cursor_offset_x, cursor_offset_y;
extern int wrap;
void game(int s_h, int s_w, char *mode_name, int ncells, int mode_index) {
unsigned long int gen = 0;
int h, w;
int gen_step = 1, play = 0, time_const = 100, time_step = 1;
wrap = 1;
int ph, pw, t_y = 0, t_x = 0, ct_x = 0, ct_y = 0;
window_T status_w, screen_w, game_w;
reset_screen:
status_w = window_split(menu_w, 1, 3, 0, "Status", "Game");
screen_w = window_sibiling(status_w);
window_set_title(menu_w, NULL);
window_clear(menu_w);
wrap = (s_w > 0 && s_h > 0);
if (!wrap) {
w = window_wight(screen_w) / 2;
h = window_height(screen_w);
logic_init(0, 0, wrap);
} else {
w = s_w;
h = s_h;
logic_init(w, h, wrap);
}
cord = wrap ? coordinate_wrap : coordinate_nowrap;
game_w = window_center(screen_w, 0, h, w * 2, mode_name);
evolution_init(mode_index);
redraw:;
int CLINES = LINES, CCOLS = COLS;
clock_t start_t, end_t = 0, total_t;
if (!wrap) {
game_w = window_center(screen_w, 0, window_height(screen_w),
window_wight(screen_w), mode_name);
}
WINDOW *game_W = window_win(game_w);
ph = window_height(game_w), pw = window_wight(game_w) / 2;
window_clear(menu_w);
window_clear(screen_w);
window_clear(status_w);
screen_offset_y = t_y - ph / 2;
screen_offset_x = t_x - pw / 2;
if (wrap) {
screen_offset_x = (screen_offset_x + w) % w;
screen_offset_y = (screen_offset_y + h) % h;
}
cursor_offset_y = ct_y - screen_offset_y;
cursor_offset_x = ct_x - screen_offset_x;
if (wrap) {
cursor_offset_x = (cursor_offset_x + w) % w;
cursor_offset_y = (cursor_offset_y + h) % h;
}
CLAMP(cursor_offset_y, 0, ph - 1);
CLAMP(cursor_offset_x, 0, pw - 1);
display_game(game_w, h, w, ph, pw);
display_cursor(game_W, h, w, ph, pw);
wrefresh(game_W);
int screen_change = 1;
int cursor_change = 1;
while (TRUE) {
start_t = clock();
if (wrap) {
screen_offset_x = (screen_offset_x + w) % w;
screen_offset_y = (screen_offset_y + h) % h;
}
if (play) {
do_evolution(gen_step);
screen_change = 1;
gen += gen_step;
}
display_status(status_w, gen, gen_step, wrap, h, w, play, time_const,
cord(y_at(cursor_offset_y)), cord(x_at(cursor_offset_x)));
if (play || screen_change) {
display_game(game_w, h, w, ph, pw);
wrefresh(game_W);
screen_change = 0;
cursor_change = 1;
}
if (cursor_change) {
display_cursor(game_W, h, w, ph, pw);
wrefresh(game_W);
cursor_change = 0;
}
while ((total_t = (long int)(end_t - start_t)) < time_const * TIME_MOD) {
refresh();
int c = getch();
switch (c) {
// toggle pause
case 'p':
case 'P':
play = !play;
break;
// quit
case 27:
case 'q':
case 'Q':
goto end;
// change num of evolutions before display
case '+':
gen_step++;
break;
case '-':
gen_step--;
break;
// change refreshrate
case ']':
time_const += time_step;
break;
case '[':
time_const -= time_step;
break;
}
if (!play) {
// move cursor around
switch (c) {
case 'w':
case 'W':
cursor_offset_y--;
cursor_change = 1;
break;
case 's':
case 'S':
cursor_offset_y++;
cursor_change = 1;
break;
case 'a':
case 'A':
cursor_offset_x--;
cursor_change = 1;
break;
case 'd':
case 'D':
cursor_offset_x++;
cursor_change = 1;
break;
// toggle cell
case ' ':
toggleAt(cord(y_at(cursor_offset_y)), cord(x_at(cursor_offset_x)));
cursor_change = 1;
break;
// visual selection
case 'v':
case 'V':
if (display_select(game_w, h, h) == 100) {
window_unsplit(menu_w);
save_pattern();
}
save_state();
goto reset_screen;
break;
// lead pattern
case 'l':
case 'L':
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':
case 'R':
save_state();
goto redraw;
}
}
// move screen around
switch (c) {
case KEY_A1:
case KEY_C1:
case KEY_END:
case KEY_HOME:
case KEY_LEFT:
screen_offset_x--;
screen_change = 1;
break;
case KEY_A3:
case KEY_C3:
case KEY_NPAGE:
case KEY_PPAGE:
case KEY_RIGHT:
screen_offset_x++;
screen_change = 1;
break;
}
switch (c) {
case KEY_A1:
case KEY_A3:
case KEY_HOME:
case KEY_PPAGE:
case KEY_UP:
screen_offset_y--;
screen_change = 1;
break;
case KEY_C1:
case KEY_C3:
case KEY_DOWN:
case KEY_END:
case KEY_NPAGE:
screen_offset_y++;
screen_change = 1;
}
CLAMP(cursor_offset_y, 0, ph - 1);
CLAMP(cursor_offset_x, 0, pw - 1);
CLAMP(gen_step, 1, 100);
CLAMP(time_const, 0, 1000);
if (is_term_resized(CLINES, CCOLS)) {
flushinp();
save_state();
HANDLE_RESIZE;
goto redraw;
}
end_t = clock();
}
flushinp();
}
end:;
window_unsplit(menu_w);
logic_free();
return;
}
diff --git a/src/main.c b/src/main.c
@@ -4,307 +4,16 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>
#include <time.h>
#include "display.h"
#include "file.h"
#include "game.h"
#include "logic.h"
#include "utils.h"
#include "window.h"
#ifdef _WIN32
#define TIME_MOD 1
#else
#define TIME_MOD 1000
#endif
window_T menu_w;
extern window_T MAIN_w;
extern char *evolution_names[];
extern int evolution_cells[];
extern int evolution_size;
window_T menu_w;
int top_space = 5;
void load_pattern(void);
void save_pattern(void);
#define save_state() \
{ \
t_y = cord(y_at(ph / 2)); \
t_x = cord(x_at(pw / 2)); \
ct_y = cord(y_at(cursor_offset_y)); \
ct_x = cord(x_at(cursor_offset_x)); \
}
#define y_at(y) y, screen_offset_y, h
#define x_at(x) x, screen_offset_x, w
typedef int (*coordinate_f)(int, int, int);
int coordinate_nowrap(int val, int offset, int max) { return val + offset; }
int coordinate_wrap(int val, int offset, int max) {
return (val + offset + max) % max;
}
extern coordinate_f cord;
extern int screen_offset_x, screen_offset_y;
extern int cursor_offset_x, cursor_offset_y;
extern short int **mat;
void game(int h, int w, char *name, int ncells, int mode_index) {
unsigned long int gen = 0;
int gen_step = 1, play = 0, time_const = 100, time_step = 1;
int wrap = 1;
int ph, pw, t_y = 0, t_x = 0, ct_x = 0, ct_y = 0;
window_T status_w, screen_w, game_w;
status_w = window_split(menu_w, 1, 3, 0, "Status", name);
screen_w = window_sibiling(status_w);
window_set_title(menu_w, NULL);
window_clear(menu_w);
wrap = (w > 0 && h > 0);
if (!wrap) {
w = window_wight(screen_w) / 2;
h = window_height(screen_w);
}
cord = wrap ? coordinate_wrap : coordinate_nowrap;
game_w = window_center(screen_w, 0, h, w * 2, "Game");
logic_init(w, h, wrap);
evolution_init(mode_index);
redraw:;
int CLINES = LINES, CCOLS = COLS;
clock_t start_t, end_t = 0, total_t;
if (!wrap) {
game_w = window_center(screen_w, 0, window_height(screen_w),
window_wight(screen_w), "Game");
}
WINDOW *game_W = window_win(game_w);
ph = window_height(game_w), pw = window_wight(game_w) / 2;
window_clear(menu_w);
window_clear(screen_w);
window_clear(status_w);
screen_offset_y = t_y - ph / 2;
screen_offset_x = t_x - pw / 2;
if (wrap) {
screen_offset_x = (screen_offset_x + w) % w;
screen_offset_y = (screen_offset_y + h) % h;
}
cursor_offset_y = ct_y - screen_offset_y;
cursor_offset_x = ct_x - screen_offset_x;
if (wrap) {
cursor_offset_x = (cursor_offset_x + w) % w;
cursor_offset_y = (cursor_offset_y + h) % h;
}
CLAMP(cursor_offset_y, 0, ph - 1);
CLAMP(cursor_offset_x, 0, pw - 1);
display_game(game_W, h, w, ph, pw, wrap);
display_cursor(game_W, h, w, ph, pw);
int screen_change = 1;
int cursor_change = 1;
while (TRUE) {
start_t = clock();
if (wrap) {
screen_offset_x = (screen_offset_x + w) % w;
screen_offset_y = (screen_offset_y + h) % h;
}
if (play) {
do_evolution(gen_step);
screen_change = 1;
gen += gen_step;
}
display_status(status_w, gen, gen_step, wrap, h, w, play, time_const,
cord(y_at(cursor_offset_y)), cord(x_at(cursor_offset_x)));
if (play || screen_change) {
display_game(game_W, h, w, ph, pw, wrap);
screen_change = 0;
cursor_change = 1;
}
if (cursor_change) {
display_cursor(game_W, h, w, ph, pw);
cursor_change = 0;
}
while ((total_t = (long int)(end_t - start_t)) < time_const * TIME_MOD) {
refresh();
int c = getch();
switch (c) {
// toggle pause
case 'p':
case 'P':
play = !play;
break;
// quit
case 27:
case 'q':
case 'Q':
goto end;
// change num of evolutions before display
case '+':
gen_step++;
break;
case '-':
gen_step--;
break;
// change refreshrate
case ']':
time_const += time_step;
break;
case '[':
time_const -= time_step;
break;
}
if (!play) {
// move cursor around
switch (c) {
case 'w':
case 'W':
cursor_offset_y--;
cursor_change = 1;
break;
case 's':
case 'S':
cursor_offset_y++;
cursor_change = 1;
break;
case 'a':
case 'A':
cursor_offset_x--;
cursor_change = 1;
break;
case 'd':
case 'D':
cursor_offset_x++;
cursor_change = 1;
break;
// toggle cell
case ' ':
toggleAt(cord(y_at(cursor_offset_y)), cord(x_at(cursor_offset_x)));
cursor_change = 1;
break;
// visual selection
case 'v':
case 'V':
if (display_select(game_w, h, h) == 100) {
save_pattern();
}
window_set_title(menu_w, NULL);
save_state();
goto redraw;
break;
// lead pattern
case 'l':
case 'L':
load_pattern();
window_set_title(screen_w, name);
window_set_title(menu_w, NULL);
save_state();
goto redraw;
break;
// redraw screen
case 'r':
case 'R':
save_state();
goto redraw;
}
}
// move screen around
switch (c) {
case KEY_A1:
case KEY_C1:
case KEY_END:
case KEY_HOME:
case KEY_LEFT:
screen_offset_x--;
screen_change = 1;
break;
case KEY_A3:
case KEY_C3:
case KEY_NPAGE:
case KEY_PPAGE:
case KEY_RIGHT:
screen_offset_x++;
screen_change = 1;
break;
}
switch (c) {
case KEY_A1:
case KEY_A3:
case KEY_HOME:
case KEY_PPAGE:
case KEY_UP:
screen_offset_y--;
screen_change = 1;
break;
case KEY_C1:
case KEY_C3:
case KEY_DOWN:
case KEY_END:
case KEY_NPAGE:
screen_offset_y++;
screen_change = 1;
}
CLAMP(cursor_offset_y, 0, ph - 1);
CLAMP(cursor_offset_x, 0, pw - 1);
CLAMP(gen_step, 1, 100);
CLAMP(time_const, 0, 1000);
if (is_term_resized(CLINES, CCOLS)) {
flushinp();
save_state();
HANDLE_RESIZE;
goto redraw;
}
end_t = clock();
}
flushinp();
}
end:;
window_unsplit(menu_w);
logic_free();
return;
}
void settings(char *pass, int index) {
struct imenu_T imenu_items[] = {
@@ -334,11 +43,13 @@ void mode_select(char *pass, int index) {
mode_items[i].name = evolution_names[i];
mode_items[i].callback = settings;
}
display_menu(menu_w, "Game Mode", mode_items, evolution_size);
display_menu(menu_w, "Game Mode", mode_items, evolution_size, 0);
free(mode_items);
}
void (*file_save_method)(char *, int);
void new_file(char *pass, int index) {
struct imenu_T new_file_items[] = {
{"Pick a name", 10, isalnum, NULL},
@@ -347,7 +58,7 @@ void new_file(char *pass, int index) {
window_set_title(menu_w, "New File");
while (display_imenu(menu_w, new_file_items, new_file_items_s)) {
file_save_pattern(new_file_items[0].buffer, index);
file_save_method(new_file_items[0].buffer, index);
break;
}
@@ -363,7 +74,7 @@ struct menu_T *file_menu_list(char *ext, void (*callback)(char *, int),
int n;
load_files();
n = file_select("part", &buffer);
n = file_select(ext, &buffer);
struct menu_T *file_items = malloc((n + offset) * sizeof(struct menu_T));
for (int i = 0; i < n; i++) {
@@ -382,7 +93,8 @@ void save_pattern(void) {
file_items[0].name = "NEW";
file_items[0].callback = new_file;
display_menu(menu_w, "Save Pattern", file_items, n);
file_save_method = file_save_pattern;
display_menu(menu_w, "Save Pattern", file_items, n, 0);
free(file_items);
}
@@ -390,18 +102,32 @@ void load_pattern(void) {
int n;
struct menu_T *file_items = file_menu_list("part", file_load_pattern, 0, &n);
display_menu(menu_w, "Load Pattern", file_items, n);
display_menu(menu_w, "Load Pattern", file_items, n, 0);
free(file_items);
}
void load(char *pass, int index) {
int n;
struct menu_T *file_items = file_menu_list("part", file_load, 0, &n);
struct menu_T *file_items = file_menu_list("all", file_load, 0, &n);
display_menu(menu_w, "Load Game", file_items, n);
display_menu(menu_w, "Load Game", file_items, n, 0);
free(file_items);
}
void save(void) {
int n;
struct menu_T *file_items = file_menu_list("all", file_save, 1, &n);
file_items[0].name = "NEW";
file_items[0].callback = new_file;
file_save_method = file_save;
display_menu(menu_w, "Save Pattern", file_items, n, 0);
free(file_items);
}
mode_select("nothing", 0);
void help(char *pass, int index) {
window_set_title(menu_w, "Help");
display_patterns(menu_w);
}
void exitp(char *pass, int index) {
@@ -412,6 +138,7 @@ void exitp(char *pass, int index) {
struct menu_T menu_items[] = {
{mode_select, "Start"},
{ load, "Load"},
{ help, "Help"},
{ exitp, "Exit"}
};
@@ -432,7 +159,7 @@ int main(void) {
menu_w = MAIN_w;
while (TRUE) {
display_menu(menu_w, "Main menu", menu_items, menu_items_s);
display_menu(menu_w, "Main menu", menu_items, menu_items_s, 1);
}
window_free(MAIN_w);
diff --git a/src/utils.c b/src/utils.c
@@ -1,4 +0,0 @@
#include <curses.h>
#include "utils.h"
diff --git a/src/window.c b/src/window.c
@@ -29,9 +29,8 @@ void WINDOW_init(WINDOW *win, char *title) {
window_settings(win);
wattrset(win, COLOR_PAIR(0));
box(win, ACS_VLINE, ACS_HLINE);
if (title) {
if (title)
mvwprintw(win, 0, 1, "%s", title);
}
wrefresh(win);
}
@@ -209,6 +208,14 @@ void window_clear(T self) {
WINDOW_init(self->win, self->title);
}
void window_clear_noRefresh(T self) {
werase(self->win);
wattrset(self->win, COLOR_PAIR(0));
box(self->win, ACS_VLINE, ACS_HLINE);
if (self->title)
mvwprintw(self->win, 0, 1, "%s", self->title);
}
void window_settings(WINDOW *win) { keypad(win, TRUE); }
void wcenter_horizontal(T window, int y, int n) {