ライフゲームについては既知とします。必要ならWikipediaの記事を見てください。
結論
一世代計算するワンライナーです。
life(w) = let sz = size(w); let v1 = fill(0, sz[1]-2), v2 = fill(0, sz[2]); [v2'; v1 [w[i-1:i+1, j-1:j+1] .* [1 1 1;1 9 1;1 1 1] |> sum for i in 2:sz[1]-1, j in 2:sz[2]-1] .|> (a -> ((a==3 || a==11 || a==12) && return 1; return 0)) v1; v2'] end end
REPLにコピペすれば動きます。0
が死んでいるセル、1
が生きているセルを示します。
実行例
解説
1.let sz = size(w); ... end
入力配列の大きさは何回も使うのでlet
ブロックで事前定義
2.let v1 = fill(0, sz[1]-2), v2 = fill(0, sz[2]); ... end
行列をパディングする(後述)時に使う配列
3.w[i-1:i+1, j-1:j+1] .* [1 1 1;1 9 1;1 1 1]
入力行列から$3 \times 3$の大きさの行列を切り出し、行列
\begin{matrix}
1&1&1\\
1&9&1\\
1&1&1
\end{matrix}
との要素ごとの積を取る。
4.|> sum
3.で求めた行列の要素を足し上げる。つまり畳み込み和(convolution sum)を計算している。
5.for i in 2:sz[1]-1, j in 2:sz[2]-1
3.、4.の計算を、入力行列の外周を除く要素で行う。
6..|> (a -> ((a==3 || a==11 || a==12) && return 1; return 0))
5.の結果できた行列の各要素に対し、次のような関数を適用する。
3 -> 1
11 -> 1
12 -> 1
_ -> 0
Juliaにswitch/case
式やパターンマッチの構文は無いが、短絡評価する演算子&&
で似たようなことができる。
行列[1 1 1;1 9 1;1 1 1]
と、この関数でライフゲームのルールを表現していることになる。
7.[v2'; v1 ... v1; v2']
これが無いと、入力の大きさ(m,n)
に対し、出力が(m-2, n-2)
になってしまう。ので、周辺を0
でパディングする。(もっとうまい方法がある気がするが...)
画像・動画にしたい?
例えば、Plots.heatmap()
で画像にして、そのままPlots
で動画にするのがお手軽で良いんじゃ無いんでしょうか。