1 of 5

Haskell, 135 (+4?) bytes

Completely different approach to FrownyFrog's answer but about the same (depending on how strict the rules are) byte count:

r x=0:(zipWith3(?)x=<<tail$tail x++[0])
f y=map(" o"!!)<$>take 40(iterate r[sum[1|elem i y]|i<-[0..40]])

Uses \$1\$-indexing and has a leading space on each line, try it online!

If that is not allowed, we can adjust the generated range and squeeze in a tail to get rid of the leading space for +4 bytes: Try it online!


We're going to work with length-\$40\$ lists with values \$\texttt{0}\$, \$\texttt{1}\$, so let's start with the correct array:

f y=                               [sum[1|elem i y]|i<-[0..40]]

Next we're going to iterate the rule \$40\$ times:

                  take 40(iterate r                            )

And finally map each \$\texttt{0}\$ and \$\texttt{1}\$ to some fancy character:

    map(" o"!!)<$>

The function r which applies the \$\texttt{110}\$-rule is pretty simple: Using zipWith3 and some padding we can outsource the actual decision for the next cell to (?):

r x=0:(zipWith3(?)x=<<tail$tail x++[0])

The (?) operator is the most interesting part of the solution: Using a Karnaugh map I found that when we're treating \$\texttt{a}\$, \$\texttt{b}\$ and \$\texttt{c}\$ as Boolean values the following holds

$$ \texttt{next(a,b,c)} = \bar{\texttt{a}}\cdot\texttt{c} + \bar{\texttt{b}}\cdot\texttt{c} + \bar{\texttt{c}}\cdot\texttt{b} $$

For Integers we can use (0^), (*) and (+) for negation, and and or respectively. The leading (0^0^..) is needed because otherwise (0?0)1 would be 2 which will propagate and give index too large errors later on: