我的目标是编程康威生命游戏在 LaTeX 中,并将输出转换为动画 PDF。我打算为此使用 PGF/TikZ,尤其是pgfmath
,但目前我陷入困境,因为我需要为数组元素分配值,我不知道如何做到这一点,如果可能的话。
这是我的第一种方法,它输出单个生成:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,positioning}
\begin{document}
\pagestyle{empty}
\begin{tikzpicture}
\def\grid{{{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}}}
\def\temp{{{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,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,0},%
{0,0,0,0,0,0,0,0,0},%
{0,0,0,0,0,0,0,0,0}}}
\foreach \z in \grid {
\foreach[count=\xi] \x in \z {
\foreach[count=\yi] \y in \x {
\pgfmathtruncatemacro{\i}{\xi - 1}
\pgfmathtruncatemacro{\iminus}{mod(mod(\xi - 2, 9) + 9, 9)}
\pgfmathtruncatemacro{\iplus}{mod(\xi, 9)}
\pgfmathtruncatemacro{\j}{\yi - 1}
\pgfmathtruncatemacro{\jminus}{mod(mod(\yi - 2, 9) + 9, 9)}
\pgfmathtruncatemacro{\jplus}{mod(\yi, 9)}
\pgfmathtruncatemacro{\value}{\grid[\i][\j]}
\pgfmathtruncatemacro{\topleft}{\grid[\iminus][\jminus]}
\pgfmathtruncatemacro{\top}{\grid[\iminus][\j]}
\pgfmathtruncatemacro{\topright}{\grid[\iminus][\jplus]}
\pgfmathtruncatemacro{\left}{\grid[\i][\jminus]}
\pgfmathtruncatemacro{\right}{\grid[\i][\jplus]}
\pgfmathtruncatemacro{\bottomleft}{\grid[\iplus][\jminus]}
\pgfmathtruncatemacro{\bottom}{\grid[\iplus][\j]}
\pgfmathtruncatemacro{\bottomright}{\grid[\iplus][\jplus]}
\pgfmathtruncatemacro{\neighbourcount}{\topleft + \top + \topright + \left + \right + \bottomleft + \bottom + \bottomright}
\pgfmathtruncatemacro{\nextvalue}{(\value == 1 && (\neighbourcount == 2 || \neighbourcount == 3)) || (\value == 0 && \neighbourcount == 3) ? 1 : 0}
\node at (\yi, -\xi) {\value};
%\node at (\yi, -\xi) {\value, \neighbourcount};
\node at ($ (0, -10) + (\yi, -\xi) $) {\nextvalue};
}
}
}
\end{tikzpicture}
\end{document}
我只需要有关如何为数组元素分配值的帮助,因为我想自己解决其他问题。
编辑#1
问题的原标题是为数组元素赋值(PGF/TikZ),但我认为,根据以下答案,这个标题不再适合这个问题。
答案1
我运行了你的代码,但它似乎非常慢,我怀疑是所有的\pgfmathtruncatemacro
。但在这里我们可以\numexpr
轻松地用 进行所有计算。此代码基于TeX
原语\ifnum
、\ifcase
和\csname..\endcsname
。
我在前两个代码示例中使用了\foreach
循环,因为我想接近原始框架。在第三个代码示例中,我使用了\xintFor
来自包新工具。由于\xintFor
不创建组,因此在这样的上下文中使用起来更容易。
更新:惊讶杰利迪亚兹的动画片的高斯珀枪TeX
,我也按照图中的“规则”做过LaTeX
。
更新:基于马克·维布罗的评论相关问题我添加了初始代码的版本,该版本仅更新已更改的单元格。
最后更新:第三个代码示例(生成下面的 Gosper Gun)也已更改为仅在单元格实际发生变化时才更新单元格。没有整个宇宙的临时数组。
\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}
改进版本仅修改了已修改的单元格:
\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}
这是 Gosper Gun 使用的代码。使用\xintFor
而不是\foreach
。所以现在组没有问题了。还更新为仅修改已修改的单元格(原文如此)。
\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}
答案2
只是为了好玩(但也许对任何人都有用),这是我的 Lua 解决方案:
主 TeX 文件
\documentclass{article}
\usepackage{pgffor}
\usepackage{xcolor}
\usepackage{courier} % Courier has bold series, while cm doesnt
\usepackage[active,tightpage]{preview}\PreviewEnvironment{tabular}
% Load lua program, and define macros for accessing its functions
\directlua{dofile("life.lua")}
\newcommand{\UniverseInit}[1]{\directlua{universe=text_to_matrix("#1")}}
\newcommand{\Evolve}{\directlua{Evolve(universe)}}
\newcommand{\TabularUniverse}[2]{\ttfamily\directlua{tabular_dump(universe,[[\noexpand#1]],[[\noexpand#2]])}}
% The arguments of TabularUniverse are the tex macros to be used to represent
% a live cell (#1), and an empty one (#2)
%
% For convenience, we define two macros to store these
\def\on{\textbf{1}}
\def\off{\color{black!20}0}
\begin{document}
% Initialize the universe
\UniverseInit{
000000000
000000000
000000000
000010000
000001000
000111000
000000000
000000000
000000000
}
% Show it
\TabularUniverse{\on}{\off}
% Let it evolve for 10 generations
\foreach \i in {1,...,10} {
\Evolve
\TabularUniverse{\on}{\off}
}
\end{document}
life.lua 文件(已更新)
我在 rosettacode 中使用的代码中发现了一个错误。Evolve 函数假设宇宙是正方形的(行数和列数相同),但这并不是必需的。新版本的代码没有做出这个假设。
-- From http://rosettacode.org/wiki/Conway's_Game_of_Life#Lua
--
function Evolve( cell )
local m = #cell
local n = #cell[1]
local cell2 = {}
for i = 1, m do
cell2[i] = {}
for j = 1, n do
cell2[i][j] = cell[i][j]
end
end
for i = 1, m do
for j = 1, n do
local count
if cell2[i][j] == 0 then count = 0 else count = -1 end
for x = -1, 1 do
for y = -1, 1 do
if i+x >= 1 and i+x <= m and j+y >= 1 and j+y <= n and cell2[i+x][j+y] == 1 then count = count + 1 end
end
end
if count < 2 or count > 3 then cell[i][j] = 0 end
if count == 3 then cell[i][j] = 1 end
end
end
return cell
end
-- From http://tex.stackexchange.com/a/123754/12571
function justWords(str)
local t = {}
local function helper(word) table.insert(t, word) return "" end
if not str:gsub("%w+", helper):find"%S" then return t end
end
function text_to_matrix(txt)
local m = {}
local l = justWords(txt)
for i=1,#l do
if (l[i]~= nil and #l[i]>1) then
j = 1; row = {}
for c in l[i]:gmatch(".") do
row[j] = tonumber(c)
j = j + 1
end
m[i] = row
end
end
return m
end
-- Coded for this answer:
function matrix_to_text(m, on, off, col_sep, row_sep)
local str_tab = {}
for j = 1, #m do
row = m[j]
str_row = {}
for i = 1, #row do
if (row[i]==0) then str_row[i] = off
else str_row[i] = on
end
end
str_tab[j] = table.concat(str_row,col_sep)
end
return table.concat(str_tab,row_sep)
end
function verbatim_dump(m, on, off)
tex.sprint("\\begin{verbatim}")
tex.sprint(matrix_to_text(m,on,off,"","\r"))
tex.sprint("\\end{verbatim}")
end
function tabular_dump(m, on, off)
spec = {}
for i = 1, #m[1] do
spec[i]="c"
end
header = string.format("\\begin{tabular}{%s}", table.concat(spec,"@{\\ }"))
tex.sprint(header)
tex.sprint(matrix_to_text(m,on,off,"&", "\\\\"))
tex.sprint("\\end{tabular}")
end
结果
(你也可以看看生成的 pdf)
更多示例
将初始宇宙更改为:
% Initialize the universe
\UniverseInit{
000000000000000000000000100000000000
000000000000000000000010100000000000
000000000000110000001100000000000000
000000000001000100001100000000000011
000000000010000010001100000000000011
110000000010001011000010100000000000
110000000010000010000000100000000000
000000000001000100000000000000000000
000000000000110000000000000000000000
000000000000000000000000000000000000
000000000000000000000000000000000000
000000000000000000000000000000000000
000000000000000000000000000000000000
000000000000000000000000000000000000
000000000000000000000000000000000000
000000000000000000000000000000000000
000000000000000000000000000000000000
000000000000000000000000000000000000
000000000000000000000000000000000000
}
计算 60 代(在我的 2008 年 iMac 上花费了 7 秒),我们可以看到令人惊叹的“Gosper Gun”的实际行动(注意,906K gif):
答案3
在得到这两个答案后,我也想发布我的解决方案。看到jfbu 的回答我有点害怕,所以我选择了 luatex 方式。
该代码可能效率不高,但它可以生成动画 PDF(遗憾的是此功能仅适用于 Adobe Reader)或具有不同演化阶段的页面。此外,该代码仅适用于 n×n 矩阵。
\documentclass{article}
\usepackage[a0paper]{geometry}
\usepackage{luacode}
\usepackage{animate}
\usepackage{tikz}
\usepackage{xcolor}
\usepackage[active, tightpage]{preview}
\PreviewEnvironment{animateinline}
%\PreviewEnvironment{tikzpicture}
\tikzset{%
cellframe/.style={%
minimum size=5mm,%
draw,%
fill=white,%
fill opacity=0%
}%
}
\tikzset{%
alivecell/.style={%
circle,%
inner sep=0pt,%
minimum size=4mm,%
fill=black%
}%
}
\setlength{\PreviewBorder}{5mm}
\begin{document}
\begin{luacode*}
iterations = 36
grid = {{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}}
\end{luacode*}
\begin{luacode*}
function evolve(grid)
local temp = {}
local gridsize = #grid
for i = 1, gridsize do
temp[i] = {}
for j = 1, gridsize do
temp[i][j] = 0
end
end
for i = 1, gridsize do
for j = 1, gridsize do
iminus = i - 1
iplus = i + 1
jminus = j - 1
jplus = j + 1
if iminus == 0 then
iminus = gridsize
end
if iplus == gridsize + 1 then
iplus = 1
end
if jminus == 0 then
jminus = gridsize
end
if jplus == gridsize + 1 then
jplus = 1
end
neighbourcount = grid[iminus][jminus] +
grid[iminus][j] +
grid[iminus][jplus] +
grid[i][jminus] +
grid[i][jplus] +
grid[iplus][jminus] +
grid[iplus][j] +
grid[iplus][jplus]
if (grid[i][j] == 1 and (neighbourcount == 2 or neighbourcount == 3)) or (grid[i][j] == 0 and neighbourcount == 3) then
temp[i][j] = 1
else
temp[i][j] = 0
end
end
end
return temp
end
function display(grid)
local gridsize = #grid
for i = 1, gridsize do
for j = 1, gridsize do
tex.sprint([[\node[cellframe] at (]])
tex.sprint((i - 1) * 5)
tex.sprint([[mm,]])
tex.sprint(-((j - 1) * 5))
tex.sprint([[mm){0};]])
if grid[j][i] == 1 then
tex.sprint([[\node[alivecell] at (]])
tex.sprint((i - 1) * 5)
tex.sprint([[mm,]])
tex.sprint(-((j - 1) * 5))
tex.sprint([[mm){1};]])
end
end
end
end
function animate(grid, iterations)
for i = 1, iterations - 1 do
display(grid)
tex.sprint([[\newframe]])
grid = evolve(grid)
end
display(grid)
end
function frames(grid, iterations)
for i = 1, iterations - 1 do
tex.sprint([[\begin{tikzpicture}]])
display(grid)
grid = evolve(grid)
tex.sprint([[\end{tikzpicture}]])
tex.sprint([[\clearpage]])
end
tex.sprint([[\begin{tikzpicture}]])
display(grid)
tex.sprint([[\end{tikzpicture}]])
end
\end{luacode*}
\noindent\begin{animateinline}[autoplay,loop,
begin={\begin{tikzpicture}[scale=1]},
end={\end{tikzpicture}}]{5}
\luadirect{animate(grid, iterations)}
\end{animateinline}
%\noindent\luadirect{frames(grid, iterations)}
\end{document}
上述代码生成了以下滑翔机的动画:
如果您想将每个框架生成为一个新页面,那么您只需稍微修改一下代码:
注释第 9 行:
\PreviewEnvironment{animateinline}
→%\PreviewEnvironment{animateinline}
取消注释第 10 行:
%\PreviewEnvironment{tikzpicture}
→\PreviewEnvironment{tikzpicture}
将第 153–157 行注释成如下形式:
%\noindent\begin{animateinline}[autoplay,loop, %begin={\begin{tikzpicture}[scale=1]}, %end={\end{tikzpicture}}]{5} % \luadirect{animate(grid, iterations)} %\end{animateinline}
取消注释第 158 行,如下所示:
\noindent\luadirect{frames(grid, iterations)}
您可以通过将另一个数组分配给 lua 变量(第 41 行)来指定初始种子grid
,并通过分配 lua 变量(第 39 行)来设置迭代次数(帧或页面的数量)iterations
。
\begin{luacode*}
iterations = 300
grid = {{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,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,0,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,0,0,0,0,0,1,0,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,1,1,0,0,0,0,0,0,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,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,1,0,0,0,0,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,1,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,0,0,0,0,0,0,0,1,0,0,0,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,0,0,0,0,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,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},
{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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,0,0,0}}
\end{luacode*}