Created
June 14, 2021 01:57
-
-
Save madgarden/5de44ff4987bf8e8fceced07b68c9c0b to your computer and use it in GitHub Desktop.
Cellular automata with configurable rules
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <ctype.h> | |
#include <memory.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <time.h> | |
#define CELLS_W 40 | |
#define CELLS_H 20 | |
#define RULES 9 | |
enum | |
{ | |
BORDER_DEAD = 0, | |
BORDER_LIVE, | |
BORDER_WRAP | |
}; | |
static char cells[CELLS_W][CELLS_H]; | |
static char cells_buff[CELLS_W][CELLS_H]; | |
static int rules_survive[RULES]; | |
static int rules_birth[RULES]; | |
static int border_mode = BORDER_WRAP; | |
static int fill_percent = 50; | |
static void process_cell(int x, int y) | |
{ | |
int dx, dy; | |
int around = 0; | |
int alive = 0; | |
int i; | |
for(dy = -1; dy < 2; dy++) | |
{ | |
for(dx = -1; dx < 2; dx++) | |
{ | |
int cellx = dx + x; | |
int celly = dy + y; | |
if(!dx && !dy) continue; | |
if(cellx < 0 || cellx >= CELLS_W) | |
{ | |
if(border_mode == BORDER_WRAP) | |
{ | |
cellx = (cellx + CELLS_W) % CELLS_W; | |
} | |
else if(border_mode == BORDER_LIVE) | |
{ | |
around++; | |
continue; | |
} | |
else | |
{ | |
continue; | |
} | |
} | |
if(celly < 0 || celly >= CELLS_H) | |
{ | |
if(border_mode == BORDER_WRAP) | |
{ | |
celly = (celly + CELLS_H) % CELLS_H; | |
} | |
else if(border_mode == BORDER_LIVE) | |
{ | |
around++; | |
continue; | |
} | |
else | |
{ | |
continue; | |
} | |
} | |
if(cells[cellx][celly]) | |
{ | |
around++; | |
} | |
} | |
} | |
// Living cells | |
if(cells[x][y]) | |
{ | |
// Survive | |
for(i = 0; i < RULES; i++) | |
{ | |
if(around == rules_survive[i]) | |
{ | |
alive = 1; | |
break; | |
} | |
} | |
} | |
// Dead cells | |
else | |
{ | |
// Birth | |
for(i = 0; i < RULES; i++) | |
{ | |
if(around == rules_birth[i]) | |
{ | |
alive = 1; | |
break; | |
} | |
} | |
} | |
cells_buff[x][y] = alive; | |
} | |
static void step_cells(void) | |
{ | |
int x, y; | |
memset(cells_buff, 0, sizeof(cells_buff)); | |
for(y = 0; y < CELLS_H; y++) | |
{ | |
for(x = 0; x < CELLS_W; x++) | |
{ | |
process_cell(x, y); | |
} | |
} | |
memcpy(cells, cells_buff, sizeof(cells)); | |
} | |
static void reset_random(void) | |
{ | |
int x, y; | |
for(y = 0; y < CELLS_H; y++) | |
{ | |
for(x = 0; x < CELLS_W; x++) | |
{ | |
cells[x][y] = ((rand() >> 8) % 100) < fill_percent; | |
} | |
} | |
} | |
static int parse_rules(int argc, char *argv[]) | |
{ | |
int i; | |
int *rules; | |
int ok = 0; | |
for(i = 1; i < argc; i++) | |
{ | |
char *ptr = argv[i]; | |
int j; | |
int ch = tolower(*ptr); | |
if(ch == 'l') | |
{ | |
border_mode = BORDER_LIVE; | |
continue; | |
} | |
if(ch == 'b') | |
{ | |
rules = rules_birth; | |
} | |
else if(ch == 's') | |
{ | |
rules = rules_survive; | |
} | |
else | |
{ | |
continue; | |
} | |
ptr++; | |
j = 0; | |
ok = 1; | |
memset(rules, -1, sizeof(rules_birth)); | |
while(*ptr && j < RULES) | |
{ | |
rules[j] = *ptr - '0'; | |
j++; | |
ptr++; | |
} | |
} | |
return ok; | |
} | |
static void print_rules(int steps) | |
{ | |
int i; | |
printf("RULES: "); | |
printf("B"); | |
for(i = 0; i < RULES; i++) | |
{ | |
if(rules_birth[i] < 0 || rules_birth[i] > 8) break; | |
printf("%d", rules_birth[i]); | |
} | |
printf("/S"); | |
for(i = 0; i < RULES; i++) | |
{ | |
if(rules_survive[i] < 0 || rules_survive[i] > 8) break; | |
printf("%d", rules_survive[i]); | |
} | |
if(border_mode == BORDER_LIVE) | |
{ | |
printf(", live border"); | |
} | |
else if(border_mode == BORDER_WRAP) | |
{ | |
printf(", wrap border"); | |
} | |
else | |
{ | |
printf(", dead border"); | |
} | |
printf("\nstep %d", steps); | |
if(!steps) | |
{ | |
printf(" (random fill %%%d)", fill_percent); | |
} | |
printf("\n"); | |
} | |
int main(int argc, char *argv[]) | |
{ | |
int ch = 0, in; | |
int x, y; | |
int steps = 0; | |
int ok = 0; | |
memset(rules_survive, -1, sizeof(rules_survive)); | |
memset(rules_birth, -1, sizeof(rules_birth)); | |
if(argc > 1) | |
{ | |
ok = parse_rules(argc, argv); | |
} | |
if(!ok) | |
{ | |
// Conway default | |
rules_survive[0] = 2; | |
rules_survive[1] = 3; | |
rules_birth[0] = 3; | |
} | |
srand(time(NULL)); | |
memset(cells, 0, sizeof(cells)); | |
reset_random(); | |
while(ch != 'q') | |
{ | |
int chars = 0; | |
int *rule = NULL; | |
int changed = 0; | |
print_rules(steps); | |
for(y = 0; y < CELLS_H; y++) | |
{ | |
printf(" "); | |
for(x = 0; x < CELLS_W; x++) | |
{ | |
int icon = '.'; | |
if(cells[x][y]) icon = 'O'; | |
putchar(icon); | |
} | |
printf("\n"); | |
} | |
printf("\n(r)eset, (b/s) rule, (q)uit: "); | |
ch = 0; | |
while((in = getchar()) != '\n') | |
{ | |
ch = tolower(in); | |
if(ch == 'b') | |
{ | |
rule = rules_birth; | |
chars = 0; | |
} | |
else if(ch == 's') | |
{ | |
rule = rules_survive; | |
chars = 0; | |
} | |
else if(ch == 'l') | |
{ | |
border_mode = BORDER_LIVE; | |
changed = 1; | |
} | |
else if(ch == 'd') | |
{ | |
border_mode = BORDER_DEAD; | |
changed = 1; | |
} | |
else if(ch == 'w') | |
{ | |
border_mode = BORDER_WRAP; | |
changed = 1; | |
} | |
else if(ch == 'f') | |
{ | |
unsigned int amount = 0; | |
while((in = getchar())) | |
{ | |
int digit = in - '0'; | |
if(digit < 0 || digit > 9) break; | |
amount *= 10; | |
amount += digit; | |
} | |
ungetc(in, stdin); | |
if(amount > 100) amount = 100; | |
fill_percent = amount; | |
changed = 1; | |
} | |
if(rule && chars <= RULES) | |
{ | |
if(chars == 0) | |
{ | |
memset(rule, -1, sizeof(rules_birth)); | |
} | |
else | |
{ | |
rule[chars - 1] = ch - '0'; | |
} | |
changed = 1; | |
} | |
chars++; | |
} | |
printf("\n"); | |
if(!changed) | |
{ | |
step_cells(); | |
steps++; | |
} | |
if(ch == 'r') | |
{ | |
reset_random(); | |
steps = 0; | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment