aboutsummaryrefslogtreecommitdiff
path: root/gol.c
diff options
context:
space:
mode:
authortoni <matzeton@googlemail.com>2017-01-11 01:07:23 +0100
committertoni <matzeton@googlemail.com>2017-01-11 01:28:42 +0100
commitd039eca8d4269333fb103042b37b15138ddcadfc (patch)
treeb713320339fc77c2dbc42b1c253bc38a7f22d454 /gol.c
initial commit
Diffstat (limited to 'gol.c')
-rw-r--r--gol.c469
1 files changed, 469 insertions, 0 deletions
diff --git a/gol.c b/gol.c
new file mode 100644
index 0000000..77d54a6
--- /dev/null
+++ b/gol.c
@@ -0,0 +1,469 @@
+/**
+ * @author Steffen Vogel <info@steffenvogel.de>, Toni Uhlig <matzeton@googlemail.com>
+ * @copyright Copyright (c) 2010-2014, Steffen Vogel, Toni Uhlig
+ * @license http://opensource.org/licenses/gpl-license.php GNU Public License
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <curses.h>
+#include <time.h>
+
+/* configuration */
+#include "config.h"
+
+#if defined(RANDOM_SPAWNS)
+static uint8_t rnd_spawns = 0xFF;
+#endif
+
+struct config {
+ uint8_t paused:1;
+ uint8_t quit:1;
+ uint8_t menu:1;
+ uint8_t reprint:1;
+};
+
+struct pattern {
+ uint8_t width;
+ uint8_t height;
+ uint8_t * data;
+};
+
+struct cursor {
+ uint8_t x;
+ uint8_t y;
+};
+
+uint8_t start[3][3] = {
+ {0, 1, 1},
+ {1, 1, 0},
+ {0, 1, 0}
+};
+
+uint8_t glider[3][3] = {
+ {0, 1, 0},
+ {0, 0, 1},
+ {1, 1, 1}
+};
+
+uint8_t segler[4][5] = {
+ {0, 1, 1, 1, 1},
+ {1, 0, 0, 0, 1},
+ {0, 0, 0, 0, 1},
+ {1, 0, 0, 1, 0}
+};
+
+uint8_t buffer[7][3] = {
+ {1, 1, 1},
+ {1, 0, 1},
+ {1, 0, 1},
+ {0, 0, 0},
+ {1, 0, 1},
+ {1, 0, 1},
+ {1, 1, 1}
+};
+
+uint8_t kreuz[3][3] = {
+ {0, 1, 0},
+ {1, 1, 1},
+ {0, 1, 0}
+};
+
+uint8_t ship[3][3] = {
+ {1, 1, 0},
+ {1, 0, 1},
+ {0, 1, 1}
+};
+
+/* initialize world with zero (dead cells) */
+void clean_world(uint8_t ** world, uint8_t width, uint8_t height) {
+ int a;
+ for (a = 0; a < width; a++) {
+ memset(world[a], 0, height * sizeof(uint8_t));
+ }
+}
+
+/* allocate memory for world */
+uint8_t ** create_world(uint8_t width, uint8_t height) {
+ uint8_t ** world = calloc(width, sizeof(uint8_t *));
+ int a;
+ for (a = 0; a < width; a++) {
+ world[a] = calloc(height, sizeof(uint8_t));
+ }
+
+ clean_world(world, width, height);
+ return world;
+}
+
+void free_world(uint8_t ** world, uint8_t width, uint8_t height) {
+ int a;
+ for (a = 0; a < width; a++) {
+ free(world[a]);
+ }
+ free(world);
+}
+
+/* insert pattern at (x|y) into world */
+void inhabit_world(struct pattern pattern, uint8_t x, uint8_t y, uint8_t ** world, uint8_t width, uint8_t height) {
+ uint8_t a, b;
+
+ for (a = 0; a < pattern.height; a++) {
+ int c = a;
+ if ((y + c) >= height) c -= height;
+
+ for (b = 0; b < pattern.width; b++) {
+ int d = b;
+ if ((x + d) >= width) d -= width;
+ world[x+d][y+c] = pattern.data[(a*pattern.width)+b];
+ }
+ }
+}
+
+/* calc alive cells */
+uint8_t calc_cell_count(uint8_t ** world, uint8_t width, uint8_t height) {
+ int cell_count = 0;
+ uint8_t a, b;
+
+ for (a = 0; a < width; a++) {
+ for (b = 0; b < height; b++) {
+ cell_count += (world[a][b]) ? 1 : 0;
+ }
+ }
+
+ return cell_count;
+}
+
+uint8_t calc_cell_neighbours(uint8_t x, uint8_t y, uint8_t ** world, uint8_t width, uint8_t height) {
+ uint8_t neighbours = 0;
+ int a, b;
+
+ for (a = x-1; a <= x+1; a++) {
+ int c = a;
+ if (a < 0) c += width;
+ if (a >= width) c -= width;
+
+ for (b = y-1; b <= y+1; b++) {
+ int d = b;
+ if (a == x && b == y) continue;
+ if (b < 0) d += height;
+ if (b >= height) d -= height;
+
+ neighbours += (world[c][d] > 0) ? 1 : 0;
+ }
+ }
+
+ return neighbours; /* 0 <= neighbours <= 8 */
+}
+
+uint8_t calc_next_cell_gen(uint8_t x, uint8_t y, uint8_t ** world, uint8_t width, uint8_t height) {
+ fflush(stdout);
+ uint8_t neighbours = calc_cell_neighbours(x, y, world, width, height);
+ uint8_t alive = world[x][y];
+
+ if (alive) {
+ if (neighbours > 3 || neighbours < 2) {
+ return 0; /* died by over-/underpopulation */
+ }
+ else {
+ return 1; /* kept alive */
+ }
+ }
+ else if (neighbours == 3) {
+ return 1; /* born */
+ }
+ else {
+ return 0; /* still dead */
+ }
+}
+
+void calc_next_gen(uint8_t ** world, uint8_t ** next_gen, uint8_t width, uint8_t height) {
+ uint8_t x, y;
+
+ for (x = 0; x < width; x++) {
+ for (y = 0; y < height; y++) {
+ next_gen[x][y] = calc_next_cell_gen(x, y, world, width, height);
+ }
+ }
+
+ /* copy world */
+ for (x = 0; x < width; x++) {
+ for (y = 0; y < height; y++) {
+ world[x][y] = next_gen[x][y];
+ }
+ }
+}
+
+/* print world with colors and count of neighbours */
+void print_world(uint8_t ** world, uint8_t width, uint8_t height) {
+ uint8_t x, y;
+ move(0, 0); /* reset cursor */
+
+ /* cells */
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ uint8_t neighbours = calc_cell_neighbours(x, y, world, width, height);
+
+ if (neighbours > 1) attron(COLOR_PAIR(neighbours));
+ addch((world[x][y]) ? '0' + neighbours : ' ');
+ if (neighbours > 1) attroff(COLOR_PAIR(neighbours));
+ }
+ }
+}
+
+#ifdef ENABLE_CURSOR
+void print_cursor(uint8_t ** world, struct cursor cur) {
+ uint8_t color = (world[cur.x][cur.y]) ? 7 : 6;
+
+ move(cur.y, cur.x);
+ addch(CURSOR_CHAR | A_BLINK | A_BOLD | A_STANDOUT | COLOR_PAIR(color));
+}
+#endif
+
+#ifdef ENABLE_HOTKEYS
+#define MENU_WIDTH 25
+void print_menu(uint8_t width, uint8_t height) {
+ uint8_t startx, starty, mwdth_rel, posy = 0;
+
+ mwdth_rel = (uint8_t) (MENU_WIDTH)/2+10;
+ startx = (uint8_t) (width/2) - mwdth_rel;
+ starty = (uint8_t) (height/2) - (uint8_t) (mwdth_rel/2);
+ attron(COLOR_PAIR(1));
+ for (int i = 0; i < MENU_WIDTH; i++) {
+ mvprintw(starty, startx+i, "-");
+ mvprintw(starty+(mwdth_rel*2/3), startx+i, "-");
+ if (i % 2 == 0) {
+ mvprintw(starty+posy+1, startx-1, "|");
+ mvprintw(starty+posy+1, startx+MENU_WIDTH, "|");
+ posy++;
+ }
+ }
+ mvprintw(starty, startx-1, "+");
+ mvprintw(starty, startx+MENU_WIDTH, "+");
+ mvprintw(starty+posy+1, startx-1, "+");
+ mvprintw(starty+posy+1, startx+MENU_WIDTH, "+");
+ for (int i = startx; i < startx+MENU_WIDTH; i++) {
+ for (int j = starty+1; j <= starty+posy; j++) {
+ mvprintw(j, i, " ");
+ }
+ }
+ mvprintw(starty+1, startx+1, "q ~ Exit");
+ mvprintw(starty+3, startx+1, "c ~ Clear Screen");
+#if defined(RANDOM_SPAWNS)
+ mvprintw(starty+5, startx+1, "d ~ Rnd Spawns [%d]", (rnd_spawns == 0 ? 0 : 1));
+#endif
+ mvprintw(starty+7, startx+1, "p ~ Pause");
+ mvprintw(starty+9, startx+1, "+/- ~ Change Framerate");
+ mvprintw(starty+11, startx+1, "0-5 ~ Create Pattern");
+ attroff(COLOR_PAIR(1));
+}
+#endif
+
+/* set up ncurses screen */
+WINDOW * init_screen() {
+ WINDOW * win = initscr();
+ noecho();
+ timeout(0);
+ keypad(win, 1);
+ mousemask(BUTTON1_PRESSED, NULL);
+ mouseinterval(200);
+ curs_set(0);
+
+ start_color();
+ init_color(COLOR_CYAN, 500, 1000, 0); /* redefine as orange */
+
+ init_pair(1, COLOR_BLACK, COLOR_WHITE);
+ init_pair(2, COLOR_WHITE, COLOR_BLACK);
+ init_pair(3, COLOR_GREEN, COLOR_BLACK);
+ init_pair(4, COLOR_YELLOW, COLOR_BLACK);
+ init_pair(5, COLOR_CYAN, COLOR_BLACK);
+ init_pair(6, COLOR_BLUE, COLOR_BLACK);
+ init_pair(7, COLOR_MAGENTA, COLOR_BLACK);
+ init_pair(8, COLOR_RED, COLOR_BLACK);
+
+ return win;
+}
+
+uint8_t **init_world(WINDOW *win, uint8_t **worlds[2], uint8_t *width, uint8_t *height) {
+ getmaxyx(win, *height, *width);
+ for (int i = 0; i < 2; i++)
+ worlds[i] = create_world(*width, *height);
+ return (worlds[0]);
+}
+
+void free_all(uint8_t **worlds[2], uint8_t width, uint8_t height) {
+ free_world(worlds[0], width, height);
+ free_world(worlds[1], width, height);
+}
+
+// returns realloc'd && resized world
+uint8_t **resized(WINDOW *win, uint8_t **worlds[2], uint8_t *width, uint8_t *height) {
+ free_world(worlds[0], *width, *height);
+ free_world(worlds[1], *width, *height);
+ return (init_world(win, worlds, width, height));
+}
+
+int main(int argc, char * argv[]) {
+ WINDOW * win = init_screen();
+#ifdef ENABLE_CURSOR
+ MEVENT event;
+#endif
+
+ /* predefined patterns */
+ struct pattern patterns[] = {
+ {3, 3, (uint8_t *) start},
+ {3, 3, (uint8_t *) glider},
+ {5, 4, (uint8_t *) segler},
+ {3, 7, (uint8_t *) buffer},
+ {8, 4, (uint8_t *) kreuz},
+ {6, 6, (uint8_t *) ship}
+ };
+ struct cursor cur = {0, 0};
+ struct config cfg;
+
+ int generation = 0, input, framerate = 17;
+ uint8_t width, height;
+ uint8_t ** worlds[2], ** world;
+#if defined(RANDOM_SPAWNS)
+ int idle_gens = 0;
+ srand(time(NULL));
+#endif
+
+ memset(&cfg, '\0', sizeof(struct config));
+ /* initialize world */
+ world = init_world(win, worlds, &width, &height);
+ /* make the world real */
+ inhabit_world(patterns[3], width/2, height/2, worlds[0], width, height);
+
+ /* simulation loop */
+ while(!cfg.quit) {
+ if (!cfg.paused) {
+ /* calc next generation */
+ usleep(1 / (float) framerate * 1000000); /* sleep */
+ calc_next_gen(world, worlds[++generation % 2], width, height);
+ world = worlds[generation % 2]; /* new world */
+ }
+
+ /* handle events */
+ switch (input = getch()) {
+#ifdef ENABLE_HOTKEYS
+ case '+': /* increase framerate */
+ framerate++;
+ break;
+
+ case '-': /* decrease framerate */
+ if (framerate > 1) framerate--;
+ break;
+
+ case 'q': /* quit */
+ cfg.quit = 1;
+ break;
+#if defined(RANDOM_SPAWNS)
+ case 'd': /* disable random spawn */
+ rnd_spawns = ~rnd_spawns;
+ break;
+#endif
+ case 'p': /* pause */
+ cfg.paused ^= 1;
+ break;
+
+ case 'c': /* clean world */
+ clean_world(world, width, height);
+ generation = 0;
+ break;
+#endif
+#if defined(ENABLE_HOTKEYS) || defined(ENABLE_CURSOR)
+ case '0': /* insert pattern */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ inhabit_world(patterns[input - '0'], cur.x, cur.y, world, width, height);
+ break;
+#endif
+#ifdef ENABLE_CURSOR
+ case ' ': /* toggle cell at cursor position */
+ world[cur.x][cur.y] = (world[cur.x][cur.y]) ? 0 : 1;
+ break;
+
+ case KEY_MOUSE: /* move cursor to mouse posititon */
+ if (getmouse(&event) == OK && event.bstate & BUTTON1_PRESSED) {
+ cur.x = event.x;
+ cur.y = event.y;
+ if (cur.x >= width) cur.x = width - 1;
+ if (cur.y >= height) cur.y = height - 1;
+ world[cur.x][cur.y] = (world[cur.x][cur.y]) ? 0 : 1;
+ }
+ break;
+
+ case KEY_UP:
+ if (cur.y > 0) {
+ cur.y--;
+ }
+ break;
+
+ case KEY_DOWN:
+ if (cur.y < height-1) {
+ cur.y++;
+ }
+ break;
+
+ case KEY_LEFT:
+ if (cur.x > 0) {
+ cur.x--;
+ }
+ break;
+
+ case KEY_RIGHT:
+ if (cur.x < width-1) {
+ cur.x++;
+ }
+ break;
+ case KEY_RESIZE:
+ world = resized(win, worlds, &width, &height);
+ break;
+#endif
+ }
+
+#if defined(RANDOM_SPAWNS)
+ if (rnd_spawns) {
+ /* spawn a new pattern at a random position, if nothing happens */
+ idle_gens++;
+ if (idle_gens >= RANDOM_SPAWNS && !cfg.paused)
+ {
+ idle_gens = 0;
+ inhabit_world(patterns[rand() % (sizeof(patterns)/sizeof(patterns[0]))], rand() % (width - 1), rand() % (height - 1), world, width, height);
+ }
+ }
+#endif
+
+ /* update screen */
+ print_world(world, width, height);
+#ifdef ENABLE_CURSOR
+ print_cursor(world, cur);
+#endif
+#ifdef ENABLE_STATUS
+ attron(COLOR_PAIR(1));
+ for (int i = 0; i < width; i++) mvprintw(0, width - i, " ");
+ mvprintw(0, 0, "[generation:%4d] [cells:%3d] [fps:%2d] [width:%d] [height:%d] [cursor:%2d|%2d]", generation, calc_cell_count(world, width, height), framerate, width, height, cur.x, cur.y);
+ if (cfg .paused) mvprintw(0, width-6, "PAUSED");
+ attroff(COLOR_PAIR(1));
+#endif
+
+#ifdef ENABLE_HOTKEYS
+ if (cfg.paused) {
+ print_menu(width, height);
+ usleep(1);
+ }
+#endif
+ refresh();
+ }
+
+ free_all(worlds, width, height);
+ delwin(win);
+ endwin(); /* exit ncurses mode */
+ return (EXIT_SUCCESS);
+}