セルオートマトンで遊ぶ
はじめに
皆さんパソコンの背景は何にしていますか?なんか背景を変えたいなと思い、色彩センスはゼロですがいい感じの規則的な模様を勝手に生成させて背景にすることができたので紹介します。
一次元セルオートマトン
一次元セルオートマトンをご存知でしょうか
セル・オートマトン - Wikipedia
二次元の場合はライフゲームと言われたりします。簡単に言うと一列に0と1が並んでいて、1ステップごとに0と1が周りの0,1の配置に応じて変化するというものです。単純なものではi番目の数字は1ステップ前のi-1, i, i+1番目の数字(8通り考えられる)のみに依存して変わるもので、ルールを次のような表にまとめることが出来ます。
1ステップ前の数字 | 0になるか1になるか |
---|---|
0,0,0 | 0 |
0,0,1 | 1 |
0,1,0 | 1 |
0,1,1 | 1 |
1,0,0 | 1 |
1,0,1 | 0 |
1,1,0 | 0 |
1,1,1 | 0 |
このようなルールは256通り考えることが出来て、上の表を二進数だと思うことでルール0からルール255と名前がついています。上の場合はルール30(00011110)です。
興味深い性質
ほとんど意味のないルールもありますが(例えばルール0は1ステップですべて0になって変化しません)、ルール18,22,30,54,60,90,110などは面白い挙動を示すらしいです。ちなみにルール110はマリオメーカーと同様にチューリング完全であるらしいです。チューリング完全オタクの力は怖いですね。*1
画像生成
一次元の数字の列が時系列に沿って変化していくので、それを並べると二次元の0,1の表になります。せっかくなのでルールを入力して画像を出力できるようにしてみました。
pythonで書かれていて、実行時引数としてルールと初期条件を与えることが出来ます。
simpleのときは真ん中に1が一つで残り0。randomのときはその次に与えた引数の確率で1になります。確率が0に近いときと0.5に近いときで異なる見た目のものが生成されるのでいろいろ試してみてください。最後に自分が背景に使用している画像を張っておきます。
ライブラリとしてはPILを使用しています。
コード
from PIL import Image import sys import numpy as np import random def make_image(rule,mode = "simple",p=0.5): color1 = (0x66,0x99,0x99) #0の色 color2 = (0xff,0x99,0x99) #1の色 x = 1920 y = 1080 #出力画像サイズ 今はフルHDの画像を出力します dotsize = 2 #一つのセルがドットいくつ分か filename = "rule"+str(rule)+"_"+str(x)+"x"+str(y)+"_"+str(dotsize)+"_"+mode+".png" img = Image.new('RGB',(x,y),color1) rule_list = list() for i in range(8): rule_list.append(rule %2) rule = rule // 2 print (rule_list) nx = x//dotsize + 1 ny = y//dotsize + 1 cell_map =[[0 for i in range(ny)] for j in range(nx+2) ] if mode=="simple": cell_map[nx//(2)][0] = 1 elif mode =="random": for i in range(nx): cell_map[i][0]=1 if random.random()<p else 0 cell_map[nx][0]=cell_map[0][0] cell_map[nx+1][0]=cell_map[1][0] for j in range(ny-1): for i in range(nx): num = 4*cell_map[i][j] + 2* cell_map[i+1][j]+cell_map[i+2][j] cell_map[i+1][j+1] = rule_list[num] cell_map[0][j+1]=cell_map[nx][j+1] cell_map[nx+1][j+1]=cell_map[1][j+1] for i in range(x): for j in range(y): if cell_map[i//dotsize][j//dotsize]==1: img.putpixel((i,j),color2) img.save(filename) return img if __name__ == "__main__": argv = sys.argv if len(argv)==1: rule = 30 else: rule = int(argv[1]) if len(argv)<=2: mode = "simple" else: mode = argv[2] if len(argv)>3: p = float(argv[3]) else: p = 0.5 img = make_image(rule,mode,p) img.show()