Pythonのススメ11

はじめに

Pythonでプログラムを書くにあたり、文法や言語仕様などの個人的なメモを記載する。
今回のネタはライフゲーム
Tkinterという、PythonGUIツールを使ってライフゲームの実装を行った。


ライフゲーム

生命の誕生、進化、淘汰などのプロセスを簡易的なモデルで再現したシミュレーションゲームで、イギリスの数学者ジョン・ホートン・コンウェイによって考案された。
セル・オートマトンの一種と言われているが、難しいことは置いといて、ライフゲームには以下のようなルールがある。

生物の誕生

 死んでいるセルの周囲に生きているセルが3つあれば、次の世代に生物が誕生する。

生物が継続して生存

 生きているセルの周囲に生きているセルが2つ以上3つ以下あれば、次の世代に生物は継続して生存する。

過疎状態

 周囲に生きているセルが1つ以下しかなければ、次の世代には死滅する。

過密状態

 生きているセルの周囲に生きているセルが4つ以上あれば、過密により死滅する。

上記4つの制約(いずれもシンプル)を守ったうえでゲームを実装する。


ライフゲームソースコード

今回作成したソースコードは以下の通り。
乱数を使って状態を初期化し、300ミリ秒ごとに上記制約から次の状態を判断し描画している。
(ループを多用しすぎている感があるので、とりあえず動くものは出来たが、リファクタリングの価値はまだあると思っている)

from tkinter import *
from random import randint

COLS, LOWS = [70, 50]
CW = 10
data = []
CELL_NEXT_TURN_VAL_EXIST = 1
CELL_NEXT_TURN_VAL_NON_EXIST = 0
TIME_INTERVAL = 300
win = Tk()
cv = Canvas(win, width=700, height=500)
cv.pack()

"""
Canvasの初期化メソッド.
0か1の乱数を発生し、1の場合は青色の円を描写する.
"""
def initializeCanvas():
    tmp = []
    for col in range(0, COLS):
        for low in range(0, LOWS):
            tmp.append(randint(0, 1))
        data.append(tmp)
        tmp = []
    paintCanvas(data)


"""
次ターンでセルが生存しているかどうか.
"""
def isExistNextTurn(col, low, data):
    targetPointList = [[-1, -1], [0, -1], [1, -1], [-1, 0], [1, 0], [-1, 1], [0, 1], [1, 1]]
    currentPositionValue = data[col][low]
    result = 0
    for targetPoint in targetPointList:
        x = col + targetPoint[0]
        y = low + targetPoint[1]
        if (0 <= x < COLS) and (0 <= y < LOWS):
            if data[x][y] == 1:
                result += 1

    # 次ターンで「誕生」
    if currentPositionValue == 0 and result == 3:
        return True
    # 次ターンで「存続」
    elif currentPositionValue == 1 and 2 <= result <= 3:
        return True
    # 次ターンで「消滅」
    else:
        return False


def getNextTurnCellData(data):
    result = []
    tmp = []
    for col in range(0, COLS):
        for low in range(0, LOWS):
            if isExistNextTurn(col, low, data):
                tmp.append(CELL_NEXT_TURN_VAL_EXIST)
            else:
                tmp.append(CELL_NEXT_TURN_VAL_NON_EXIST)
        result.append(tmp)
        tmp = []
    return result


def paintCanvas(data):
    cv.delete("all")
    for col in range(0, COLS):
        for low in range(0, LOWS):
            if data[col][low] == 1:
                cv.create_oval(col * CW, low * CW, col * CW + CW, low * CW + CW, fill="blue")
            else:
                cv.create_oval(col * CW, low * CW, col * CW + CW, low * CW + CW, fill="white")


def game_loop(data):
    data = getNextTurnCellData(data)
    paintCanvas(data)
    win.after(TIME_INTERVAL, game_loop, data)  # 指定時間後に再度描画


initializeCanvas()
win.after(TIME_INTERVAL, game_loop, data)  # 指定時間後に再度描画
win.mainloop()


終わりに

本投稿では、Pythonライフゲームの実装をしたことを述べた。
守るべき制約はあるものの、いずれもシンプルなものであり、工夫次第でいくらでもプログラムできる。
今回記載したソースコードでとりあえず動くが、ループを多用しすぎている感がある。
もっと良い書き方があると思っているので、気が向いたらリファクタリングをしてみようと思う。


参考文献

ライフゲーム - Wikipedia
ゼロからはじめるPython(9) 生物集団の栄枯盛衰"ライフゲーム"を作ってみよう | マイナビニュース
ゼロからはじめるPython(8) ゲームで覚えるPythonプログラミング - Tkinterで始めよう | マイナビニュース