Change the rules of Life

15

Life-like cellular automaton are cellular automaton that are similar to Conway's Game of Life, in that they operate on a (theoretically) infinitely large square grid, where each cell has exactly 8 neighbours, and is one of 2 states, namely alive and dead.

However, these Like-like versions are different in a crucial way: the rules for a given cell to come alive and the rules for a given cell to survive to the next generation.

For example, classic Game of Life uses the rule B3/S23, meaning that it takes 3 alive cells to birth a new one, and either 2 or 3 living neighbours to survive. For this challenge, we will assume that neighbours do not include itself, so each cell has exactly 8 neighbours.

Your task is, given a starting configuration, a birth rule, a survival rule and a positive integer (the number of generations to be run), simulate the Life-like automaton using those rules for the number of generations given in the shortest code possible. The starting configuration will be a square matrix/2-dimensional array or a multiline string, you may choose. The others may be given in any reasonable format and method.

For example, if the birth rule was 12345678 (any living neighbours), the survival rule was 2357 and the starting configuration was

0 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0


the next two generations would be

Generation 1:           Generation 2:

0 0 0 0 0               1 1 1 1 1
0 1 1 1 0               1 1 0 1 1
0 1 0 1 0               1 0 1 0 1
0 1 1 1 0               1 1 0 1 1
0 0 0 0 0               1 1 1 1 1


If the number of generations given was 10, the output would be something along the lines of

0 1 1 1 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
0 1 1 1 0


You do not have to handle changes that happen outside of the bounds given by the input matrix, however, all cells outside the matrix begin dead. Therefore, the input matrix may be any size, up to the maximum value your language can support. You do not have to output the board between generations.

This is a so the shortest code wins.

Test cases

These use the B/S notation to indicate the rules used

B2/S2, generations = 100, configuration:

1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0


Output:

0 0 0 0 0 0 0 0
0 1 0 0 0 0 1 0
1 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0


B1357/S2468, generations = 12, configuration:

1 0 1 0 1 0
0 1 1 0 1 0
1 0 0 0 0 0
0 0 0 0 0 1
1 1 1 1 1 0
0 1 1 0 0 1


Output:

0 1 0 0 0 0
0 1 1 1 1 0
0 1 0 1 1 0
1 1 1 0 0 0
0 0 1 1 1 0
0 1 1 0 0 0


If you need to generate more test cases, you can use this wonderful simulator. Please make sure to limit the board size

Is the simulation toroidal? – Erik the Outgolfer – 2017-10-20T15:35:52.827

@EriktheOutgolfer no, as the matrix is (theoretically) infinite in size – caird coinheringaahing – 2017-10-20T15:36:48.917

Also, can we assume the given matrix is square? – Erik the Outgolfer – 2017-10-20T15:46:27.510

2@EriktheOutgolfer "infinitely large square grid" – caird coinheringaahing – 2017-10-20T15:47:04.930

But it doesn't say you can assume that...will edit in. – Erik the Outgolfer – 2017-10-20T15:47:38.047

How will the input be given? Can we assume the starting configuration is given in the form of the 2D array/multiline string/etc? And can the birth and survival rules be given as lists/arrays of integers, or must we take them as a string as shown? – Arnold Palmer – 2017-10-20T16:26:35.147

@ArnoldPalmer you may take input in standard methods, "The starting configuration will be a square matrix/2-dimensional array or a multiline string, you may choose." and you can take them in any reasonable format – caird coinheringaahing – 2017-10-20T16:27:53.137

9

MATL, 24 23 bytes

xx:"tt3Y6Z+1Gm<8M2Gmb*+


Inputs are:

• Array with birth rule
• Array with survival rule
• Number of generations
• Matrix with initial cell configuration, using ; as row separator.

Try it online! Or see test cases: 1, 2.

For a few bytes more you can see the evolution in ASCII art.

Explanation

xx      % Take two inputs implicitly: birth and survival rules. Delete them
% (but they get copied into clipboard G)
:"      % Take third input implicitly: number of generations. Loop that many times
tt    %   Duplicate twice. This implicitly takes the initial cell configuration
%   as input the first time. In subsequent iterations it uses the cell
%   configuration from the previous iteration
3Y6   %   Push Moore neighbourhood: [1 1 1; 1 0 1; 1 1 1]
Z+    %   2D convolution, maintaining size
1G    %   Push first input from clipboard G: birth rule
m     %   Ismember: gives true for cells that fulfill the birth rule
<     %   Less than (element-wise): a cell is born if it fulfills the birth rule
%   *and* was dead
8M    %   Push result of convolution again, from clipboard M
2G    %   Push second input from clipboard G: survival rule
m     %   Ismember: gives true for cells that fulfill the survival rule
b     %   Bubble up the starting cell configuration
*     %   Multiply (element-wise): a cell survives if it fulfills the survival
%   rule *and* was alive
+     %   Add: a cell is alive if it has been born or has survived, and those
%   are exclusive cases. This produces the new cell configuration
% Implicit end loop. Implicit display


Can you save bytes by changing the order of the inputs? The xx at the start seems a bit wasteful to me... – Erik the Outgolfer – 2017-10-20T16:56:19.223

@EriktheOutgolfer I don't see how. I need to delete the first two to later reuse them several times (one per iteration), and the other inputs are already implicit now – Luis Mendo – 2017-10-20T16:57:11.013

Oh so "deleting" the inputs adds them to some sort of input list? – Erik the Outgolfer – 2017-10-20T16:58:16.293

@EriktheOutgolfer Yes. MATL input is interactive, meaning the program doesn't know in advance how many input there are. Here, deleting from an empty stack causes an input to be implicitly taken. Once taken, each input gets copied into clipboard G, and they can be retrieved later. – Luis Mendo – 2017-10-20T16:59:42.700

3

Wolfram Language (Mathematica), 144 122 bytes

CellularAutomaton[{Tr[2^#&/@Flatten@MapIndexed[2#+2-#2[[1]]&,{#2,#3},{2}]],{2,{{2,2,2},{2,1,2},{2,2,2}}},{1,1}},#,{{#4}}]&


Try it online!

Example usage:

%[RandomInteger[1, {10, 10}], {2, 3}, {3}, 5]


uses a 10x10 random grid as a start, survives with either 2 or 3 neighbors, births with 3 neighbors, plot result at 5 iterations.

Too bad the builtin is only one dimensional (correct me if I'm wrong) – Zacharý – 2017-10-20T22:32:19.253

I’m using the built-in “CellularAutomaton” with a 9-neighbor totalistic rule. Much of the code converts the survival/birth inputs into a rule number. – Kelly Lowder – 2017-10-21T23:35:50.540

1

R, 256 bytes

function(x,B,S,r){y=cbind(0,rbind(0,x,0),0)
n=dim(y)[1]
z=c(1,n)
f=function(h){w=-1:1
b=h%%n+1
a=(h-b+1)/n+1
'if'(a%in%z|b%in%z,0,sum(x[w+b,w+a])-x[b,a])}
while(r){x=y
for(i in 1:n^2){u=f(i-1)
y[i]=u%in%B
y[i]=(y[i]&!x[i])|(x[i]&(u%in%S))}
r=r-1}
y[-z,-z]}


Try it online!

Sadly, this does not look as golfed as I'd hoped.

Input: an R matrix, and the challenge parameters. Output: the matrix after R generations.

The algorithm pads the matrix with zeros to handle the boundaries. Then, iteratively: 1st) it applies the Birth rule and 2nd) it kills the pre-existing cells that did not pass the Survival rule. Padding is removed when returning.

nice byte count! – Giuseppe – 2018-04-12T18:47:44.270

I managed to get it to 217 bytes but if we can find exactly one more golf, we can get it to 216 which is at least a cube...

– Giuseppe – 2018-04-12T19:00:12.147

1

Python 2, 156149 146 bytes

lambda R,g,c:g and f(R,g-1,[[sum(sum(l[y+y/~y:y+2])for l in c[x+x/~x:x+2])-c[x][y]in R[c[x][y]]for y,_ in e(c)]for x,_ in e(c)])or c
e=enumerate


Try it online!

Takes input:

• Rules: [birth,survial] rules as list of string. eg.(['135','246'])
• generations: int
• configuration: Square 2D array of 1/0 or True/False

Returns 2d array of True/False