cgol

Conway's Game of Life
git clone git://git.bitsmanent.org/cgol
Log | Files | Refs | README | LICENSE

cgol.c (4990B)


      1 /* Conway's Game of Life */
      2 
      3 #include <errno.h>
      4 #include <signal.h>
      5 #include <stdarg.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 #include <sys/ioctl.h>
     10 #include <time.h>
     11 #include <unistd.h>
     12 
     13 #include "arg.h"
     14 char *argv0;
     15 
     16 /* macros */
     17 #define LENGTH(X)       (sizeof X / sizeof X[0])
     18 
     19 /* VT100 escape sequences */
     20 #define CLEARRIGHT      "\33[0K"
     21 #define CURPOS          "\33[%d;%dH"
     22 #define CURSON          "\33[?25h"
     23 #define CURSOFF         "\33[?25l"
     24 
     25 /* function declarations */
     26 void *ecalloc(size_t nmemb, size_t size);
     27 void die(const char *fmt, ...);
     28 void draw(void);
     29 int msleep(int ms);
     30 int mvprintf(int x, int y, char *fmt, ...);
     31 int neighbors(int pos);
     32 int gridfile(char *file);
     33 void gridrand(int w, int h);
     34 void resize(int x, int y);
     35 void setup(void);
     36 void sigwinch(int unused);
     37 void tick(void);
     38 void usage(void);
     39 
     40 /* variables */
     41 int gw, gh, gs; /* grid, width, height, size */
     42 int *grid, *diff;
     43 int rows, cols;
     44 int generation;
     45 
     46 /* function implementations */
     47 void *
     48 ecalloc(size_t nmemb, size_t size) {
     49 	void *p;
     50 
     51 	if(!(p = calloc(nmemb, size)))
     52 		die("Cannot allocate memory.");
     53 	return p;
     54 }
     55 
     56 void
     57 die(const char *fmt, ...) {
     58 	va_list ap;
     59 	va_start(ap, fmt);
     60 	vfprintf(stderr, fmt, ap);
     61 	va_end(ap);
     62 
     63 	if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
     64 		fputc(' ', stderr);
     65 		perror(NULL);
     66 	} else {
     67 		fputc('\n', stderr);
     68 	}
     69 
     70 	exit(0);
     71 }
     72 
     73 void
     74 draw(void) {
     75 	int r, c;
     76 
     77 	printf(CURSOFF);
     78 	mvprintf(1, 1, "Conway's Game of Life ⋅ #%d ⋅ %0dx%0d" CLEARRIGHT, generation, rows, cols);
     79 	for(r = 0; r < rows - 1 && r < gh; ++r)
     80 		for(c = 0; c < cols && c < gw; ++c)
     81 			mvprintf(1+c, 2+r, "%s", grid[r * gw + c] ? "\033[07m \033[m" : " ");
     82 	printf(CURPOS CURSON, rows, cols);
     83 }
     84 
     85 int
     86 gridfile(char *file) {
     87 	FILE *fp;
     88 	char *line, *p;
     89 	size_t len = 0, nc;
     90 	int r, c;
     91 
     92 	fp = fopen(file, "r");
     93 	if(!fp)
     94 		return -1;
     95 	r = 0;
     96 	while((nc = getline(&line, &len, fp)) != -1) {
     97 		p = line;
     98 		c = 0;
     99 		while(*p && *p != '\n') {
    100 			grid[r * gw + c] = (*p == '1') ? 1 : 0;
    101 			++c;
    102 			p += 2;
    103 		}
    104 		++r;
    105 	}
    106 	free(line);
    107 	fclose(fp);
    108 	return 0;
    109 }
    110 
    111 void
    112 gridrand(int w, int h) {
    113 	int r, c;
    114 
    115 	for(r = 0; r < h && r < gh; ++r)
    116 		for(c = 0; c < w && c < gw; ++c)
    117 			grid[r * gw + c] = random() % 4 ? 0 : 1;
    118 }
    119 
    120 int
    121 msleep(int ms) {
    122 	struct timespec req = {0}, rem;
    123 	int r = ms / 1000;
    124 
    125 	if(r >= 1) {
    126 		req.tv_sec = r;
    127 		ms -= r * 1000;
    128 	}
    129 	if(ms)
    130 		req.tv_nsec = ms * 1000000;
    131 
    132 	while((r = nanosleep(&req, &rem)) == -1 && errno == EINTR)
    133 		req = rem;
    134 	return r;
    135 }
    136 
    137 int
    138 mvprintf(int x, int y, char *fmt, ...) {
    139 	va_list ap;
    140 	int len;
    141 
    142 	printf(CURPOS, y, x);
    143 	va_start(ap, fmt);
    144 	len = vfprintf(stdout, fmt, ap);
    145 	va_end(ap);
    146 	return len;
    147 }
    148 
    149 int
    150 neighbors(int pos) {
    151 	int r = pos / gw;
    152 	int c = pos % gw;
    153 	int sides[] = {
    154 		r - 1 >= 0 && c - 1 >= 0 ? (r - 1) * gw + (c - 1) : -1, /* top-left */
    155 		r - 1 >= 0 ? (r - 1) * gw + c : -1,                     /* top */
    156 		r - 1 >= 0 && c + 1 < gw ? (r - 1) * gw + (c + 1) : -1, /* top-right */
    157 		c + 1 < gw ? r * gw + (c + 1) : -1,                     /* right */
    158 		r + 1 < gh && c + 1 < gw ? (r + 1) * gw + (c + 1) : -1, /* bottom-right */
    159 		r + 1 < gh ? (r + 1) * gw + c : -1,                     /* bottom */
    160 		r + 1 < gh && c - 1 >= 0 ? (r + 1) * gw + (c - 1) : -1, /* bottom-left */
    161 		c - 1 >= 0 ? r * gw + (c - 1) : -1,                     /* left */
    162 	};
    163 	int n = 0, i;
    164 
    165 	for(i = 0; i < LENGTH(sides); ++i)
    166 		n += sides[i] >= 0 ? grid[sides[i]] : 0;
    167 	return n;
    168 }
    169 
    170 void
    171 resize(int wsrow, int wscol) {
    172 	rows = wsrow;
    173 	cols = wscol;
    174 }
    175 
    176 void
    177 setup(void) {
    178 	struct sigaction sa;
    179 	struct winsize ws;
    180 
    181 	sa.sa_flags = 0;
    182 	sigemptyset(&sa.sa_mask);
    183 	sa.sa_handler = sigwinch;
    184 	sigaction(SIGWINCH, &sa, NULL);
    185 	ioctl(0, TIOCGWINSZ, &ws);
    186 	resize(ws.ws_row, ws.ws_col);
    187 	setbuf(stdout, NULL);
    188 }
    189 
    190 void
    191 sigwinch(int unused) {
    192 	struct winsize ws;
    193 
    194 	ioctl(0, TIOCGWINSZ, &ws);
    195 	resize(ws.ws_row, ws.ws_col);
    196 	draw();
    197 }
    198 
    199 void
    200 tick(void) {
    201 	int i, n;
    202 
    203 	for(i = 0; i < gs; ++i) {
    204 		n = neighbors(i);
    205 		if(grid[i] == 1 && (n < 2 || n > 3))
    206 			diff[i] = 0;
    207 		else if(grid[i] == 0 && n == 3)
    208 			diff[i] = 1;
    209 		else
    210 			diff[i] = grid[i];
    211 	}
    212 	for(i = 0; i < gs; ++i)
    213 		if(grid[i] != diff[i])
    214 			grid[i] = diff[i];
    215 	++generation;
    216 }
    217 
    218 void
    219 usage(void) {
    220 	die("Usage: %s [-v] [-dgn <arg>] [file]", argv0);
    221 }
    222 
    223 int
    224 main(int argc, char *argv[]) {
    225 	int delay = 250, gen = 0, ticks = 0;
    226 	char *file = NULL;
    227 
    228 	ARGBEGIN {
    229 	case 'd': delay = atoi(EARGF(usage())); break;
    230 	case 'g': gen = atoi(EARGF(usage())); break;
    231 	case 'n': ticks = atoi(EARGF(usage())); break;
    232 	case 'v': die("cgol-"VERSION);
    233 	default: usage();
    234 	} ARGEND;
    235 
    236 	if(argc) file = argv[0];
    237 	setup();
    238 
    239 	if(!gw) gw = 256;
    240 	if(!gh) gh = 256;
    241 	gs = gw * gw;
    242 	grid = ecalloc(gs, sizeof(int));
    243 	diff = ecalloc(gs, sizeof(int));
    244 
    245 	if(file) {
    246 		if(gridfile(file))
    247 			die("%s: %s:", argv0, file);
    248 	}
    249 	else {
    250 		srandom(time(NULL));
    251 		gridrand(cols, rows);
    252 	}
    253 
    254 	while(gen-- > 0)
    255 		tick();
    256 	while(1) {
    257 		draw();
    258 		tick();
    259 		if(!--ticks)
    260 			break;
    261 		msleep(delay);
    262 	}
    263 	/* NOTREACHED */
    264 	free(grid);
    265 	free(diff);
    266 	return 0;
    267 }