/* * src/cgol.c * Curses Game of Life * * Copyright (C) 2011 Patrick "P. J." McDermott * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include void init_curses(); void init_game(); void fini_game(); void fini_curses(); void tick(); int count_live_neighbors(int i, int j); bool get_cell(int i, int j); void set_cell(int i, int j, bool s); void print_cell(int i, int j, bool next); void usage(const char *invocation); static int width, height; static double seedprob; static int genrate, gens; static bool *grid_cur, *grid_next; int main(int argc, char **argv) { int opt, i, c; width = 0; height = 0; seedprob = 0.5; genrate = 1; /* TODO: Units. */ gens = 0; while ((opt = getopt(argc, argv, "w:h:s:r:n:")) != -1) { switch (opt) { case 'w': width = atoi(optarg); break; case 'h': height = atoi(optarg); break; case 's': seedprob = atof(optarg); break; case 'r': genrate = atoi(optarg); break; case 'n': gens = atoi(optarg); break; default: usage(argv[0]); return 1; } } /* Sanity. Let's check it. */ if (seedprob <= 0 || seedprob > 1) { fprintf(stderr, "Error: Ensure 0 < SEEDPROB <= 1\n"); return 2; } init_curses(); init_game(); /* TODO: Write a better main event loop with a good controller and timer. */ timeout(1000); /* NB: i starts at -1 so we don't exit immediately after showing the last * desired state. */ for (i = -1; gens == 0 || i < gens; ++i) { c = getch(); if (c == 'q') { break; } /* Calculate the next generation only if it is to be shown on the * screen. */ if (gens == 0 || i + 1 < gens) { tick(); } } fini_game(); fini_curses(); return 0; } void init_curses() { int maxwidth, maxheight; initscr(); noecho(); raw(); curs_set(0); /* Leave room for spaces between cells, but allow for one more cell on odd- * width terminals. */ maxwidth = (COLS - 2) / 2 + COLS % 1; maxheight = (LINES - 2); if (width == 0 || width > maxwidth) { width = maxwidth; } if (height == 0 || height > maxheight) { height = maxheight; } } void init_game() { size_t size; int i, j, n; double r; n = width * height; /* Allocate and initialize the grids. */ size = sizeof(bool) * n; grid_cur = malloc(size); memset(grid_cur, 0, size); grid_next = malloc(size); memset(grid_next, 0, size); srand(time(NULL)); /* Generate the seed pattern. */ for (i = 0; i < n; ++i) { r = rand() / (double) RAND_MAX; if (r > seedprob) { /* Dead. */ grid_cur[i] = false; grid_next[i] = false; } else { /* Alive. */ grid_cur[i] = true; grid_next[i] = true; } } /* Print the grid. */ for (i = 0; i < height; ++i) { for (j = 0; j < width; ++j) { print_cell(i, j, false); } } } void fini_game() { free(grid_cur); free(grid_next); } void fini_curses() { delwin(stdscr); endwin(); } void tick() { int i, j, n; for (i = 0; i < height; ++i) { for (j = 0; j < width; ++j) { n = count_live_neighbors(i, j); if (get_cell(i, j) == true) { if (n < 2 || n > 3) { set_cell(i, j, false); print_cell(i, j, true); } } else { if (n == 3) { set_cell(i, j, true); print_cell(i, j, true); } } } } memcpy(grid_cur, grid_next, sizeof(bool) * width * height); refresh(); } int count_live_neighbors(int i, int j) { return get_cell(i - 1, j - 1) + get_cell(i - 1, j) + get_cell(i - 1, j + 1) + get_cell(i, j - 1) + get_cell(i, j + 1) + get_cell(i + 1, j - 1) + get_cell(i + 1, j) + get_cell(i + 1, j + 1); } bool get_cell(int i, int j) { if (i < 0 || j < 0 || i >= height || j >= width) { /* This is something of a sanity check. Anything off the finite grid * is by definition dead. With this, count_live_neighbors() can be * dumb. */ return 0; } else { return grid_cur[i * width + j]; } } void set_cell(int i, int j, bool s) { grid_next[i * width + j] = s; } void print_cell(int i, int j, bool next) { /* TODO: Center this. */ if (next) { mvprintw(1 + i, 1 + j * 2, "%c", grid_next[i * width + j] ? 'o' : ' '); } else { mvprintw(1 + i, 1 + j * 2, "%c", grid_cur[i * width + j] ? 'o' : ' '); } } void usage(const char *invocation) { fprintf(stderr, "Usage: %s [-w WIDTH] [-h HEIGHT] [-s SEEDPROB] [-r RATE] " "[-n GENERATIONS]\n", invocation); }