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 }