How to write a function from scratch to simulate a cellular automaton
Rule 254 is just a BitOr
(or BitNor if you flip the colours and padding to 1) performed on a 3-element sliding window:
state0 = {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0};
update[state_] := MovingMap[BitOr @@ # &, state, {3, Center}, 0]
ArrayPlot[
NestList[update, state0, 5]
]
For general 3-valued boolean functions you can do this:
rule = 254;
state0 = {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0};
update[state_] := MovingMap[Boole[BooleanFunction[rule, 3]@@#] &,state,{3,Center},0]
ArrayPlot[
NestList[update, state0, 5]
]
Here is a very simple implementation. It is not the fastest implementation but it probably works in Mathematica versions dating back to the 90s*.
rules[wcode_] := Thread@Rule[
ReverseSortBy[Tuples[{1, 0}, 3], FromDigits[#, 2] &],
PadLeft[IntegerDigits[wcode, 2], 8]
]
state = CenterArray[13];
step[wcode_][state_] := ArrayPad[Partition[state, 3, 1] /. rules[wcode], 1]
NestList[step[254], state, 5] // ArrayPlot
NestList[step[30], state, 5] // ArrayPlot
If you want to simulate more steps, make sure to increase the number 13 in CenterArray
so that it corresponds to the width of the picture that you would like generate.
* CenterArray
and ArrayPad
are comparatively modern conveniences, but Partition
existed in Mathematica 1.0 in 1988, and FromDigits
and IntegerDigits
existed in Mathematica 3.0 in 1996. In fact, Richard Gaylord's 1996 book Modeling Nature: Cellular Automata Simulations with Mathematica uses Partition
to implement a function for simulating cellular automata. (The technique can be easily extended to 2D as well.)
Notes on Wolfram code
The implementation of Wolfram codes to evolution rules conversion can be explained as follows.
Each cellular automata consists of eight rules:
RulePlot[CellularAutomaton[254]]
In this picture, black corresponds to 1 and white to 0. Consequently, these cells can be read out as {1, 1, 1, 1, 1, 1, 1, 0}
. Converting this to a decimal yields the rule number:
FromDigits[{1, 1, 1, 1, 1, 1, 1, 0}, 2]
254
Note that the cells have to be sorted in a particular way to get the right binary sequence.
The top row in each cell can be interpreted as a binary number. {1, 1, 1}
, {1, 1, 0}
, {1, 0, 1}
etc. Each cell has a corresponding decimal number:
FromDigits[#, 2] & /@ {{1, 1, 1}, {1, 1, 0}, {1, 0, 1}}
{7, 6, 5}
The cells are sorted by their decimal numbers in descending order.
To convert from a Wolfram code to rules, we simply need to take these steps in reverse. We generate all possible cells, sort them by their decimal representation, and assign to each cell an output given by the binary representation of the rule number.
Some rules, such as rule 30, do not have a binary representation with eight digits.
IntegerDigits[30, 2]
{1, 1, 1, 1, 0}
i.e. there is not one number for each cell. In these cases we pad the binary representation with zeros on the left.