tomabouの日記

Haskellなどで勉強したことなどを書いていきます

セルオートマトンで遊ぶ

はじめに

皆さんパソコンの背景は何にしていますか?なんか背景を変えたいなと思い、色彩センスはゼロですがいい感じの規則的な模様を勝手に生成させて背景にすることができたので紹介します。

一次元セルオートマトン

一次元セルオートマトンをご存知でしょうか
セル・オートマトン - 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を使用しています。

実行結果

入力

python automaton.py 30 simple

f:id:tomabou:20171008173553p:plain

python automaton.py 90 random 0.02

f:id:tomabou:20171008173116p:plain

デスクトップ背景

コード

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()

*1:ちなみにマリオメーカーチューリング完全であることを示すのに使用されたcyclic tag systemチューリング完全性はこのルール110のチューリング完全性を示すために用いられたものなので深い関係が実はあります