commit aea5b3965a13efe4ba9a2679055229aec01ba613
parent 8e6a27421fd714f1ef85cd441400e0172c603347
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Mon, 6 Jun 2022 16: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:
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) {