mirror of
https://codeberg.org/emilwilliams/life
synced 2024-11-25 20:56:47 -05:00
255 lines
5.2 KiB
C
255 lines
5.2 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>
|
|
|
|
#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;
|
|
}
|
|
|
|
/* 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] [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;
|
|
}
|