\myheading{Reversing back the state of ``Game of Life''} \leveldown{} How could we reverse back a known state of GoL? This can be solved by brute-force, but this is extremely slow and inefficient. Let's try to use SAT solver. First, we need to define a function which will tell, if the new cell will be created/born, preserved/stay or died. Quick refresher: cell is born if it has 3 neighbours, it stays alive if it has 2 or 3 neighbours, it dies in any other case. This is how I can define a function reflecting state of a new cell in the next state: \begin{lstlisting} if center==true: return popcnt2(neighbours) || popcnt3(neighbours) if center==false return popcnt3(neighbours) \end{lstlisting} We can get rid of ``if'' construction: \begin{lstlisting} result=(center==true && (popcnt2(neighbours) || popcnt3(neighbours))) || (center==false && popcnt3(neighbours)) \end{lstlisting} \dots where ``center'' is state of a center cell, ``neighbours'' are 8 neighbouring cells, popcnt2 is a function which returns True if it has exactly 2 bits on input, popcnt3 is the same, but for 3 bits (just like these were used in my ``Minesweeper'' example (\ref{minesweeper_SAT})). Using Wolfram Mathematica, I first create all helper functions and truth table for the function, which returns \emph{true}, if a cell must be present in the next state, or \emph{false} if not: \begin{lstlisting} In[1]:= popcount[n_Integer]:=IntegerDigits[n,2] // Total In[2]:= popcount2[n_Integer]:=Equal[popcount[n],2] In[3]:= popcount3[n_Integer]:=Equal[popcount[n],3] In[4]:= newcell[center_Integer,neighbours_Integer]:=(center==1 && (popcount2[neighbours]|| popcount3[neighbours]))|| (center==0 && popcount3[neighbours]) In[13]:= NewCellIsTrue=Flatten[Table[Join[{center},PadLeft[IntegerDigits[neighbours,2],8]] -> Boole[newcell[center, neighbours]],{neighbours,0,255},{center,0,1}]] Out[13]= {{0,0,0,0,0,0,0,0,0}->0, {1,0,0,0,0,0,0,0,0}->0, {0,0,0,0,0,0,0,0,1}->0, {1,0,0,0,0,0,0,0,1}->0, {0,0,0,0,0,0,0,1,0}->0, {1,0,0,0,0,0,0,1,0}->0, {0,0,0,0,0,0,0,1,1}->0, {1,0,0,0,0,0,0,1,1}->1, ... \end{lstlisting} Now we can create a \ac{CNF}-expression out of truth table: \begin{lstlisting} In[14]:= BooleanConvert[BooleanFunction[NewCellIsTrue,{center,a,b,c,d,e,f,g,h}],"CNF"] Out[14]= (!a||!b||!c||!d)&&(!a||!b||!c||!e)&&(!a||!b||!c||!f)&&(!a||!b||!c||!g)&&(!a||!b||!c||!h)&& (!a||!b||!d||!e)&&(!a||!b||!d||!f)&&(!a||!b||!d||!g)&&(!a||!b||!d||!h)&&(!a||!b||!e||!f)&& (!a||!b||!e||!g)&&(!a||!b||!e||!h)&&(!a||!b||!f||!g)&&(!a||!b||!f||!h)&&(!a||!b||!g||!h)&& (!a||!c||!d||!e)&&(!a||!c||!d||!f)&&(!a||!c||!d||!g)&&(!a||!c||!d||!h)&&(!a||!c||!e||!f)&& (!a||!c||!e||!g)&&(!a||!c||!e||!h)&&(!a||!c||!f||!g)&&(!a||!c||!f||!h)&& ... \end{lstlisting} Also, we need a second function, \emph{inverted one}, which will return \emph{true} if the cell must be absent in the next state, or \emph{false} otherwise: \begin{lstlisting} In[15]:= NewCellIsFalse=Flatten[Table[Join[{center},PadLeft[IntegerDigits[neighbours,2],8]] -> Boole[Not[newcell[center, neighbours]]],{neighbours,0,255},{center,0,1}]] Out[15]= {{0,0,0,0,0,0,0,0,0}->1, {1,0,0,0,0,0,0,0,0}->1, {0,0,0,0,0,0,0,0,1}->1, {1,0,0,0,0,0,0,0,1}->1, {0,0,0,0,0,0,0,1,0}->1, ... In[16]:= BooleanConvert[BooleanFunction[NewCellIsFalse,{center,a,b,c,d,e,f,g,h}],"CNF"] Out[16]= (!a||!b||!c||d||e||f||g||h)&&(!a||!b||c||!d||e||f||g||h)&&(!a||!b||c||d||!e||f||g||h)&& (!a||!b||c||d||e||!f||g||h)&&(!a||!b||c||d||e||f||!g||h)&&(!a||!b||c||d||e||f||g||!h)&& (!a||!b||!center||d||e||f||g||h)&&(!a||b||!c||!d||e||f||g||h)&&(!a||b||!c||d||!e||f||g||h)&& (!a||b||!c||d||e||!f||g||h)&&(!a||b||!c||d||e||f||!g||h)&&(!a||b||!c||d||e||f||g||!h)&& (!a||b||c||!d||!e||f||g||h)&&(!a||b||c||!d||e||!f||g||h)&&(!a||b||c||!d||e||f||!g||h)&& ... \end{lstlisting} Using the very same way as in my ``Minesweeper'' example (\ref{minesweeper_SAT}), I can convert \ac{CNF} expression to list of clauses: \begin{lstlisting} def mathematica_to_CNF (s:str, d:Dict[str, str]) -> List[List[str]]: for k in d.keys(): s=s.replace(k, d[k]) s=s.replace("!", "-").replace("||", " ").replace("(", "").replace(")", "") lst=s.split ("&&") rt=[] for l in lst: rt.append(l.split(" ")) return rt \end{lstlisting} And again, as in ``Minesweeper'', there is an invisible border, to make processing simpler. \ac{SAT} variables are also numbered as in previous example: % FIXME: \begin{lstlisting} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 ... 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 \end{lstlisting} Also, there is a visible border, always fixed to \emph{False}, to make things simpler. Now the working source code. Whenever we encounter ``*'' in \TT{final\_state[]}, we add clauses generated by \TT{cell\_is\_true()} function, or \TT{cell\_is\_false()} if otherwise. When we get a solution, it is negated and added to the list of clauses, so when minisat is executed next time, it will skip solution which was already printed. \begin{lstlisting} ... def cell_is_false (center, a): s="(!a||!b||!c||d||e||f||g||h)&&(!a||!b||c||!d||e||f||g||h)&&(!a||!b||c||d||!e||f||g||h)&&" \ "(!a||!b||c||d||e||!f||g||h)&&(!a||!b||c||d||e||f||!g||h)&&(!a||!b||c||d||e||f||g||!h)&&" \ "(!a||!b||!center||d||e||f||g||h)&&(!a||b||!c||!d||e||f||g||h)&&(!a||b||!c||d||!e||f||g||h)&&" \ "(!a||b||!c||d||e||!f||g||h)&&(!a||b||!c||d||e||f||!g||h)&&(!a||b||!c||d||e||f||g||!h)&&" \ "(!a||b||c||!d||!e||f||g||h)&&(!a||b||c||!d||e||!f||g||h)&&(!a||b||c||!d||e||f||!g||h)&&" \ "(!a||b||c||!d||e||f||g||!h)&&(!a||b||c||d||!e||!f||g||h)&&(!a||b||c||d||!e||f||!g||h)&&" \ "(!a||b||c||d||!e||f||g||!h)&&(!a||b||c||d||e||!f||!g||h)&&(!a||b||c||d||e||!f||g||!h)&&" \ "(!a||b||c||d||e||f||!g||!h)&&(!a||!c||!center||d||e||f||g||h)&&(!a||c||!center||!d||e||f||g||h)&&" \ "(!a||c||!center||d||!e||f||g||h)&&(!a||c||!center||d||e||!f||g||h)&&(!a||c||!center||d||e||f||!g||h)&&" \ "(!a||c||!center||d||e||f||g||!h)&&(a||!b||!c||!d||e||f||g||h)&&(a||!b||!c||d||!e||f||g||h)&&" \ "(a||!b||!c||d||e||!f||g||h)&&(a||!b||!c||d||e||f||!g||h)&&(a||!b||!c||d||e||f||g||!h)&&" \ "(a||!b||c||!d||!e||f||g||h)&&(a||!b||c||!d||e||!f||g||h)&&(a||!b||c||!d||e||f||!g||h)&&" \ "(a||!b||c||!d||e||f||g||!h)&&(a||!b||c||d||!e||!f||g||h)&&(a||!b||c||d||!e||f||!g||h)&&" \ "(a||!b||c||d||!e||f||g||!h)&&(a||!b||c||d||e||!f||!g||h)&&(a||!b||c||d||e||!f||g||!h)&&" \ "(a||!b||c||d||e||f||!g||!h)&&(a||b||!c||!d||!e||f||g||h)&&(a||b||!c||!d||e||!f||g||h)&&" \ "(a||b||!c||!d||e||f||!g||h)&&(a||b||!c||!d||e||f||g||!h)&&(a||b||!c||d||!e||!f||g||h)&&" \ "(a||b||!c||d||!e||f||!g||h)&&(a||b||!c||d||!e||f||g||!h)&&(a||b||!c||d||e||!f||!g||h)&&" \ "(a||b||!c||d||e||!f||g||!h)&&(a||b||!c||d||e||f||!g||!h)&&(a||b||c||!d||!e||!f||g||h)&&" \ "(a||b||c||!d||!e||f||!g||h)&&(a||b||c||!d||!e||f||g||!h)&&(a||b||c||!d||e||!f||!g||h)&&" \ "(a||b||c||!d||e||!f||g||!h)&&(a||b||c||!d||e||f||!g||!h)&&(a||b||c||d||!e||!f||!g||h)&&" \ "(a||b||c||d||!e||!f||g||!h)&&(a||b||c||d||!e||f||!g||!h)&&(a||b||c||d||e||!f||!g||!h)&&" \ "(!b||!c||!center||d||e||f||g||h)&&(!b||c||!center||!d||e||f||g||h)&&(!b||c||!center||d||!e||f||g||h)&&" \ "(!b||c||!center||d||e||!f||g||h)&&(!b||c||!center||d||e||f||!g||h)&&(!b||c||!center||d||e||f||g||!h)&&" \ "(b||!c||!center||!d||e||f||g||h)&&(b||!c||!center||d||!e||f||g||h)&&(b||!c||!center||d||e||!f||g||h)&&" \ "(b||!c||!center||d||e||f||!g||h)&&(b||!c||!center||d||e||f||g||!h)&&(b||c||!center||!d||!e||f||g||h)&&" \ "(b||c||!center||!d||e||!f||g||h)&&(b||c||!center||!d||e||f||!g||h)&&(b||c||!center||!d||e||f||g||!h)&&" \ "(b||c||!center||d||!e||!f||g||h)&&(b||c||!center||d||!e||f||!g||h)&&(b||c||!center||d||!e||f||g||!h)&&" \ "(b||c||!center||d||e||!f||!g||h)&&(b||c||!center||d||e||!f||g||!h)&&(b||c||!center||d||e||f||!g||!h)" return mathematica_to_CNF(s, center, a) def cell_is_true (center, a): s="(!a||!b||!c||!d)&&(!a||!b||!c||!e)&&(!a||!b||!c||!f)&&(!a||!b||!c||!g)&&(!a||!b||!c||!h)&&" \ "(!a||!b||!d||!e)&&(!a||!b||!d||!f)&&(!a||!b||!d||!g)&&(!a||!b||!d||!h)&&(!a||!b||!e||!f)&&" \ "(!a||!b||!e||!g)&&(!a||!b||!e||!h)&&(!a||!b||!f||!g)&&(!a||!b||!f||!h)&&(!a||!b||!g||!h)&&" \ "(!a||!c||!d||!e)&&(!a||!c||!d||!f)&&(!a||!c||!d||!g)&&(!a||!c||!d||!h)&&(!a||!c||!e||!f)&&" \ "(!a||!c||!e||!g)&&(!a||!c||!e||!h)&&(!a||!c||!f||!g)&&(!a||!c||!f||!h)&&(!a||!c||!g||!h)&&" \ "(!a||!d||!e||!f)&&(!a||!d||!e||!g)&&(!a||!d||!e||!h)&&(!a||!d||!f||!g)&&(!a||!d||!f||!h)&&" \ "(!a||!d||!g||!h)&&(!a||!e||!f||!g)&&(!a||!e||!f||!h)&&(!a||!e||!g||!h)&&(!a||!f||!g||!h)&&" \ "(a||b||c||center||d||e||f)&&(a||b||c||center||d||e||g)&&(a||b||c||center||d||e||h)&&" \ "(a||b||c||center||d||f||g)&&(a||b||c||center||d||f||h)&&(a||b||c||center||d||g||h)&&" \ "(a||b||c||center||e||f||g)&&(a||b||c||center||e||f||h)&&(a||b||c||center||e||g||h)&&" \ "(a||b||c||center||f||g||h)&&(a||b||c||d||e||f||g)&&(a||b||c||d||e||f||h)&&(a||b||c||d||e||g||h)&&" \ "(a||b||c||d||f||g||h)&&(a||b||c||e||f||g||h)&&(a||b||center||d||e||f||g)&&(a||b||center||d||e||f||h)&&" \ "(a||b||center||d||e||g||h)&&(a||b||center||d||f||g||h)&&(a||b||center||e||f||g||h)&&" \ "(a||b||d||e||f||g||h)&&(a||c||center||d||e||f||g)&&(a||c||center||d||e||f||h)&&" \ "(a||c||center||d||e||g||h)&&(a||c||center||d||f||g||h)&&(a||c||center||e||f||g||h)&&" \ "(a||c||d||e||f||g||h)&&(a||center||d||e||f||g||h)&&(!b||!c||!d||!e)&&(!b||!c||!d||!f)&&" \ "(!b||!c||!d||!g)&&(!b||!c||!d||!h)&&(!b||!c||!e||!f)&&(!b||!c||!e||!g)&&(!b||!c||!e||!h)&&" \ "(!b||!c||!f||!g)&&(!b||!c||!f||!h)&&(!b||!c||!g||!h)&&(!b||!d||!e||!f)&&(!b||!d||!e||!g)&&" \ "(!b||!d||!e||!h)&&(!b||!d||!f||!g)&&(!b||!d||!f||!h)&&(!b||!d||!g||!h)&&(!b||!e||!f||!g)&&" \ "(!b||!e||!f||!h)&&(!b||!e||!g||!h)&&(!b||!f||!g||!h)&&(b||c||center||d||e||f||g)&&" \ "(b||c||center||d||e||f||h)&&(b||c||center||d||e||g||h)&&(b||c||center||d||f||g||h)&&" \ "(b||c||center||e||f||g||h)&&(b||c||d||e||f||g||h)&&(b||center||d||e||f||g||h)&&" \ "(!c||!d||!e||!f)&&(!c||!d||!e||!g)&&(!c||!d||!e||!h)&&(!c||!d||!f||!g)&&(!c||!d||!f||!h)&&" \ "(!c||!d||!g||!h)&&(!c||!e||!f||!g)&&(!c||!e||!f||!h)&&(!c||!e||!g||!h)&&(!c||!f||!g||!h)&&" \ "(c||center||d||e||f||g||h)&&(!d||!e||!f||!g)&&(!d||!e||!f||!h)&&(!d||!e||!g||!h)&&(!d||!f||!g||!h)&&" \ "(!e||!f||!g||!h)" return mathematica_to_CNF(s, center, a) ... \end{lstlisting} ( \url{\RepoURL/\CURPATH/GoL_SAT_utils.py} ) \lstinputlisting[style=custompy]{\CURPATH/reverse1.py} ( \url{\RepoURL/\CURPATH/reverse1.py} ) Here is the result: \begin{lstlisting} HEIGHT= 3 WIDTH= 3 .*. *.* .*. 1.rle written .** *.. *.* 2.rle written **. ..* *.* 3.rle written *.* *.. .** 4.rle written *.* ..* **. 5.rle written *.* .*. *.* 6.rle written unsat! \end{lstlisting} The first result is the same as initial state. Indeed: this is ``still life'', i.e., state which will never change, and it is correct solution. The last solution is also valid. Now the problem: 2nd, 3rd, 4th and 5th solutions are equivalent to each other, they just mirrored or rotated. In fact, this is reflectional\footnote{\url{https://en.wikipedia.org/wiki/Reflection_symmetry}} (like in mirror) and rotational\footnote{\url{https://en.wikipedia.org/wiki/Rotational_symmetry}} symmetries. We can solve this easily: we will take each solution, reflect and rotate it and add them negated to the list of clauses, so minisat will skip them during its work: \begin{lstlisting} ... while True: solution=try_again() if solution==None: break s.add_clause(SAT_lib.negate_clause(GoL_SAT_utils.grid_to_clause(solution, grid, VAR_FALSE, H, W))) s.add_clause(SAT_lib.negate_clause(GoL_SAT_utils.grid_to_clause(my_utils.reflect_vertically(solution), grid, VAR_FALSE, H, W))) s.add_clause(SAT_lib.negate_clause(GoL_SAT_utils.grid_to_clause(my_utils.reflect_horizontally(solution), grid, VAR_FALSE, H, W))) # is this square? if W==H: s.add_clause(SAT_lib.negate_clause(GoL_SAT_utils.grid_to_clause(my_utils.rotate_rect_array(solution,1), grid, VAR_FALSE, H, W))) s.add_clause(SAT_lib.negate_clause(GoL_SAT_utils.grid_to_clause(my_utils.rotate_rect_array(solution,2), grid, VAR_FALSE, H, W))) s.add_clause(SAT_lib.negate_clause(GoL_SAT_utils.grid_to_clause(my_utils.rotate_rect_array(solution,3), grid, VAR_FALSE, H, W))) ... \end{lstlisting} ( \url{\RepoURL/\CURPATH/reverse2.py} ) Functions \TT{reflect\_vertically()}, \TT{reflect\_horizontally} and \TT{rotate\_squarearray()} are simple array manipulation routines. Now we get just 3 solutions: \begin{lstlisting} HEIGHT= 3 WIDTH= 3 .*. *.* .*. 1.rle written .** *.. *.* 2.rle written *.* .*. *.* 3.rle written unsat! \end{lstlisting} This one has only one single ancestor: \begin{lstlisting} final_state=[ " * ", " * ", " * "] _PRE_END _PRE_BEGIN HEIGHT= 3 WIDTH= 3 ... *** ... 1.rle written unsat! \end{lstlisting} This is oscillator, of course. How many states can lead to such picture? \begin{lstlisting} final_state=[ " * ", " ", " ** ", " * ", " * ", " *** "] \end{lstlisting} 28, these are few: \begin{lstlisting} HEIGHT= 6 WIDTH= 5 .*.*. ..*.. .**.* ..*.. ..*.* .**.. 1.rle written .*.*. ..*.. .**.* ..*.. *.*.* .**.. 2.rle written ..*.* ..**. .**.. ....* *.*.* .**.. 3.rle written ..*.* ..**. .**.. *...* ..*.* .**.. 4.rle written ... \end{lstlisting} Now the biggest, ``space invader'': \begin{lstlisting} final_state=[ " ", " * * ", " * * ", " ******* ", " ** *** ** ", " *********** ", " * ******* * ", " * * * * ", " ** ** ", " "] \end{lstlisting} \begin{lstlisting} HEIGHT= 10 WIDTH= 13 ..*.*.**..... .....*****... ....**..*.... ......*...*.. ..**...*.*... .*..*.*.**..* *....*....*.* ..*.*..*..... ..*.....*.*.. ....**..*.*.. 1.rle written *.*.*.**..... .....*****... ....**..*.... ......*...*.. ..**...*.*... .*..*.*.**..* *....*....*.* ..*.*..*..... ..*.....*.*.. ....**..*.*.. 2.rle written ..*.*.**..... *....*****... ....**..*.... ......*...*.. ..**...*.*... .*..*.*.**..* *....*....*.* ..*.*..*..... ..*.....*.*.. ....**..*.*.. 3.rle written ... \end{lstlisting} I don't know how many possible states can lead to ``space invader'', perhaps, too many. Had to stop it. And it slows down during execution, because number of clauses is increasing (because of negating solutions addition). All solutions are also exported to RLE files, which can be opened by Golly\footnote{\url{http://golly.sourceforge.net/}}. \myheading{Garden of Eden} As they say, ``A Garden of Eden is a pattern that has no parents and thus can only occur in generation 0.''\footnote{\url{https://www.conwaylife.com/wiki/Garden_of_Eden}}. We can check if these are really \textit{gardens}: \lstinputlisting[style=custompy]{\CURPATH/eden.py} It's indeed so for the \textit{garden} I've copypasted. \levelup{}