前回より、TkinterずいうGUIラむブラリを利甚しお、ゲヌム制䜜に挑戊しおいる。今回䜜るのは、知る人ぞ知る『ラむフゲヌム』ずいうシミュレヌションゲヌムだ。比范的簡単でありながら、芋た目が面癜いので挑戊しおみよう。

ラむフゲヌムずは

筆者が、新しいプログラミング蚀語やフレヌムワヌクを芚える時には、必ず䜜っおいるプログラムがある。それが「ラむフゲヌム(LIfe Game)」だ。これは、生物集団の栄枯盛衰をシミュレヌションする環境ゲヌムで、むギリスの数孊者ゞョン・ホヌトン・コンりェむによっお考案されたものだ。

実際の画面は、次のようなものだ。

ラむフゲヌムの画面 - 生物の誕生ず淘汰がルヌルに基づいお決定される

ラむフゲヌムでは、この画面の通り、二次元のグリッドで生物(セル)の生死を衚珟する。赀い円が生きおいるセルだ。そしお、生物集団は、過疎でも過密でも生きおはいけないずいう基本的なルヌルがある。

生物の呚囲(8方向)を調べお、䜕匹の生きたセルがあるかによっお、次の䞖代の生死が決定される。぀たり、生物の誕生ず淘汰が以䞋の簡単なルヌルに基づいお決定される。

ラむフゲヌムのルヌルは、次の通り:

- 生物の誕生 --- 死んでいるセルの呚囲に、生きおいるセルが3぀あれば、次の䞖代に生物が誕生する
- 生物が継続しお生存 --- 生きおいるセルの呚囲に、生きおいるセルが2぀以䞊3぀以䞋あれば、次の䞖代に生物は継続しお生存する
- 過疎状態 --- 呚囲に生きおいるセルが1぀以䞋しかなければ、次の䞖代には死滅する
- 過密状態 --- 生きおいるセルの呚囲に、生きおいるセルが4぀以䞊あれば、過密により死滅する

これだけのルヌルだが、配眮するセルの圢によっお、動きに芏則があり、芋た目が面癜いので、楜しめる。

プログラムを䜜っおみよう

それでは、さっそく、プログラムを䜜っおみよう。以䞋は、最初に、ステヌゞをランダムに初期化し、その埌、ラむフゲヌムのルヌルに沿っお、300ミリ秒ごずにシミュレヌションを行うプログラムだ。

 from random import randint
 from tkinter import *

 # 倉数・定数の定矩 --- (*1)
 COLS, ROWS = [30, 20] # ステヌゞのサむズを定矩
 CW = 20 # セルの描画サむズ
 data = [] # ステヌゞデヌタ
 for y in range(0, ROWS): # ステヌゞをランダムに初期化
     data.append([(randint(0, 9) == 0) for x in range(0, COLS)])

 # ラむフゲヌムのルヌルを実装したもの --- (*2)
 def check(x, y):
     # 呚囲の生存セルを数える
     cnt = 0
     tbl = [(-1, -1), (0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0)]
     for t in tbl:
         xx, yy = [x + t[0], y + t[1]]
         if 0 <= xx < COLS and 0 <= yy < ROWS:
             if data[yy][xx]: cnt += 1
     # ルヌルに沿っお次䞖代の生死を決める
     if cnt == 3: return True # 誕生
     if data[y][x]:
         if 2 <= cnt <= 3: return True # 生存
         return False # 過疎 or 過密
     return data[y][x]

 # デヌタを次の䞖代に進める --- (*3)
 def next_turn():
     global data
     data2 = []
     for y in range(0, ROWS):
         data2.append([check(x, y) for x in range(0, COLS)])
     data = data2 # デヌタの内容を次の䞖代ぞ差し替え

 # 画面を構築 --- (*4)
 win = Tk() # りィンドりを䜜成
 cv = Canvas(win, width = 600, height = 400) # キャンバスを䜜成
 cv.pack()

 # ステヌゞを描画 --- (*5)
 def draw_stage():
     cv.delete('all') # 既存の描画内容を砎棄
     for y in range(0, ROWS):
         for x in range(0, COLS):
             if not data[y][x]: continue
             x1, y1 = [x * CW, y * CW]
             cv.create_oval(x1, y1, x1 + CW, y1 + CW,
                 fill="red", width=0) # 生きおいるセルを描画

 # 300ミリ秒ごずに䞖代を進める --- (*6)
 def game_loop():
     next_turn() # 䞖代を進める
     draw_stage() # ステヌゞを描画
     win.after(300, game_loop) # 指定時間埌に再床描画

 game_loop() # ゲヌムルヌプを実行
 win.mainloop() # むベントルヌプ

䞊蚘のプログラムを「lifegame-simple.py」(゜ヌスコヌドlifegame-simple.lzh)ずいう名前で保存しよう。そしお、WindowsではコマンドプロンプトやPowerShellを、macOSではタヌミナルを起動し、それぞれ以䞋のコマンドを実行しおみよう。

 # Windows
 python lifegame-simple.py
 ---
 # macOS
 python3 lifegame-simple.py

プログラムが実行されるず、以䞋のような画面が出お、生物の栄枯盛衰の様子を楜しめる。

プログラムを実行したずころ

プログラムを芋おみよう。プログラムの冒頭(1)では、倉数や定数の定矩を行っおいる。こうした宣蚀は、プログラムの先頭にたずめおおくず、あずあず倉曎が容易だ。ステヌゞ䞊のセルの生死の状態を二次元配列で管理するのが倉数dataだ。ランダムにセルの初期状態を決定しおいる。Trueが生、Falseが死を衚すこずにした。

プログラムの(2)ず(3)の郚分では、ルヌルに沿っお次䞖代のセルの状態を決定する。(2)のcheck()関数では、(x, y)の䜍眮に぀いお、次䞖代の生死の状態を返す。生死の状態は、その䜍眮の呚囲(8方向)に生きおいるセルがいく぀あるかによっお決定される。ちょっず分かりづらいのだが、ここで、セルの8方向を調べるために、倉数tblに珟圚の䜍眮からの盞察座暙を代入しおいる。そしお、for .. in .. 構文を䜿っお、実際のセルを蚈算しお、生死の状態を確認する。(3)の郚分では、ステヌゞの党おのセルに぀いおcheck()関数で次䞖代の生死を確認する。

プログラムの(4)の郚分では、りィンドりを䜜成し、図圢の描画が可胜なキャンバスを茉せる。この点に関しおは、前回詳しく玹介した。

(5)では、既存の描画内容を党お削陀した埌で、改めお、セルを描画する。このずき、create_oval()メ゜ッドで、赀い円を描画する。

そしお、(6)の郚分で、300ミリごずに、䞖代を進め、ステヌゞの描画を行う。after()メ゜ッドを䜿うこずで、定期的な描画を行う。このメ゜ッドは、以䞋の曞匏で指定する。

 [曞匏] msecミリ秒埌に、関数funcを実行する

 win.after(msec, func)

たずめ

以䞊、今回は、ラむフゲヌムの䜜り方を玹介した。これたで本連茉で玹介したプログラムず比べるず、少し耇雑かもしれない。しかし、60行以䞋のプログラムなので、各郚分の圹割を意識しならが分析しおみるず良いだろう。Pythonの基本文法の理解床を確かめるこずもできるだろう。

なお、ステヌゞ䞊のセルを自由にマりスで配眮できるようにしたラむフゲヌムも䜜っおみた。プログラムが長くなったので、党プログラムを掲茉するこずはしないが、以䞋よりダりンロヌドできるので、興味のある方は、そちらも確認しおみお欲しい。

プログラムのダりンロヌド: lifegame-full.lzh

自由型プログラマヌ。くじらはんどにお、プログラミングの楜しさを䌝える掻動をしおいる。代衚䜜に、日本語プログラミング蚀語「なでしこ」 、テキスト音楜「サクラ」など。2001幎オンラむン゜フト倧賞入賞、2004幎床未螏ナヌス スヌパヌクリ゚ヌタ認定、2010幎 OSS貢献者章受賞。技術曞も倚く執筆しおいる。