aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--life.c334
2 files changed, 335 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4b10a05
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+life
diff --git a/life.c b/life.c
new file mode 100644
index 0000000..8bc9ea0
--- /dev/null
+++ b/life.c
@@ -0,0 +1,334 @@
+/* @BAKE \
+ cc -O2 -Wshadow -Wall -Wextra -Wpedantic -g -fsanitize=address,undefined,bounds \
+ $@ -o $* -lm -DNDEBUG $+ @STOP */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define DELC static inline
+
+/* nearness mask, life mask */
+#define LIFE_NEAR_MASK ((1 << 4) - 1)
+#define LIFE_MASK (32)
+/* life nearness function */
+#define life_near life_near_lim
+
+enum {
+ LIFE_NONE,
+ LIFE_ALIVE,
+ LIFE_DEAD
+};
+
+char symbol [] = " O.";
+
+/* tty */
+
+#define CSI "\033["
+
+DELC void clear (void) { printf (CSI "3J"); }
+
+DELC void move_up (int n) { printf (CSI "%dF", n); }
+
+DELC void move_down (int n) { printf (CSI "%dE", n); }
+
+DELC void move_back (int n) { printf (CSI "%dD", n); }
+
+DELC void move_forward (int n) { printf (CSI "%dC", n); }
+
+DELC void color (int fg, int bg) { printf (bg != -1 ? CSI "%d;%dm" : CSI "%dm", fg, bg); }
+
+/* board */
+
+typedef struct {
+ int w, h;
+ int ** g;
+} board_t;
+
+DELC board_t * board_alloc (int w, int h) {
+ board_t * b = malloc (sizeof (board_t));
+ b->w = w;
+ b->h = h;
+ b->g = malloc (sizeof (int *) * h);
+ for (int i = 0; i < h; ++i) {
+ b->g [i] = malloc (sizeof (int) * w);
+ }
+ return b;
+}
+
+DELC void board_free (board_t * b) {
+ for (int i = 0; i < b->h; ++i) {
+ free (b->g [i]);
+ }
+ free (b->g);
+ free (b);
+}
+
+DELC void board_set (board_t * b, int v) {
+ for (int i = 0; i < b->h; ++i) {
+ for (int f = 0; f < b->w; ++f) {
+ b->g [i] [f] = v;
+ }
+ }
+}
+
+DELC void board_randomize (board_t * b, int v) {
+ for (int i = 0; i < b->h; ++i) {
+ for (int f = 0; f < b->w; ++f) {
+ b->g [i] [f] = rand () % v;
+ }
+ }
+}
+
+DELC void board_eq_filter (board_t * b, int v) {
+ for (int i = 0; i < b->h; ++i) {
+ for (int f = 0; f < b->w; ++f) {
+ b->g [i] [f] = b->g [i] [f] == v;
+ }
+ }
+}
+
+DELC void board_copy_to (board_t * dest, board_t * src, int x, int y) {
+ for (int i = 0; i < src->h; ++i) {
+ for (int f = 0; f < src->w; ++f) {
+ dest->g [y + i] [x + f] = src->g [i] [f];
+ }
+ }
+}
+
+DELC void board_copy (board_t * dest, board_t * src) {
+ board_copy_to (dest, src, 0, 0);
+}
+
+/* TODO: dup without copy */
+DELC board_t * board_dup (board_t * b) {
+ board_t * a = board_alloc (b->w, b->h);
+ board_copy (a, b);
+ return a;
+}
+
+#if 0
+DELC int board_cmp (board_t * a, board_t * b) {
+ int s = 0;
+ for (int i = 0; i < b->h; ++i) {
+ for (int f = 0; f < b->w; ++f) {
+ s += a->g [i] [f] != b->g [i] [f];
+ }
+ }
+ return s;
+}
+
+DELC void board_invert (board_t * b) {
+ for (int i = 0; i < b->h; ++i) {
+ for (int f = 0; f < b->w; ++f) {
+ b->g [i] [f] = !(b->g [i] [f] == 1);
+ }
+ }
+}
+#endif
+
+/* Must place the cursor at the top left of the already printed board */
+DELC void board_highlight (board_t * b, int fg, int x, int y) {
+ if (y) { move_down (y); }
+ if (x) { move_forward (x*2); }
+
+ color (fg, -1);
+ printf ("%c", symbol [b->g [y] [x]]);
+ color (0, -1);
+
+ if (x) { move_back (x*2); }
+ if (y) { move_up (y); }
+}
+
+DELC void board_print (board_t * b) {
+ for (int i = 0; i < b->h; ++i) {
+ for (int f = 0; f < b->w; ++f) {
+ printf (f + 1 != b->w ? "%c " : "%c\n", symbol [(b->g [i] [f])]);
+ }
+ }
+}
+
+DELC board_t * board_alloc_text (char * s) {
+ int r = atoi (s);
+ return board_alloc (r, r);
+}
+
+/* life */
+
+#if 0
+DELC int life_near_loop (board_t * b, int x, int y) {
+ int ** g = b->g, w = b->w, h = b->h;
+
+ int
+ ty = (y-1) % h, lx = (x-1) % w,
+ cy = ( y) % h, cx = ( x) % w,
+ by = (y+1) % h, rx = (x+1) % w;
+
+ return
+ g [ty] [lx] + g [ty] [cx] + g [ty] [rx]
+ + g [cy] [lx] + (g [cy] [cx] << 5) + g [cy] [rx]
+ + g [by] [lx] + g [by] [cx] + g [by] [rx];
+}
+
+DELC int life_near_lim (board_t * b, int x, int y) {
+ int ** g = b->g, w = b->w, h = b->h;
+
+ int
+ ty = (y-1), lx = (x-1),
+ cy = ( y), cx = ( x),
+ by = (y+1), rx = (x+1),
+ s = g [cy] [cx] << 5;
+
+ if (ty < h) {
+ s += lx < w ? g [ty] [lx] : 0;
+ s += g [ty] [cx] ;
+ s += rx >= 0 ? g [ty] [rx] : 0;
+ }
+
+ s += lx < w ? g [cy] [lx] : 0;
+ s += rx >= 0 ? g [cy] [rx] : 0;
+
+ if (by >= 0) {
+ s += lx < w ? g [by] [lx] : 0;
+ s += g [by] [cx] ;
+ s += rx >= 0 ? g [by] [rx] : 0;
+ }
+
+ return s;
+}
+#endif
+
+DELC int life_near_lim (board_t * b, int x, int y) {
+ int ** g = b->g, w = b->w, h = b->h, s = 0, i, f, yi, xf;
+ for (i = -1; i < 2; ++i) {
+ for (f = -1; f < 2; ++f) {
+ if (i == 0 && f == 0)
+ { s += g [y] [x] * LIFE_MASK; continue; }
+ yi = y + i;
+ xf = x + f;
+ if (yi >= 0 && yi < h
+ && xf >= 0 && xf < w)
+ { s += g [yi] [xf] == 1; }
+ }
+ }
+ return s;
+}
+
+DELC int life_pop (board_t * b) {
+ int s = 0;
+ for (int i = 0; i < b->h; ++i) {
+ for (int f = 0; f < b->w; ++f) {
+ s += b->g [i] [f] == LIFE_ALIVE;
+ }
+ }
+ return s;
+}
+
+DELC void life_update (board_t * a, board_t * b) {
+ int near;
+ for (int i = 0; i < b->h; ++i) {
+ for (int f = 0; f < b->w; ++f) {
+ near = life_near (b, f, i);
+ if ((near & LIFE_NEAR_MASK) == 3) { a->g [i] [f] = LIFE_ALIVE; }
+ else if ((near & LIFE_MASK) && (near & LIFE_NEAR_MASK) < 2) { a->g [i] [f] = LIFE_DEAD; }
+ else if ((near & LIFE_MASK) && (near & LIFE_NEAR_MASK) > 3) { a->g [i] [f] = LIFE_DEAD; }
+ }
+ }
+}
+
+#if 0
+DELC void life_update (board_t * a, board_t * b) {
+ int near, me, ** g = a->g;
+ for (int i = 0; i < b->h; ++i) {
+ for (int f = 0; f < b->w; ++f) {
+ near = life_near (b, f, i);
+ me = near & LIFE_ALIVE;
+ near &= LIFE_NEAR_MASK;
+ g [i] [f] =
+ (near == 3)
+ + ( (me && (near < 2))
+ || (me && (near > 3))) * LIFE_DEAD;
+ }
+ }
+}
+#endif
+
+DELC void life (board_t * b, int step, int wait) {
+ printf (
+ "rect %d:%d\n"
+ "step %d\n"
+ "wait sec / %d\n\n",
+ b->w, b->h,
+ step,
+ wait);
+
+ wait = 1e6 / wait;
+
+ board_t * a = board_dup (b);
+
+ int gen = 1;
+
+ while (step) {
+ board_print (b);
+
+ printf ("\npop: %d\ngen: %d\n", life_pop (b), gen);
+
+ move_up (b->h + 3);
+
+ life_update (a, b);
+ board_copy (b, a);
+
+ usleep (wait);
+ --step;
+ ++gen;
+ };
+ board_free (a);
+
+ move_down (b->h + 3);
+
+ printf ("\nEnd of this life.\n");
+}
+
+#ifndef NDEBUG
+DELC void life_debug_near (board_t * b, int w, int h) {
+ board_print (b);
+ move_up (b->h);
+ board_highlight (b, 31, w, h);
+ move_down (b->h);
+ int near = life_near (b, w, h);
+ printf ("%s near %d,%d: %d\n", near & LIFE_MASK ? "alive" : "dead", w, h, near & LIFE_NEAR_MASK);
+}
+#endif
+
+int main (int argc, char ** argv) {
+ srand (time (NULL));
+
+ if ( argc >= 2
+ && (strcmp (argv [1], "-h") == 0
+ || strcmp (argv [1], "--help") == 0)) {
+ printf ("%s [GRID SIZE] [WAIT BETWEEN EACH STEP=4] [MAX STEP=-1]\n", argv [0]);
+ return 1;
+ }
+
+ char * rect = argc >= 2 ? argv [1] : "20";
+ board_t * b = board_alloc_text (rect);
+
+ board_randomize (b, 5);
+ board_eq_filter (b, 1);
+
+ int wait = argc >= 3 ? atoi (argv [2]) : 4;
+ int step = argc >= 4 ? atoi (argv [3]) : -1;
+
+#ifndef NDEBUG
+ life_debug_near (b, 0, 0);
+#endif
+
+ life (b, step, wait);
+
+ board_free (b);
+
+ return 0;
+}