/* @BAKE \ cc -O2 -Wshadow -Wall -Wextra -Wpedantic \ $@ -o $* -lm -DNDEBUG $+ @STOP */ #include #include #include #include #include #include /* defaults */ #define DEFAULT_GRID_SIZE "20" #define DEFAULT_WAIT 4 #define DEFAULT_MAX_STEP -1 /* symbols used */ char symbol [] = " O."; /* ... */ /* maps to symbol */ enum { LIFE_NONE, LIFE_ALIVE, LIFE_DEAD }; #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 /* 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; } /* 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 */ 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; } } } } 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=%s] [WAIT BETWEEN EACH STEP=%d] [MAX STEP=-%d]\n", argv [0], DEFAULT_GRID_SIZE, DEFAULT_WAIT, DEFAULT_MAX_STEP); return 1; } char * rect = argc >= 2 ? argv [1] : DEFAULT_GRID_SIZE; board_t * b = board_alloc_text (rect); board_randomize (b, 5); board_eq_filter (b, 1); int wait = argc >= 3 ? atoi (argv [2]) : DEFAULT_WAIT; int step = argc >= 4 ? atoi (argv [3]) : DEFAULT_MAX_STEP; #ifndef NDEBUG life_debug_near (b, 0, 0); #endif life (b, step, wait); board_free (b); return 0; }