マンデルブログ

主にPythonのことを書いていきます。

Pythonでライフゲーム

本当は昨日書くつもりだった

ご無沙汰しております。
昨日、久しぶりに何かプログラム作ってブログ書こうと思っていたんです。
プログラムを作り終わったころには深夜になっていたので、もう寝てしまいました。
今から書きます。

ライフゲームとは

ライフゲームとは、生命の誕生、進化、淘汰などのプロセスを簡易的なモデルで再現したセルオートマトンです。
イギリスの数学者Conwayが考案しました。
二次元正方格子上で自身の周りの8セルの状態に依存して時間発展していきます。

各セルが誕生、生存、死亡するルールは以下の4つです。

  1. 空白のセルの周辺に生きたセルがちょうど3つあれば誕生(増殖)
  2. 生きたセルの周辺に生きたセルが2~3つあれば生き続ける(生存)
  3. 生きたセルの周辺に生きたセルが1つ以下ならば死亡(過疎)
  4. 生きたセルの周囲に生きたセルが4つ以上ならば死亡(過密)

固定物体、振動子、移動物体など、多彩で見ていて面白い物体がたくさん作られるのですが
ライフゲーム - Wikipediaに結構詳しく書かれていたのでそちらを参照してください。

Pythonで書いてみよう

L \times Lの場にある各セルが1(生)か0(死)の状態を取るとします。
まずは、ライフゲームの心臓部である時間発展をさせる関数を作っていきます。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

class LifeGame():
    def __init__(self,N):
        self.L = N
        self.field = np.zeros([self.L,self.L], dtype=int)
        self.next_field = np.zeros([self.L,self.L], dtype=int)

    def evolution(self):
        for i in range(self.L):
            for j in range(self.L):
                evlist = self.environment(i,j)
                evnum = sum([self.field[i[0],i[1]] for i in evlist])
                
                if self.field[i,j] == 0:
                    if evnum == 3:
                        self.next_field[i,j] = 1
                    else:
                        self.next_field[i,j] = 0
                elif self.field[i,j] == 1:
                    if evnum == 2 or evnum == 3:
                        self.next_field[i,j] = 1
                    elif evnum <= 1:
                        self.next_field[i,j] = 0
                    elif evnum >= 4:
                        self.next_field[i,j] = 0
                        
        self.field = self.next_field
        self.next_field = np.zeros([self.L,self.L], dtype=int)
        return self.field

試しにブリンガーという振動子を入れてみます。
3つのセルが縦横に振動するやつです。

def test():
    L = LifeGame(5)
    L.field = np.array([[0,0,0,0,0],
                        [0,0,1,0,0],
                        [0,0,1,0,0],
                        [0,0,1,0,0],
                        [0,0,0,0,0]])
    L.evolution()
    print(L.field)
[[0 0 0 0 0]
 [0 0 0 0 0]
 [0 1 1 1 0]
 [0 0 0 0 0]
 [0 0 0 0 0]]

良さげです。いえーい

アニメーションにする

ライフゲームは時間発展の様子が面白いんです。
アニメーションにしないとよくわからない点の集合を見せるしかありません。
ということでGIFアニメ―ションにしてみたんですが、
PythonでのGIFの保存が思っていたよりも面倒くさくてかなり時間を使ってしまいました。

own-search-and-study.xyz

こちらのサイトを参考になんとかPythonでアニメーションを完成させることができました。
完成したのがこちらです。

f:id:woodhero0908:20181109235110g:plain


いくつかの固定物体、振動子があるのがわかりますね。
しばらくこれで遊んでみたいと思います。

最後に完成したプログラムを貼っておきます。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

class LifeGame():
    def __init__(self,N):
        self.L = N
        self.field = np.zeros([self.L,self.L], dtype=int)
        self.next_field = np.zeros([self.L,self.L], dtype=int)
        
    def init(self,p):
        self.field = np.random.choice([0,1],
                    [self.L,self.L],p=[1-p,p])


    def environment(self,i,j):
        return [ [(i-1 + self.L)%self.L, (j-1 + self.L)%self.L],
                 [(i-1 + self.L)%self.L, (  j + self.L)%self.L],
                 [(i-1 + self.L)%self.L, (j+1 + self.L)%self.L],
                 [(  i + self.L)%self.L, (j-1 + self.L)%self.L],
                 [(  i + self.L)%self.L, (j+1 + self.L)%self.L],
                 [(i+1 + self.L)%self.L, (j-1 + self.L)%self.L],
                 [(i+1 + self.L)%self.L, (  j + self.L)%self.L],
                 [(i+1 + self.L)%self.L, (j+1 + self.L)%self.L]]


    def evolution(self):
        for i in range(self.L):
            for j in range(self.L):
                evlist = self.environment(i,j)
                evnum = sum([self.field[i[0],i[1]] for i in evlist])
                
                if self.field[i,j] == 0:
                    if evnum == 3:
                        self.next_field[i,j] = 1
                    else:
                        self.next_field[i,j] = 0
                elif self.field[i,j] == 1:
                    if evnum == 2 or evnum == 3:
                        self.next_field[i,j] = 1
                    elif evnum <= 1:
                        self.next_field[i,j] = 0
                    elif evnum >= 4:
                        self.next_field[i,j] = 0
                        
        self.field = self.next_field
        self.next_field = np.zeros([self.L,self.L], dtype=int)
        return self.field
    
def main():
    L = LifeGame(100)
    L.init(0.3)
    
    fig = plt.figure(figsize=(5,5))
    ax = fig.add_subplot(111)
    ax.set_ylim(0,100)
    ax.set_xlim(0,100)
    n = 100
    
    ims = []
    
    for i in range(n):
        ev_arr = L.evolution()
        pl_arr = np.array(np.where(ev_arr == 1))
        im=plt.plot(*pl_arr,"s",ms=2,c="r")
        ims.append(im)
     
    ani = animation.ArtistAnimation(fig, ims, interval=200, repeat_delay=1000)
    ani.save("LifeGame.gif",writer="imagemagick")