diff --git a/bin/conway b/bin/conway new file mode 100755 index 0000000..dbd0aff Binary files /dev/null and b/bin/conway differ diff --git a/src/conway.c b/src/conway.c index 5eea941..6ae42af 100644 --- a/src/conway.c +++ b/src/conway.c @@ -1,36 +1,69 @@ #include #include +#include #include #include +#include /* configuration */ #define CELL_CHAR '#' #define CURSOR_CHAR 'X' -#define WIDTH 80 -#define HEIGHT 24 #define ESC 27 -#define FRAME_RATE 5 +#define FRAME_RATE 17 -void clear_screen() { - printf("%c[2J", ESC); -} +/* start pattern */ +uint8_t start[3][3] = { + {0, 1, 1}, + {1, 1, 0}, + {0, 1, 0} +}; -void set_cursor(int row, int col) { - printf("%c[%d;%dH", ESC, row, col); -} +uint8_t glider[3][3] = { + {0, 1, 0}, + {0, 0, 1}, + {1, 1, 1} +}; -void clean_world(uint8_t world[WIDTH][HEIGHT]) { - size_t a, b; +struct cursor { + uint8_t x; + uint8_t y; +}; - for (a = 0; a < WIDTH; a++) { - for (b = 0; b < HEIGHT; b++) { - world[a][b] = 0; - } +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)); } } -void inhabit_world(uint8_t pattern[3][3], size_t x, size_t y, uint8_t world[WIDTH][HEIGHT]) { - size_t a, b; +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; +} + +void destroy_world(uint8_t ** world, uint8_t width) { + uint8_t a; + + for (a = 0; a < width; a++) { + free(world[a]); + } + free(world); +} + +void inhabit_world(uint8_t pattern[3][3], 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++) { @@ -39,14 +72,31 @@ void inhabit_world(uint8_t pattern[3][3], size_t x, size_t y, uint8_t world[WIDT } } -uint8_t calc_cell_neighbours(size_t x, size_t y, uint8_t world[WIDTH][HEIGHT]) { +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; - int8_t a, b; + int a, b; for (a = x-1; a <= x+1; a++) { for (b = y-1; b <= y+1; b++) { - if (a >= 0 && b >= 0 && a != x && b != y && world[a][b] > 0) { - neighbours++; + if (a == x && b == y) + continue; + + if (a >= 0 && b >= 0 && + a < width && b < height) { + neighbours += (world[a][b] > 0) ? 1 : 0; } } } @@ -54,84 +104,173 @@ uint8_t calc_cell_neighbours(size_t x, size_t y, uint8_t world[WIDTH][HEIGHT]) { return neighbours; } -uint8_t calc_next_cell_gen(size_t x, size_t y, uint8_t world[WIDTH][HEIGHT]) { - uint8_t neighbours = calc_cell_neighbours(x, y, world); +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 (neighbours < 2) { - return 0; + if (alive) { + if (neighbours > 3 || neighbours < 2) { + return 0; /* died by over-/underpopulation */ + } + else { + return neighbours; + } } - else if (neighbours > 3) { - return 0; - } - else if (neighbours == 2 || neighbours == 3) { - return 1; + else if (neighbours == 3) { + return neighbours; } else { - return -1; + return 0; } } -void calc_next_gen(uint8_t world[WIDTH][HEIGHT]) { - uint8_t next_gen[WIDTH][HEIGHT]; - size_t x, y; +void calc_next_gen(uint8_t ** world, uint8_t ** next_gen, uint8_t width, uint8_t height) { + uint8_t x, y; - clean_world(next_gen); - - for (x = 0; x < WIDTH; x++) { - for (y = 0; y < HEIGHT; y++) { - next_gen[x][y] = calc_next_cell_gen(x, y, world); + 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); } } - memcpy(world, next_gen, WIDTH*HEIGHT); -} - -void print_world(uint8_t world[WIDTH][HEIGHT]) { - size_t x, y; - - /* reset cursor */ - set_cursor(0, 0); - - for (y = 0; y < HEIGHT; y++) { - for (x = 0; x < WIDTH; x++) { - /*printf("%c", (world[x][y]) ? CELL_CHAR : ' ');*/ - printf("%d,", (world[x][y] != 0) ? (int) world[x][y] : ' '); + /* copy world */ + for (x = 0; x < width; x++) { + for (y = 0; y < height; y++) { + world[x][y] = next_gen[x][y]; } - printf("\n"); /* new line */ - } - - for (y = 0; y < HEIGHT; y++) { - printf("|"); - for (x = 0; x < WIDTH; x++) { - printf("%c", (world[x][y]) ? CELL_CHAR : ' '); - } - printf("|\n"); /* new line */ } } -/* start pattern */ -uint8_t glider[3][3] = { - {0, 1, 0}, - {0, 0, 1}, - {1, 1, 1} -}; +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++) { + printw("%c", (world[x][y] > 0) ? CELL_CHAR : ' '); + } + } +} + +void print_cursor(uint8_t ** world, struct cursor cur) { + uint8_t color = (world[cur.x][cur.y] + + move(cur.y, cur.x); + attron(COLOR_PAIR(1)); + addch(CURSOR_CHAR); + attroff(COLOR_PAIR(1)); +} + +WINDOW * init_screen() { + WINDOW * win = initscr(); + noecho(); + timeout(0); + keypad(win, 1); + 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); + + return win; +} int main(int argc, char * argv[]) { - uint8_t world[WIDTH][HEIGHT]; - int counter = 0; + WINDOW * win = init_screen(); + MEVENT event; - clean_world(world); /* initialize world */ - inhabit_world(glider, WIDTH/2, HEIGHT/2, world); /* inhabit world */ - clear_screen(); -print_world(world); + struct cursor cur = {0, 0}; + + int generation = 0; + uint8_t width, height, paused = 1; + uint8_t ** world, ** next_gen; + + getmaxyx(win, height, width); + + /* initialize world */ + world = create_world(width, height); + next_gen = create_world(width, height); + + inhabit_world(start, width/2, height/2, world); /* simulation loop */ - while(0) { - calc_next_gen(world); - print_world(world); - usleep((float) 1 / FRAME_RATE * 1000000); /* sleep */ - printf("frame: %d\n", counter++); + while(1) { + /* handle events */ + switch (getch()) { + case 'q': + endwin(); + exit(0); + break; + + case 'p': + paused ^= 1; + break; + + case 'c': + clean_world(world, width, height); + break; + + case 'g': + inhabit_world(glider, cur.x, cur.y, world); + break; + + case ' ': + world[cur.x][cur.y] = (world[cur.x][cur.y]) ? 0 : 1; + break; + + case KEY_MOUSE: + if (getmouse(&event) == OK && event.bstate & BUTTON1_CLICKED) { + cur.x = event.x; + cur.y = event.y; + 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; + } + + if (!paused) { + usleep((float) 1 / FRAME_RATE * 1000000); /* sleep */ + calc_next_gen(world, next_gen, width, height); + generation++; + } + + 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); + if (paused) mvprintw(0, width-6, "PAUSED"); + attroff(COLOR_PAIR(2)); + + refresh(); } + endwin(); return 0; }