見出し画像

ライフゲームを作る(2) Pythonで

Cindyscriptでライフゲームを作る授業のことは前回書いた。

次はこれをPythonで書くことだ。これからは高校の授業でもプログラミングはPythonが主流になるだろうし,ライフゲームは教科書にも載っているものだからだ。

とりあえず,matplotlib  でやってみた。パソコンでこのnoteを読まれている人は,前回のものとならべてみると,Cindyscriptとの共通点・違いがよくわかるだろう。

まず,初期設定。こちらは9×9にしてみた。

import numpy as np
import matplotlib.pyplot as plt
#--- 初期設定  ---------------------
N = 9
initLife = [[0,0,0,0,0,0,0,0,0],
           [0,0,0,0,0,0,0,0,0],
           [0,0,0,0,0,0,0,0,0],
           [0,0,0,0,1,1,0,0,0],
           [0,0,0,1,1,0,0,0,0],
           [0,0,0,0,1,0,0,0,0],
           [0,0,0,0,0,0,0,0,0],
           [0,0,0,0,0,0,0,0,0],
           [0,0,0,0,0,0,0,0,0]]
Generation = 2                   # 表示する世代
#----------------------------------
Life = np.array(initLife)   # 現世代
preLife = np.zeros((N,N))   # 前世代
plt.figure(figsize=(N/3*2+1,(N+1)/3))
plt.axis([0,N*2+1,0,N+1])
plt.xticks([])
plt.yticks([])

Matrix を life でなく Life にしたのは,Matrix より意味が通じるかということ。また,先頭を L にしたのは,l (エル)と1(数字のいち)が紛らわしいから。意味を考えずに打ち込んで間違える生徒がひとりやふたりはいる。次に,画面表示の関数を作る。Cindyscriptの display() に相当する。

def drawboad():
   for y in range(N):
       py = [y,y,y+1,y+1,y]
       for x in range(N):               # (x,y) 1マスずつ描画
           px = [x,x+1,x+1,x,x]
           plt.plot(px,py,color = 'k',lw = 0.5)
           if preLife[y][x] == 1:    # x,yの順序に注意
               plt.plot(x+0.5,N-y-0.5,'ob',markersize = 10)
           px = [x+N+1,x+N+2,x+N+2,x+N+1,x+N+1]
           plt.plot(px,py,color = 'k',lw = 0.5)
           if Life[y][x] == 1:    # x,yの順序に注意
               plt.plot(x+N+1.5,N-y-0.5,'ob',markersize = 10)
   plt.text(2,N+0.3,str(Generation-1)+' th generation ago')
   plt.text(3+N,N+0.3,str(Generation)+' th generation')

生命体は円を描画するのでなく,plot の 'o' オプションで点にする。サイズを 10 にすれば大きくなるのでそれでよい。現世代と前世代の2つを描画しているので,Cindyscript版のおよそ2倍の分量があることになる。
 次のように表示される。

画像1

次に,次世代を求める next() を作る。Cindyscriptのコードとほぼ同じだ。

def nextlife():
   work = np.zeros((N,N))     # 作業用配列
   for i in range(1,N-1):
       for j in range(1,N-1):
           s = 0              # 周囲8コマの合計
           for x in range(i-1,i+2):
               for y in range(j-1,j+2):
                   s = s + Life[x][y]
           s = s - Life[i][j]
           if Life[i][j] == 0 and s == 3:             # 誕生
               work[i][j] = 1
           if Life[i][j] == 1 and (s == 2 or s == 3): # 生存
               work[i][j] = 1    

   for x in range(N):
       for y in range(N):
           preLife[x][y] = Life[x][y]     # 現世代を前世代に
           Life[x][y] = work[x][y]        # 次世代を現世代に

この中の何箇所かを空欄にして,生徒の課題とすればよい。

あとはこの関数を実行して表示するだけ。

for i in range(Generation+1):
   nextlife()
drawboad()
plt.show()

Generation は初めに2としている。したがって,第1世代と第2世代が表示され,前掲図のようになる。3にすればもう一つ進んだ世代が表示される。

コード量はCindyscriptのものよりかなり多くなっているが2つ作っているのでやむを得ないところだ。
ボタンで世代を進めるということはできていない。マウスクリックで生命体を置くというのもできていないから,別パターンをやるにははじめのリストの 1/0 を書き換えるしかない。
それでも,実習の目標が「ルールに従って世代を進めるプログラムを書く」ということであればこれでできたことになる。

matplotlib でボタンを作る方法を書いたWebページもあるが,具体的に作ろうとするとよくわからない。matplotlib 本家のページで Button のところを読めばいいのだが,読んでもわからない。わからない用語がたくさんあるからだ。
マウスクリックを検出して座標を取得する方法を書いてあるページも見たが,そのコードをコピーしても書き換えてみると思ったような動きをしない。

しかたがないのでそのままになっていた。

Amazon からの案内で,「逆引き Python 標準ライブラリ」(大津真,田中賢一郎:インプレス)を買った。単行本 3300円のところ,Unlimited で0円。

この中に,Tkinter の解説があった。今まで,ボタンの作り方はわかっていたが,図形の描き方がわからなかった。読んでみると,Tkinter で Canvas に図形を描くというのがある。そうなのか,Canvas というのを作って図形を描くのか。 
相変わらずよくわからない用語が並んでいるが,できそうだ。
よくわからないので,Webを検索した。そうして,

を見つけた,というわけ。
分量は多いが,用語の説明もあり,重複するくらいの説明がわかりやすい。

つまり

 初心者がプログラミングでつまづくのは,「用語の意味がわからない」というのが結構あるのだ。今になって「逆引き python ライブラリ」のTkinter の節を読むと意味がわかる。最初はわからなかったが。
ずっと以前買った,辛島信芳の「tkinter 速習入門」も,いまならわかる部分が多くなった。
 しかし,matplotlib での,ボタンやマウスクリックの取得をもう一度読み直してもなぜ動かないかわからない。原理がわからないからだ。

というわけで,Tkinter 版は次の記事にする。というのは,別記事の方が比較しやすいと思うからだ。タブレット端末やスマホでは2画面を並べることはできないが,パソコンなら2画面を並べて比較できるだろう。

なお,三重大の奥村先生のページには,これとは異なる方法で作ったものが載せられている。