finished conways game of life
This commit is contained in:
parent
3147b711e9
commit
f6316ed56c
2 changed files with 142 additions and 73 deletions
BIN
bin/conway
BIN
bin/conway
Binary file not shown.
215
src/conway.c
215
src/conway.c
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue