From 947ff4e7658442cbb48215422c108742a7cdceab Mon Sep 17 00:00:00 2001 From: Emil Williams Date: Wed, 24 Jul 2024 20:05:18 +0000 Subject: [PATCH] On the third day... --- .gitignore | 1 + life.c | 334 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 335 insertions(+) create mode 100644 .gitignore create mode 100644 life.c 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 +#include +#include +#include +#include +#include + +#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; +}