BobbyQuineのブログ(備忘録)

Linux関係の備忘録、その他タバコ、Vape関連なんかも。

シェル芸でライフゲームをやってみた

へべれけです.シェル芸でライフゲーム出来たら楽しいだろうなって思ってやってみた.

まぁ,最終的にシェル芸か怪しい物が出来上がった.

 

ライフゲーム - Wikipedia

 

ライフゲームが何ぞやってのはWikipediaでも読んでください.

まずはこの中の振動子「銀河」を作ったのでそれを.

 

とりま結果がこちら

 

seq 8|awk '{if ($1 == 1) print "ZWNobyBhNDhiMmExYjZhNmIyYTFiNmE2YjJhMTNiMmE1YjJhNmIyYTViMmE2YjJhNWIyYTEzYjJhNmI2YTFiMmE2YjZhMWIyYTQ4fHNlZCAtZSBzL2EvXFxcbjBcfGhlYWRcIFwtblwgL2cgLWUgcy9iL1xcXG4xXHxoZWFkXCBcLW5cIC9nfHNlZCAvXlwkL2R8c2VkIHMvXi95ZXNcIC9nfGJhc2h8dGVlIGEK" ; else print "Y2F0IGF8dHIgJ1xuJyAnICd8c2VkIHMvXi95ZXNcIC9nfGJhc2h8aGVhZCAtbiAxOTV8bmwgLW4gbG58YXdrIC1GIlx0IiB7J3ByaW50ICQyIiwiJDEnfXxhd2sgLUYgIiwiIHsncHJpbnQgImVjaG8gIiQxInxhd2sge3ByaW50ICQiJDIiKyQiJDIrMSIrJCIkMisyIiskIiQyKzE1IiskIiQyKzE3IiskIiQyKzMwIiskIiQyKzMxIiskIiQyKzMyIiwkIiQyKzE2In0iJ318c2VkIC1lIHMvXHsvXHtcJy9nIC1lIHMvXH0vXCdcfS9nfGJhc2h8YXdrICd7aWYgKCQxID09ICIyIikgcHJpbnQgJDI7IGVsc2UgaWYoJDEgPT0gIjMiKSBwcmludCAiMSI7IGVsc2UgcHJpbnQgIjAifSd8c2VkIC1lIDFzL14vMDAwMDAwMDAwMDAwMDAwMC8gLWUgJyQgYSAwMDAwMDAwMDAwMDAwMCd8c2VkIHMvLi9cJlxcXG4vZ3xzZWQgL15cJC9kfHRlZSBhCg=="}'|base64 -d|sed "$ a rm a"|bash|awk '{ORS=NR%15?"":"\n";print}'|sed s/0/\ \ /g|sed s/1/\[\]/g|textimg -asl15 -F100 -f /u*/s*/f*/t*/h*o/*A.ttf

 

解説

まず銀河は一周期8世代なので

seq 8

で1世代を与えて8世代まで演算させることとする.

パイプ先のawkで一行目に上記の"ZW〜EK"を与え,それ以外の行には"Y2〜=="を与える.これはそれぞれbase64エンコードされており,デコードすると前者は初期状態を,後者はそれから次世代を演算するシェル芸が得られる.それがそれぞれ次である.

 

echo a48b2a1b6a6b2a1b6a6b2a13b2a5b2a6b2a5b2a6b2a5b2a13b2a6b6a1b2a6b6a1b2a48|sed -e s/a/\\\n0\|head\ \-n\ /g -e s/b/\\\n1\|head\ \-n\ /g|sed /^\$/d|sed s/^/yes\ /g|bash|tee a

 

cat a|tr '\n' ' '|sed s/^/yes\ /g|bash|head -n 195|nl -n ln|awk -F"\t" {'print $2","$1'}|awk -F "," {'print "echo "$1"|awk {print $"$2"+$"$2+1"+$"$2+2"+$"$2+15"+$"$2+17"+$"$2+30"+$"$2+31"+$"$2+32",$"$2+16"}"'}|sed -e s/\{/\{\'/g -e s/\}/\'\}/g|bash|awk '{if ($1 == "2") print $2; else if($1 == "3") print "1"; else print "0"}'|sed -e 1s/^/0000000000000000/ -e '$ a 00000000000000'|sed s/./\&\\\n/g|sed /^\$/d|tee a

 

まず前者であるが,初期状態を与えるためのシェル芸である.実行した方は分かったかと思うが,今回得る出力はtextimgを用いたgifファイルにする予定であり,フィールドサイズを15×15にした.それをC言語の二次元配列をポインタとして読み出すとき同様一列にし,空白aの連続数,生存bの連続数を羅列してパイプで次に渡した.

渡した後の処理で複数パイプがあるが,これを0/1の数列に複合,teeコマンドを用いて標準出力に与えつつaというファイルに保存した.

 

後者では保存されたaを読み出し,yesコマンドとheadコマンドで最上行と最下行を除いた13行である195セル(正確には一行目及び二行目の一列目セルは演算されず,最終行は1列目セルのみ演算される)を演算するために行数を増殖させる.

nlコマンドで行数を出して次のawkで順序を入れ替える.その次のawk内でawk文を生成してsedで整え,bashに渡す.この段階で得られるのは周囲セルの合計数と自セルの0/1である.次のawkのif文で次世代の0/1を演算する.先ほど記述したとおり,最上行と最下行(正確には違う上記の通り)を0で埋めてやり,出力形態をsedで整えてからteeコマンドで標準出力に渡しつつaと言うファイルに"上書き"する.

 

もとのシェル芸に戻る.現段階でbase64のデコードは終わった.

そしてデコードが終わって得られている標準出力は初期配置コマンド\n演算コマンド*7である.

sedをもちいてこれらの実効後に残ってしまうファイルaを削除するコマンドを最終行の次行に追加し,bashに渡して演算を行う.このままでは1世代から8世代までのセル連チャンについてただ一列に並べただけのデータであるから15文字ずつに改行する形にawkで処理,0/1をsedを用いて見やすい形にしてやり,textimgで15行ごとに区切ってgifアニメーションとした.

 

正しくこれらを理解していれば分かると思うが,今回のライフゲームフィールドは最上行及び最下行は0となるためフィールドは上下方向には有限である.それに対し左右方向について見てみると一列の配列として処理しているので左端セルの左側隣接セルとして一行上の右端セルが,逆に言えば右端セルの右側隣接セルとして一行下の左端セルが認識されてしまう.何かこのシェル芸を用いて他の振動子や移動体等を得る際にはお気をつけください.シェル芸と言うにはbase64でコマンド群かくしてるだけだからチートな気がするけど気にしない.文字数の都合でシェル芸botに流せないのが悲しい.他の振動子や移動体については気が向いたら書く.眠いのでこの辺で

 

2020/02/17 23:30頃追記

やはりbase64を用いてのワンライナー化は個人的に納得しきれていなかったので改良した.まだ改良の余地はあるだろうがひとまず記しておく

 

yes "cat a|tr '\n' ' '|awk '{for (i=1;i <= 195; i++) print \$i+\$(i+1)+\$(i+2)+\$(i+15)+\$(i+17)+\$(i+30)+\$(i+31)+\$(i+32),\$(i+16)}'|awk '{if (\$1 == 2) print \$2; else if(\$1 == 3) print 1; else print 0}'|sed -e 1s/^/0000000000000000/ -e '$ a 00000000000000'|sed s/./\&\\\n/g|sed /^\$/d|tee a"|head -n 7|sed "1i echo a48b2a1b6a6b2a1b6a6b2a13b2a5b2a6b2a5b2a6b2a5b2a13b2a6b6a1b2a6b6a1b2a48|sed -e \"s/a/\\\n0\\\\|head\ \-n\ /g\" -e \"s/b/\\\n1\\\\|head\ \-n\ /g\"|sed /^\$/d|sed \"s/^/yes\ /g\"|bash|tee a"|sed "$ a rm a"|bash|awk '{ORS=NR%15?"":"\n";print}'|sed s/0/\ \ /g|sed s/1/\[\]/g|textimg -asl15 -F100 -f /u*/s*/f*/t*/h*o/*A.ttf

 

軽く解説と言うか何というか

yesコマンドで演算コマンドを複数行生成,上から得たい世代分だけheadコマンドで得る.この時,この前のシェル芸ではこちらから与える初代を第1世代としてカウントしていたのに対し,今回のシェル芸では第0世代としてカウントしている.

さて,これにより得られた演算コマンド群の最初の行にsedを用いて初代の配列を与えるワンライナーを追加し,また最終行に途中生成ファイルであるaを削除するようrm aを追加する.これをbashで実行してやり,標準出力をawkにより15文字づつの改行にしてsedで文字を変換,textimgでgifアニメーション出力を得る.

初期配列を得るワンライナーに変更はないが,演算用ワンライナーには変更があり,yesとheadを用いて演算用に標準出力を調整していた箇所をawkのfor文を用いることで短くし,無駄を減らした.

得られる出力結果は変わらないが,可読性が高くなり,またシェル芸の文字数も979文字から637行に減ったため改良したとする.

 

最後に自身へのメモ及び読者が真似するときに気にすべきパラメータについてまとめる.

この追記でのシェル芸にて,色のついた箇所がパラメータである.

行(縦方向のセル数)をN

列(横方向のセル数)をM

世代数をG(初期値で与える世代は0世代とする)

 

一つ目のawk内のfor文のこの値(195)は "(N-2)*M"

二つめの15,17,30,31,32はそれぞれM,M+2,2*M,(2*M+1),(2*M+2)

三つめの16は上記同様M+1

四つめのsedで加えている0の数は前者がM+1,後者がM-1

五つめのheadの引数7はG

六つめのechoで呼び出している文字列はa=空白,b=生きてるセルとしてフィールドの改行を取り消した時のセルの初期状態の記述.

七つめのawk内の15はM

八つめのtextimg内の15はN

 

上記条件でシェル芸を書き換えれば好きな初期配置,フィールドサイズを実現出来る.