Updated to remove termbox dependency.
This commit is contained in:
parent
3f56f36dfb
commit
ced4364da1
@ -8,10 +8,10 @@ on the command line.
|
|||||||
## Usage
|
## Usage
|
||||||
Command line flags:
|
Command line flags:
|
||||||
|
|
||||||
* `-S[0-8]*` the rules for when an alive cell survives
|
* `-S [0-8]*` the rules for when an alive cell survives
|
||||||
* `-B[0-8]*` the rules for when a new cell is born
|
* `-B [0-8]*` the rules for when a new cell is born
|
||||||
* `-A[0-256]` provide a color for alive cells
|
* `-A <color>` provide a color for alive cells
|
||||||
* `-D[0-256]` provide a color for dead cells
|
* `-D <color>` provide a color for dead cells
|
||||||
|
|
||||||
Some interesting rulesets can be found in [this list](http://psoup.math.wisc.edu/mcell/rullex_life.html).
|
Some interesting rulesets can be found in [this list](http://psoup.math.wisc.edu/mcell/rullex_life.html).
|
||||||
|
|
||||||
|
162
conway.c
162
conway.c
@ -1,77 +1,108 @@
|
|||||||
// A console conway's game of life program by Bruce Hill
|
// A console conway's game of life program by Bruce Hill
|
||||||
// Released under the MIT license, see LICENSE for details.
|
// Released under the MIT license, see LICENSE for details.
|
||||||
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <termbox.h>
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define CELL(cells, x, y) cells[(((y)+H) % H)*W + (((x)+W) % W)]
|
#define CELL(cells, x, y) cells[(((y)+H) % H)*W + (((x)+W) % W)]
|
||||||
|
|
||||||
static char rules[2][9] = {{0,0,0,1,0,0,0,0,0}, {0,0,1,1,0,0,0,0,0}};
|
static char rules[2][9] = {{0,0,0,1,0,0,0,0,0}, {0,0,1,1,0,0,0,0,0}};
|
||||||
static unsigned int W, H, DEAD = 0, ALIVE = 7;
|
static unsigned int W, H;
|
||||||
|
static char alive_color[64] = "\033[47m", dead_color[64] = "\033[0m";
|
||||||
|
|
||||||
static void update(const char *cells, char *future_cells)
|
static void update(FILE *out, const char *cells, char *future_cells)
|
||||||
{
|
{
|
||||||
|
const char *color = NULL;
|
||||||
for (int y = 0; y < H; y++) {
|
for (int y = 0; y < H; y++) {
|
||||||
|
fprintf(out, "\033[%d;1H", y+1);
|
||||||
for (int x = 0; x < W; x++) {
|
for (int x = 0; x < W; x++) {
|
||||||
int neighbors =
|
int neighbors =
|
||||||
CELL(cells,x-1,y-1) + CELL(cells,x,y-1) + CELL(cells,x+1,y-1)
|
CELL(cells,x-1,y-1) + CELL(cells,x,y-1) + CELL(cells,x+1,y-1)
|
||||||
+ CELL(cells,x-1,y) + CELL(cells,x+1,y)
|
+ CELL(cells,x-1,y) + CELL(cells,x+1,y)
|
||||||
+ CELL(cells,x-1,y+1) + CELL(cells,x,y+1) + CELL(cells,x+1,y+1);
|
+ CELL(cells,x-1,y+1) + CELL(cells,x,y+1) + CELL(cells,x+1,y+1);
|
||||||
CELL(future_cells, x, y) = rules[CELL(cells, x, y)][neighbors];
|
CELL(future_cells, x, y) = rules[CELL(cells, x, y)][neighbors];
|
||||||
tb_change_cell(2*x, y, ' ', 0, CELL(future_cells, x, y) ? ALIVE : DEAD);
|
if (CELL(future_cells, x, y) && color != alive_color) {
|
||||||
tb_change_cell(2*x+1, y, ' ', 0, CELL(future_cells, x, y) ? ALIVE : DEAD);
|
fputs(alive_color, out);
|
||||||
|
color = alive_color;
|
||||||
|
} else if (!CELL(future_cells, x, y) && color != dead_color) {
|
||||||
|
fputs(dead_color, out);
|
||||||
|
color = dead_color;
|
||||||
|
}
|
||||||
|
fputs(" ", out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fputs("\033[0m", out);
|
||||||
|
fflush(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
int ret = 0;
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
if (strcmp(argv[i], "--help") == 0) {
|
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
|
||||||
usage:
|
usage:
|
||||||
printf("Conway's Game of Life Viewer\nUsage: conway [-S<survival rules>] [-B<birth rules>] [-A<alive color>] [-D<dead color>]\n");
|
printf("Conway's Game of Life Viewer\nUsage: conway [-S<survival rules>] [-B<birth rules>] [-A<alive color>] [-D<dead color>]\n");
|
||||||
return 0;
|
return 0;
|
||||||
} else if (strncmp(argv[i], "-B", 2) == 0) {
|
} else if (strcmp(argv[i], "-B") == 0) {
|
||||||
|
if (i + 1 >= argc) goto usage;
|
||||||
memset(rules[0], 0, sizeof(rules[0]));
|
memset(rules[0], 0, sizeof(rules[0]));
|
||||||
for (char *p = &argv[i][2]; *p; p++)
|
for (char *p = argv[++i]; *p; p++)
|
||||||
if ('0' <= *p && *p <= '8')
|
if ('0' <= *p && *p <= '8')
|
||||||
rules[0][*p - '0'] = 1;
|
rules[0][*p - '0'] = 1;
|
||||||
} else if (strncmp(argv[i], "-S", 2) == 0) {
|
} else if (strcmp(argv[i], "-S") == 0) {
|
||||||
|
if (i + 1 >= argc) goto usage;
|
||||||
memset(rules[1], 0, sizeof(rules[1]));
|
memset(rules[1], 0, sizeof(rules[1]));
|
||||||
for (char *p = &argv[i][2]; *p; p++)
|
for (char *p = argv[++i]; *p; p++)
|
||||||
if ('0' <= *p && *p <= '8')
|
if ('0' <= *p && *p <= '8')
|
||||||
rules[1][*p - '0'] = 1;
|
rules[1][*p - '0'] = 1;
|
||||||
} else if (strncmp(argv[i], "-A", 2) == 0) {
|
} else if (strcmp(argv[i], "-A") == 0) {
|
||||||
ALIVE = atoi(&argv[i][2]);
|
if (i + 1 >= argc) goto usage;
|
||||||
if (ALIVE & ~0xFF) {
|
sprintf(alive_color, "\033[%sm", argv[++i]);
|
||||||
fprintf(stderr, "Colors must be between 0-255\n");
|
} else if (strcmp(argv[i], "-D") == 0) {
|
||||||
return 1;
|
sprintf(dead_color, "\033[%sm", argv[++i]);
|
||||||
}
|
|
||||||
} else if (strncmp(argv[i], "-D", 2) == 0) {
|
|
||||||
DEAD = atoi(&argv[i][2]);
|
|
||||||
if (DEAD & ~0xFF) {
|
|
||||||
fprintf(stderr, "Colors must be between 0-255\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
goto usage;
|
goto usage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = tb_init();
|
int ttyin_fd = open("/dev/tty", O_RDONLY | O_NONBLOCK);
|
||||||
if (ret) {
|
if (ttyin_fd < 0) {
|
||||||
fprintf(stderr, "tb_init() failed with error code %d\n", ret);
|
fprintf(stderr, "Couldn't open terminal input.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
tb_select_input_mode(TB_INPUT_MOUSE);
|
FILE *tty_out = fopen("/dev/tty", "w");
|
||||||
tb_select_output_mode(TB_OUTPUT_256);
|
if (!tty_out) {
|
||||||
|
fprintf(stderr, "Couldn't open terminal output.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct termios orig_termios, termios;
|
||||||
|
tcgetattr(fileno(tty_out), &orig_termios);
|
||||||
|
memcpy(&termios, &orig_termios, sizeof(termios));
|
||||||
|
cfmakeraw(&termios);
|
||||||
|
termios.c_cc[VMIN] = 10;
|
||||||
|
termios.c_cc[VTIME] = 1;
|
||||||
|
if (tcsetattr(fileno(tty_out), TCSAFLUSH, &termios) == -1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
struct winsize wsize = {0};
|
||||||
|
ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsize);
|
||||||
|
fputs("\033[?25l" // Hide cursor
|
||||||
|
"\033[?1049;" // Use alternate screen
|
||||||
|
"1000;" // Enable mouse x/y on press
|
||||||
|
"1002;" // Enable mouse dragging
|
||||||
|
"1006" // Use mouse SGR mode
|
||||||
|
"h", tty_out);
|
||||||
|
|
||||||
char *buffers[2];
|
char *buffers[2];
|
||||||
int flipflop = 0;
|
int flipflop = 0;
|
||||||
resize:
|
resize:
|
||||||
|
// TODO: handle resize in SIGWINCH handler
|
||||||
// Cells are 2 characters wide so they look square
|
// Cells are 2 characters wide so they look square
|
||||||
W = tb_width()/2, H = tb_height();
|
W = wsize.ws_col/2, H = wsize.ws_row;
|
||||||
buffers[0] = calloc(W*H, sizeof(char));
|
buffers[0] = calloc(W*H, sizeof(char));
|
||||||
buffers[1] = calloc(W*H, sizeof(char));
|
buffers[1] = calloc(W*H, sizeof(char));
|
||||||
for (int x = 0; x < W; x++)
|
for (int x = 0; x < W; x++)
|
||||||
@ -79,49 +110,58 @@ int main(int argc, char **argv) {
|
|||||||
CELL(buffers[flipflop], x, y) = random() % 2;
|
CELL(buffers[flipflop], x, y) = random() % 2;
|
||||||
|
|
||||||
int draw_mode = 1, paused = 0;
|
int draw_mode = 1, paused = 0;
|
||||||
tb_set_clear_attributes(0, DEAD);
|
fputs("\033[2J", tty_out);
|
||||||
tb_clear();
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (!paused) {
|
if (!paused) {
|
||||||
update(buffers[flipflop], buffers[!flipflop]);
|
update(tty_out, buffers[flipflop], buffers[!flipflop]);
|
||||||
flipflop = !flipflop;
|
flipflop = !flipflop;
|
||||||
}
|
}
|
||||||
present:
|
present:
|
||||||
tb_present();
|
|
||||||
usleep(60000);
|
usleep(60000);
|
||||||
struct tb_event ev;
|
|
||||||
int drew = 0;
|
int drew = 0;
|
||||||
while (tb_peek_event(&ev, 0)) {
|
char key;
|
||||||
switch (ev.type) {
|
while (read(ttyin_fd, &key, 1) == 1) {
|
||||||
case TB_EVENT_RESIZE:
|
switch (key) {
|
||||||
free(buffers[0]);
|
case '\3': ret = 1; goto done; // CTRL-C
|
||||||
free(buffers[1]);
|
case 'q': goto done;
|
||||||
goto resize;
|
case 'p': paused ^= 1; break;
|
||||||
|
case 'c': memset(buffers[flipflop], 0, W*H); break;
|
||||||
case TB_EVENT_KEY:
|
case ' ': draw_mode ^= 1; break;
|
||||||
if (ev.key == TB_KEY_ESC && tb_peek_event(&ev, 50) == 0)
|
case '<': { // Mouse dragging/clicks
|
||||||
goto done;
|
int buttons = 0, x = 0, y = 0;
|
||||||
else if (ev.ch == 'q')
|
char buf;
|
||||||
goto done;
|
while (read(ttyin_fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
|
||||||
else if (ev.ch == 'p')
|
buttons = buttons * 10 + (buf - '0');
|
||||||
paused = !paused;
|
if (buf != ';') return -1;
|
||||||
else if (ev.ch == 'c')
|
while (read(ttyin_fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
|
||||||
memset(buffers[flipflop], 0, W*H);
|
x = x * 10 + (buf - '0');
|
||||||
else if (ev.key == TB_KEY_SPACE)
|
if (buf != ';') return -1;
|
||||||
draw_mode = !draw_mode;
|
while (read(ttyin_fd, &buf, 1) == 1 && '0' <= buf && buf <= '9')
|
||||||
break;
|
y = y * 10 + (buf - '0');
|
||||||
|
if (buf != 'm' && buf != 'M') return -1;
|
||||||
case TB_EVENT_MOUSE:
|
if ((buttons & (~32)) == 0 && ((x-1)/2) < W) {
|
||||||
CELL(buffers[flipflop], ev.x/2, ev.y) = draw_mode;
|
CELL(buffers[flipflop], (x-1)/2, y-1) = draw_mode;
|
||||||
tb_change_cell(2*(ev.x/2), ev.y, ' ', 0, draw_mode ? ALIVE : DEAD);
|
fprintf(tty_out, "\033[%d;%dH%s \033[0m",
|
||||||
tb_change_cell(2*(ev.x/2)+1, ev.y, ' ', 0, draw_mode ? ALIVE : DEAD);
|
y, 2*((x-1)/2)+1,
|
||||||
|
draw_mode ? alive_color : dead_color);
|
||||||
|
fflush(tty_out);
|
||||||
drew = 1;
|
drew = 1;
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (drew) goto present;
|
if (drew) goto present;
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
tb_shutdown();
|
tcsetattr(fileno(tty_out), TCSAFLUSH, &orig_termios);
|
||||||
return 0;
|
fputs("\033[?25h" // Show cursor
|
||||||
|
"\033[?1049;" // Back to normal screen
|
||||||
|
"1000;" // Disable mouse x/y reporting
|
||||||
|
"1002;" // Disable mouse dragging
|
||||||
|
"1006" // Disable mouse SGR mode
|
||||||
|
"l", tty_out);
|
||||||
|
fclose(tty_out);
|
||||||
|
close(ttyin_fd);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user