1
0
mirror of https://codeberg.org/emilwilliams/life synced 2024-11-21 19:44:21 -05:00
life/life.c

267 lines
5.5 KiB
C

/* @BAKE \
cc -O2 -Wshadow -Wall -Wextra -Wpedantic \
$@ -o $* -lm -DNDEBUG $+ @STOP */
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
/* 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;
}