I ran your code but it appeared to be very slow, I suspect from all the \pgfmathtruncatemacro
. But here we can do all calculations with \numexpr
easily. This code is based on the TeX
primitives \ifnum
, \ifcase
and \csname..\endcsname
.
I have used \foreach
loops in the first two code samples as I wanted to stay close to your original framework. In the third code sample I use \xintFor
from package xinttools. As \xintFor
does not create groups, it is easier to use in such contexts.
Update: amazed by JLDiaz's animation of the Gosper Gun, I have done it too with TeX
"rules" in a LaTeX
picture.
Update: based on Mark Wibrow's remark in a related question I have added a version of the initial code which only updates changed cells.
Final update: the third code sample (which produces the Gosper Gun below) has also been changed to only update cells when they actually do change. No temporary array of the entire universe.
![game of life II](https://i.stack.imgur.com/eEj5F.gif)
![game of life I](https://i.stack.imgur.com/VfIYi.gif)
\documentclass{article}
\usepackage{tikz}
%%\usetikzlibrary {calc,positioning}
\usepackage{color}
\pagestyle{empty}
\begin{document}\thispagestyle{empty}
% I. FIRST INITIALIZING THE ARRAY (not in the tikz sense)
\def\LifeSeed {{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,1,0,0,0,0},%
{0,0,0,0,0,1,0,0,0},%
{0,0,0,1,1,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}}
% The indices will run from 1 to 9 --- storage is compatible with higher range
\foreach[count=\xi] \x in \LifeSeed {%
\foreach[count=\yi] \y in \x {%
\expandafter\xdef\csname GofL\xi@\yi\endcsname {\y}}}
% example \GofL3@5 expands to fifth value of third row
% (but we use \csname as we can't use directly digits in control words)
% II. This is a poor man's display command. Replace by appropriate TikZ code.
\newcommand\DISPLAY {% to be replaced by actual TikZ code!
\foreach \x in {1,...,9} {\indent
\foreach \y in {1,...,9} {%
\ifcase\csname GofL\x@\y\endcsname\space
0 \or\textcolor{red}{1} \fi}\endgraf}%
\medskip }%
% III. Compute the next generation.
% Recall than in an \ifnum or an \ifcase, each explicit number
% must be ended by a space. We use \space to end a macro expanding
% to an explicit number in such contexts.
\newcommand\PlusOne [1]{\the\numexpr\ifnum #1=9 1\else #1+1\fi\relax }
\newcommand\MinusOne [1]{\the\numexpr\ifnum #1=1 9\else #1-1\fi\relax }
\newcommand\ONETICK {%
\foreach \x in {1,...,9} {%
\edef\xplus {\PlusOne \x}% better to have it here,
\edef\xminus {\MinusOne\x}% not in the inner loop
\foreach \y in {1,...,9} {%
\edef\yplus {\PlusOne \y}%
\edef\yminus {\MinusOne\y}%
\edef\Tmp % we allow ourself \edef, as after first expansion,
% not many tokens (in fact just one here 0,1,.., or 8
{\the\numexpr \csname GofL\xplus@\yminus\endcsname
+\csname GofL\xplus@\y\endcsname
+\csname GofL\xplus@\yplus\endcsname
+\csname GofL\x@\yplus\endcsname
+\csname GofL\xminus@\yplus\endcsname
+\csname GofL\xminus@\y\endcsname
+\csname GofL\xminus@\yminus\endcsname
+\csname GofL\x@\yminus\endcsname }%
\expandafter\xdef\csname GofLnext\x@\y\endcsname
{\ifcase\csname GofL\x@\y\endcsname\space % remember the \space thing?
\ifnum\Tmp=3 1\else 0\fi
\or
\ifcase\Tmp\space 0\or 0\or 1\or 1\else 0\fi
\fi }%
}% end of \y loop
}% end of \x loop
\foreach \x in {1,...,9} {%
\foreach \y in {1,...,9} {%
\global % must use global here.
\expandafter\let\csname GofL\x@\y\expandafter\endcsname
\csname GofLnext\x@\y\endcsname
}% end of \y loop
}% end of \x loop
}
\DISPLAY
\ONETICK
\DISPLAY
\ONETICK
\DISPLAY
\end{document}
Improved version which only modifies the modified (sic) cells:
\documentclass{article}
\usepackage{tikz}
%%\usetikzlibrary {calc,positioning}
% convert -verbose -delay 25 -dispose previous -loop 0 -density 200 gameoflifeIII-crop.pdf gameoflifeIII.gif
\pagestyle{empty}
\begin{document}\thispagestyle{empty}
% I. FIRST INITIALIZING THE ARRAY (not in the tikz sense)
\def\LifeSeed {{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,1,0,0,0,0},%
{0,0,0,0,0,1,0,0,0},%
{0,0,0,1,1,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}}
% The indices will run from 1 to 9 --- storage is compatible with higher range
\foreach[count=\xi] \x in \LifeSeed {%
\foreach[count=\yi] \y in \x {%
\expandafter\xdef\csname GofL\xi@\yi\endcsname {\y}}}
% example \GofL35 expands to 5fifth value of 3rd row
% (but we use \csname as we can't use directly digits in control words)
% II. This is a poor man's display command. Replace by appropriate TikZ code.
\newcommand\DISPLAY {% to be replaced by actual TikZ code!
\foreach \x in {1,...,9} {\indent
\foreach \y in {1,...,9} {%
\ifcase\csname GofL\x@\y\endcsname\space
0 \or\textcolor{red}{1} \fi}\endgraf}%
\clearpage }%
% III. Compute the next generation.
% Recall than in an \ifnum or an \ifcase, each explicit number
% must be ended by a space. We use \space to end a macro expanding
% to an explicit number in such contexts.
% To speed up the universe update, we keep a list of only the changed cells.
\newcommand\UPDATECHANGED [1]{% when called, \x and \y are defined
\edef\tmp{\noexpand\UPDATEONE{\x@\y}#1}%
% must use \global because \foreach groups
\global
\toks0 \expandafter\expandafter\expandafter{\expandafter\tmp\the\toks0}%
}%
\newcommand\UPDATEONE [2]
{\expandafter\def\csname GofL#1\expandafter\endcsname {#2}}%
\newcommand\PlusOne [1]{\the\numexpr\ifnum #1=9 1\else #1+1\fi\relax }
\newcommand\MinusOne [1]{\the\numexpr\ifnum #1=1 9\else #1-1\fi\relax }
\newcommand\ONETICK {\toks0 {}%
\foreach \x in {1,...,9} {%
\edef\xplus {\PlusOne \x}%
\edef\xminus {\MinusOne\x}%
\foreach \y in {1,...,9} {%
\edef\yplus {\PlusOne \y}%
\edef\yminus {\MinusOne\y}%
\edef\Tmp % we allow ourself \edef, as after first expansion,
% not many tokens (in fact just one here 0,1,.., or 8
{\the\numexpr \csname GofL\xplus@\yminus\endcsname
+\csname GofL\xplus@\y\endcsname
+\csname GofL\xplus@\yplus\endcsname
+\csname GofL\x@\yplus\endcsname
+\csname GofL\xminus@\yplus\endcsname
+\csname GofL\xminus@\y\endcsname
+\csname GofL\xminus@\yminus\endcsname
+\csname GofL\x@\yminus\endcsname }%
\ifcase\csname GofL\x@\y\endcsname\space % remember the \space thing?
\ifnum\Tmp=3 \UPDATECHANGED{1}\fi
\or % playing with \if's (space after the second 0 would be significant)
\if0\if\Tmp21\fi\if\Tmp31\fi0\UPDATECHANGED{0}\fi
\fi
}% end of \y loop
}% end of \x loop
% now update the cells
\the\toks0 % space after 0 is important, do not remove
}
\DISPLAY
\ONETICK
\DISPLAY
\ONETICK
\DISPLAY
\count 255 0
\loop
\ONETICK
\DISPLAY
\ifnum \count 255 < 32
\advance\count 255 1
\repeat
\end{document}
Here is the code used for the Gosper Gun. Uses \xintFor
rather than \foreach
. So there is no problem with groups now. Also updated to only modify the modified cells (sic).
\documentclass{article}
% for big universes you will need to adjust the page geometry
% (default size in \DISPLAY macro is 10bp times 10bp per cell)
\usepackage [paperheight=10cm]{geometry}
% workflow is either pdflatex+pdfcrop, and then convert for animated gif
% or
% simply latex+xdvi, hitting continuously the space bar, or the b to go back,
% with an xdvi window in front (the page height has been reduced to fit on a
% small screen) does the animation
\usepackage{xinttools} % for \xintFor loops
\pagestyle{empty}
\begin{document}\thispagestyle{empty}
% I. FIRST INITIALIZING THE ARRAY (not in the tikz sense)
% for compactness here the input format has is row1,row2, ... with no separator in
% each row
% GOSPER GLIDER RUN
% en.wikipedia.org/wiki/Conway's_Game_of_Life
% we pick up a later starting point for smoother cycling in animation
\def\LifeSeed{% percent optional here
000000000000000000000000000100000000,
000000000000000000000000001010000000,
000000000110000000000000001101000000,
000000000101000000000000001101100011,
000011000000100000000000001101000011,
110100100100100000000000001010000000,
110011000000100000000100000100000000,
000000000101000000010100000000000000,
000000000110000000001100000000000000,
000000000000000000000000000000000000,
000000000000000000000000000000000000,
000000000000000000000000000000000000,
000000000000000000000000000000000000,
000000000000000000000000000010000000,
000000000000000000000000000001000000,
000000000000000000000000000111000000,
000000000000000000000000000000000000,
000000000000000000000000000000000000,
000000000000000000000000000000000000% percent optional, but NO comma here.
}
% side note I recommend trying it out on a *periodic* universe with one extra column
% of zero on the left and one on the right (so 38 columns) and 35 rows,
% try it for 1000 generations...
\newcount\Xcount
\newcount\Ycount
% The cells are represented by macros \GofLx.y where x is horizontal coordinate
% and y is vertical coordinate (from the top down), and a dot is used as separator.
% must use \csname for that
\Ycount 0
% comma separated so we use \xintFor for the outer loop
\xintFor #1 in \LifeSeed \do
{%
\advance\Ycount by 1 % \Ycount is a ROW index
\Xcount 0 % \Xcount is a COLUMN index
% no separator, hence \xintFor* for the inner loop
\xintFor* #2 in {#1} \do
{%
\advance\Xcount by 1
\expandafter\def\csname GofL\the\Xcount.\the\Ycount\endcsname {#2}%
}% end of #2 loop
}% end of #1 loop
% \Xcount and \Ycount hold respectively the horizontal H and vertical V
% dimensions.
% indices run from 1 to H and from 1 to V
% the column index is like X coordinate (from left to right)
% the row index is like Y coordinate (from top to bottom)
% NOW CODE FOR SIMULATION WITH A BORDER OF PERMANENTLY DEAD CELLS.
% ONE DOES NOT NEED THAT FOR A PERIODIC UNIVERSE.
\xintFor #2 in \xintintegers \do
{% when \xintFor is used in this form #2 is a \numexpr...\relax
% Hence needs to be prefixed by \the
\expandafter\def\csname GofL0.\the#2\endcsname {0}%
\expandafter\def\csname GofL\the\numexpr\Xcount+1.\the#2\endcsname {0}%
\ifnum#2=\Ycount\expandafter\xintBreakFor\fi
}
% row 0 and row V+1
% column indices from 1 to \Xcount
\xintFor #1 in \xintintegers \do
{%
\expandafter\def\csname GofL\the#1.0\endcsname {0}%
\expandafter\def\csname GofL\the#1.\the\numexpr\Ycount+1\endcsname {0}%
\ifnum#1=\Xcount\expandafter\xintBreakFor\fi
}
% Let's not forget the corners
\expandafter\def\csname GofL0.0\endcsname {0}
\expandafter\def\csname GofL\the\numexpr\Xcount+1.0\endcsname {0}
\expandafter\def\csname GofL0.\the\numexpr\Ycount+1\endcsname {0}
\expandafter\def\csname GofL\the\numexpr\Xcount+1.\the\numexpr\Ycount+1\endcsname {0}
%% END OF CODE FOR PERMANENTLY DEAD EXTRA BORDER CELLS
% DISPLAYING WITH RULES
\setlength{\unitlength}{10bp}
\setlength{\fboxsep}{0pt}
\newcommand\DISPLAY {%
% \xintintegers by default starts at 1 and steps by 1
% inside macros # must be doubled
% ##1 and ##2 will each be a \numexpr. Must be prefixed by \the
% to produce explicit numbers.
\fbox{\begin{picture}(\Xcount,\Ycount)(1,-\Ycount)
% This means the width is \Xcount and the height is \Ycount
% and the bottom left corner has coordinates x=1, y=-ymax
\xintFor ##1 in \xintintegers \do
{% first index is "X" index
\xintFor ##2 in \xintintegers \do
{% second index is "Y" index
\ifcase\csname GofL\the##1.\the##2\endcsname\space
\or \put(##1,-##2){\rule{\unitlength}{\unitlength}}
\fi
\ifnum ##2=\Ycount\expandafter\xintBreakFor\fi
}%
\ifnum ##1=\Xcount\expandafter\xintBreakFor\fi
}%
\end{picture}}%
\clearpage
}%
% III. Compute the next generation.
% Recall than in an \ifnum or an \ifcase, each explicit number
% must be ended by a space. We use \space to end a macro expanding
% to an explicit number in such contexts.
% FOR PERIODIC UNIVERSE, use this:
% \newcommand\XPlusOne [1]{\the\numexpr\ifnum #1=\Xcount 1\else #1+1\fi\relax }
% \newcommand\XMinusOne [1]{\the\numexpr\ifnum #1=1 \Xcount\else #1-1\fi\relax }
% \newcommand\YPlusOne [1]{\the\numexpr\ifnum #1=\Ycount 1\else #1+1\fi\relax }
% \newcommand\YMinusOne [1]{\the\numexpr\ifnum #1=1 \Ycount\else #1-1\fi\relax }
% FOR UNIVERSE WITH DEATH BORDER, use this:
\newcommand\XPlusOne [1]{\the\numexpr #1+1\relax }
\newcommand\XMinusOne [1]{\the\numexpr #1-1\relax }
\newcommand\YPlusOne [1]{\the\numexpr #1+1\relax }
\newcommand\YMinusOne [1]{\the\numexpr #1-1\relax }
% MACRO WHICH WILL BE USED TO UPDATE ONLY THE CHANGED CELLS:
\newcommand\UPDATECHANGED [1]{% when called, \x and \y are defined
\edef\tmp {\noexpand\UPDATEONE{\x.\y}#1}%
% no need for \global, \xintFor does not create groups
\toks0 \expandafter\expandafter\expandafter{\expandafter\tmp\the\toks0}%
}%
\newcommand\UPDATEONE [2]
{\expandafter\def\csname GofL#1\expandafter\endcsname {#2}}%
\newcommand\ONETICK {%
% \xintintegers by default starts at 1 and steps by 1
% # must be double inside macros
% ##1 and ##2 will each be a \numexpr, hence the need for \the
%
\toks0 {}% will be used as storage for the cells in need of updating
%
\xintFor ##1 in \xintintegers \do
{% first index is "X" index
\edef\x {\the##1}%
\edef\xplus {\XPlusOne {\x}}%
\edef\xminus {\XMinusOne {\x}}%
\xintFor ##2 in \xintintegers \do
{% second index is "Y" index
\edef\y {\the##2}%
\edef\yplus {\YPlusOne {\y}}%
\edef\yminus {\YMinusOne {\y}}%
\edef\GofLTmp
{\the\numexpr \csname GofL\xplus.\yminus\endcsname
+\csname GofL\xplus.\y\endcsname
+\csname GofL\xplus.\yplus\endcsname
+\csname GofL\x.\yplus\endcsname
+\csname GofL\xminus.\yplus\endcsname
+\csname GofL\xminus.\y\endcsname
+\csname GofL\xminus.\yminus\endcsname
+\csname GofL\x.\yminus\endcsname }%
\ifcase\csname GofL\x.\y\endcsname\space % remember the \space thing?
\ifnum\GofLTmp=3 \UPDATECHANGED{1}\fi
\or % playing with \if's (not \ifnum, spaces after digits do NOT disappear!)
\if0\if\GofLTmp21\else\if\GofLTmp31\fi\fi0\UPDATECHANGED{0}\fi
\fi
\ifnum ##2=\Ycount \expandafter\xintBreakFor\fi
}% end of ##2 loop
\ifnum ##1=\Xcount \expandafter\xintBreakFor\fi
}% end of ##1 loop
% now we set the universe to its computed state
% only the changed cells are updated.
\the\toks0 % space after 0 is important, do not remove
}
% display initial universe:
\DISPLAY
\newcount\tickcount
\tickcount 1
\loop
\ONETICK
\DISPLAY
\advance\tickcount 1
\ifnum \tickcount< 15
\repeat
% WE STOP AT 15 FOR SPECIAL MEASURES IN GENERATING THE ANIMATED GOSPER GLIDER
\makeatletter
% isn't it self-defeating that LaTeX's \@namedef has a @ in its name?
\@namedef {GofL32.18}{0}%
\@namedef {GofL33.18}{0}%
\@namedef {GofL32.19}{0}%
\@namedef {GofL33.19}{0}%
\makeatother
\loop
\ONETICK
\DISPLAY
\advance\tickcount 1
\ifnum \tickcount< 30
\repeat
\end{document}
Best Answer
You could insert
\displaystyle
into the column definition, such asHere I used syntax provided by the
array
package:>{...}
can insert commands before the array element*n{...}
repeats a column definition n timesSo in the example we get 3 columns, where all cells have
\displaystyle
, and 1 paragraph column without.