gol

Implementation 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 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:
Minclude/display.h | 7+++++--
Ainclude/game.h | 6++++++
Ainclude/main.h | 11+++++++++++
Ainclude/pattern.h | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/utils.h | 16++++++++++------
Minclude/window.h | 1+
Msrc/display.c | 213++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/file.c | 88++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Asrc/game.c | 319+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.c | 331+++++++------------------------------------------------------------------------
Dsrc/utils.c | 4----
Msrc/window.c | 11+++++++++--
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) {