finished conways game of life

This commit is contained in:
Steffen Vogel 2010-11-30 13:20:43 +01:00
parent 3147b711e9
commit f6316ed56c
2 changed files with 142 additions and 73 deletions

Binary file not shown.

View file

@ -1,3 +1,13 @@
/**
* C implementation of Conway's Game of Life
*
* based on libncurses
*
* @author Steffen Vogel <info@steffenvogel.de>
* @copyright Copyright (c) 2010, Steffen Vogel
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@ -6,22 +16,12 @@
#include <curses.h>
/* 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;
}