diff options
author | toni <matzeton@googlemail.com> | 2017-01-11 01:07:23 +0100 |
---|---|---|
committer | toni <matzeton@googlemail.com> | 2017-01-11 01:28:42 +0100 |
commit | d039eca8d4269333fb103042b37b15138ddcadfc (patch) | |
tree | b713320339fc77c2dbc42b1c253bc38a7f22d454 /gol.c |
initial commit
Diffstat (limited to 'gol.c')
-rw-r--r-- | gol.c | 469 |
1 files changed, 469 insertions, 0 deletions
@@ -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); +} |