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]
]

254

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

Output

NestList[step[30], state, 5] // ArrayPlot

Output

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]]

Output

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.