1.この記事を読んで学べること・学べないこと
学べること
- Python における Curses 利用の初歩の初歩。
- ライフゲーム製作の一例。
学べないこと
- 優れたPython コードの書き方。
- 優れたライフゲームのアルゴリズム。
2.実際のコード
lifegame.py
class Life:
def __init__(self, state=False):
"""
State means whether the life is alive or not.
Count means how many lives there are around the life.
Survive means whether the life survives this turn.
"""
self.count = 0
self.state = state
self.survive = False
def rule(self):
""" The core rule of this life game. """
if self.state:
if self.count in {2,3}:
self.survive = True
else:
self.survive = False
else:
if self.count is 3:
self.survive = True
else:
self.survive = False
def update(self):
""" Return True or False.
Update the state of the life after checking if it will
survive or not.
"""
self.state = self.survive
self.count = 0
return self.survive
class Environment(list):
def __init__(self, directory):
""" Initialize the array with loaded text data. """
self.life_count = 0
text_env = self.load_from(directory)
super().__init__(self)
for y in range(self.height):
self.append([])
for x in range(self.width):
if self.is_there_a_life_in( text_env[y][x] ):
self[y].append(Life(True))
else:
self[y].append(Life(False))
def is_there_a_life_in(self, string):
""" Return True or False.
Text data to load must be composed of two characters '*' and
'_'. '*' means there is life. '_' means life is dead.
"""
if string is "*":
return True
elif string in {"_", None}:
return False
else:
raise TypeError("Undefined Char Used.")
def load_from(self, directory):
""" Return list of string.
Load text data from file and calculate size of where lives
are simulated.
"""
self.width = 0
text_env = []
for char_list in self.parse_map(directory, "r"):
if self.width < len(char_list):
self.width = len(char_list)
text_env.append(char_list)
self.height = len(text_env)
return text_env
def parse_map(self, directory, mode):
""" Strips '\n' off read line. """
for line in open(directory, mode):
yield list(line.rstrip('\n'))
def how_many_lives(self):
""" Count up the lives around the life. """
for y in range(self.height):
for x in range(self.width):
for j, i in self.surround(y, x):
if self[j][i].state:
self[y][x].count += 1
self[y][x].rule()
def surround(self, y, x):
""" Generate coordinates around the life. """
from itertools import product
Y = ( y-1, y, y+1 )
X = ( x-1, x, x+1 )
for j,i in product(Y, X):
j = j % self.height
i = i % self.width
if (j, i) == (y, x):
continue
else:
yield (j, i)
def update(self):
""" Update each cell and count the number of life in the
Environment.
"""
self.life_count = 0
for y in range(self.height):
for x in range(self.width):
if self[y][x].update():
self.life_count += 1
class LifeGame:
def __init__(self, directory):
self.env = Environment(directory)
self.turn = 0
from curses import wrapper
wrapper(self.main) # main メソッドを stdscr オブジェクトを与えて呼び出す
def next_turn(self):
self.env.how_many_lives()
self.env.update()
def show_status(self, stdscr):
""" Print number of life in the environment and how many
turn has passed.
"""
stdscr.addstr( 3, self.env.width+3,
"LifeCount: %d" % self.env.life_count)
stdscr.addstr( 5, self.env.width+3,
"Generation: %d" % self.turn)
def main(self,stdscr):
while True:
stdscr.clear() # コンソール画面の初期化(黒板を消すの感じ)
for y in range(self.env.height):
for x in range(self.env.width):
if self.env[y][x].state:
stdscr.addstr( y, x, "*" ) # コンソールに出力するメソッド
else:
stdscr.addstr( y, x, "_" )
self.show_status(stdscr)
stdscr.refresh() # 出力内容の反映
if stdscr.getch(): # Enter 入力待ち
self.next_turn()
self.turn += 1
if __name__=='__main__':
LifeGame("./map.txt")
これに例えば,
map.txt
_*_
__*
***
こんなデータを与えてあげると,Enter を押すごとに変わりゆく様子を観察できる。
3.雑感
前に Ruby で Curses に挑んだときは挫折してしまったのだけれど,Python でやってみたらなんかできちゃった。Python が素敵なのか,その頃より少し技術力が上がったのか...