diff --git a/bin/conway b/bin/conway index dbd0aff..ecf4f96 100755 Binary files a/bin/conway and b/bin/conway differ diff --git a/src/conway.c b/src/conway.c index 6ae42af..954c73f 100644 --- a/src/conway.c +++ b/src/conway.c @@ -1,3 +1,13 @@ +/** + * C implementation of Conway's Game of Life + * + * based on libncurses + * + * @author Steffen Vogel + * @copyright Copyright (c) 2010, Steffen Vogel + * @license http://opensource.org/licenses/gpl-license.php GNU Public License + */ + #include #include #include @@ -6,22 +16,12 @@ #include /* configuration */ -#define CELL_CHAR '#' -#define CURSOR_CHAR 'X' -#define ESC 27 -#define FRAME_RATE 17 +#define CURSOR_CHAR '#' -/* start pattern */ -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} +struct pattern { + uint8_t width; + uint8_t height; + uint8_t * data; }; struct cursor { @@ -29,30 +29,56 @@ struct cursor { 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} +}; + +/* 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++) { + 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 = malloc(width * sizeof(uint8_t *)); int a; for (a = 0; a < width; a++) { world[a] = malloc(height * sizeof(uint8_t)); - if (world[a] == NULL) { - endwin(); - fprintf(stderr, "Cant allocate memory!\n"); - exit(-1); - } } clean_world(world, width, height); - return world; } +/* free memory for world */ void destroy_world(uint8_t ** world, uint8_t width) { uint8_t a; @@ -62,16 +88,18 @@ void destroy_world(uint8_t ** world, uint8_t width) { free(world); } -void inhabit_world(uint8_t pattern[3][3], uint8_t x, uint8_t y, uint8_t ** 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 a, b; - for (a = 0; a < 3; a++) { - for (b = 0; b < 3; b++) { - world[x+b][y+a] = pattern[a][b]; + for (a = 0; a < pattern.height; a++) { + for (b = 0; b < pattern.width; b++) { + world[x+b][y+a] = 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; @@ -90,18 +118,21 @@ uint8_t calc_cell_neighbours(uint8_t x, uint8_t y, uint8_t ** world, uint8_t wid int a, b; for (a = x-1; a <= x+1; a++) { - for (b = y-1; b <= y+1; b++) { - if (a == x && b == y) - continue; + int c = a; + if (a < 0) c += width; + if (a >= width) c -= width; - if (a >= 0 && b >= 0 && - a < width && b < height) { - neighbours += (world[a][b] > 0) ? 1 : 0; - } + 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; + 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) { @@ -113,15 +144,15 @@ uint8_t calc_next_cell_gen(uint8_t x, uint8_t y, uint8_t ** world, uint8_t width if (neighbours > 3 || neighbours < 2) { return 0; /* died by over-/underpopulation */ } - else { - return neighbours; + else { + return 1; /* kept alive */ } } else if (neighbours == 3) { - return neighbours; + return 1; /* born */ } else { - return 0; + return 0; /* still dead */ } } @@ -142,6 +173,7 @@ void calc_next_gen(uint8_t ** world, uint8_t ** next_gen, uint8_t width, uint8_t } } +/* 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 */ @@ -149,20 +181,23 @@ void print_world(uint8_t ** world, uint8_t width, uint8_t height) { /* cells */ for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - printw("%c", (world[x][y] > 0) ? CELL_CHAR : ' '); + 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)); } } } void print_cursor(uint8_t ** world, struct cursor cur) { - uint8_t color = (world[cur.x][cur.y] + uint8_t color = (world[cur.x][cur.y]) ? 7 : 6; move(cur.y, cur.x); - attron(COLOR_PAIR(1)); - addch(CURSOR_CHAR); - attroff(COLOR_PAIR(1)); + addch(CURSOR_CHAR | A_BLINK | A_BOLD | A_STANDOUT | COLOR_PAIR(color)); } +/* set up ncurses screen */ WINDOW * init_screen() { WINDOW * win = initscr(); noecho(); @@ -171,9 +206,18 @@ WINDOW * init_screen() { mousemask(BUTTON1_CLICKED, NULL); mouseinterval(200); curs_set(0); + start_color(); - init_pair(1, COLOR_RED, COLOR_BLACK); - init_pair(2, COLOR_GREEN, COLOR_BLACK); + 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; } @@ -182,46 +226,71 @@ int main(int argc, char * argv[]) { WINDOW * win = init_screen(); MEVENT event; + /* 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} + }; struct cursor cur = {0, 0}; - int generation = 0; - uint8_t width, height, paused = 1; - uint8_t ** world, ** next_gen; - - getmaxyx(win, height, width); + int generation = 0, input, framerate = 17; + uint8_t width, height, paused = 0; /* initialize world */ - world = create_world(width, height); - next_gen = create_world(width, height); - - inhabit_world(start, width/2, height/2, world); + getmaxyx(win, height, width); + uint8_t ** worlds[2] = { + create_world(width, height), /* current generation */ + create_world(width, height) /* next generation */ + }, ** world = worlds[0]; + inhabit_world(patterns[3], width/2, height/2, worlds[0]); /* simulation loop */ while(1) { + /* calc next generation */ + if (!paused) { + usleep(1 / (float) framerate * 1000000); /* sleep */ + calc_next_gen(world, worlds[++generation % 2], width, height); + world = worlds[generation % 2]; /* new world */ + } + /* handle events */ - switch (getch()) { - case 'q': + switch (input = getch()) { + case '+': /* increase framerate */ + framerate++; + break; + + case '-': /* decrease framerate */ + if (framerate > 1) framerate--; + break; + + case 'q': /* quit */ endwin(); exit(0); break; - case 'p': + case 'p': /* pause */ paused ^= 1; break; - case 'c': + case 'c': /* clean world */ clean_world(world, width, height); + generation = 0; break; - case 'g': - inhabit_world(glider, cur.x, cur.y, world); + case '0': /* insert pattern */ + case '1': + case '2': + case '3': + inhabit_world(patterns[input - '0'], cur.x, cur.y, world); break; - case ' ': + case ' ': /* toggle cell at cursor position */ world[cur.x][cur.y] = (world[cur.x][cur.y]) ? 0 : 1; break; - case KEY_MOUSE: + case KEY_MOUSE: /* move cursor to mouse posititon */ if (getmouse(&event) == OK && event.bstate & BUTTON1_CLICKED) { cur.x = event.x; cur.y = event.y; @@ -254,23 +323,23 @@ int main(int argc, char * argv[]) { break; } - if (!paused) { - usleep((float) 1 / FRAME_RATE * 1000000); /* sleep */ - calc_next_gen(world, next_gen, width, height); - generation++; - } - + /* update screen */ print_world(world, width, height); print_cursor(world, cur); - attron(COLOR_PAIR(2)); - mvprintw(0, 0, "generation: %d\tcells: %d framerate: %d fps\twidth: %d\theight: %d cursor: (%d|%d)\n", generation, calc_cell_count(world, width, height), FRAME_RATE, width, height, cur.x, cur.y); + attron(COLOR_PAIR(1)); + mvprintw(0, 0, "generation: %d\tcells: %d framerate: %d fps\twidth: %d\theight: %d cursor: (%d|%d)\n", generation, calc_cell_count(world, width, height), framerate, width, height, cur.x, cur.y); if (paused) mvprintw(0, width-6, "PAUSED"); - attroff(COLOR_PAIR(2)); + attroff(COLOR_PAIR(1)); refresh(); } - endwin(); + /* householding */ + destroy_world(worlds[0], width); + destroy_world(worlds[1], width); + + endwin(); /* exit ncurses mode */ + return 0; }