概要
ANSIエスケープシーケンスを使って複数行を書き換えるテストとして書きました。洗練度は低いですがライフゲームが端末上で動きます。
これを動かしておくことで、なんとなくかっこいい感じがします。
実装
基本的には以前作ったときと同じ実装です。
【python】numbaを使ってライフゲームを書いてみた - 静かなる名辞
ただし、使いやすいようにクラスとモジュールに押し込みました。iterableとして実装しています。
main.py
import sys import time from lifegame import LifeGame def field_to_char(A): return "\n".join("".join("#" if char else " " for char in line) for line in A) def main(): seed = int(sys.argv[1]) lifegame = LifeGame(60, 30, seed) for i, x in enumerate(lifegame): print(field_to_char(x)) print("\x1b[30A", end="") time.sleep(0.1) if i > 1000: break print("\x1b[30B", end="") if __name__ == "__main__": main()
lifegame.py
import numpy as np import numba as nb import matplotlib.pyplot as plt @nb.jit(nopython=True) def get_ijlst(x, limit): ret = [] if 0 < x: ret.append(x-1) if x < limit-1: ret.append(x+1) ret.append(x) return ret @nb.jit(nopython=True) def update_cell(i, j, field, out, field_w, field_h): i_lst = get_ijlst(i, field_h) j_lst = get_ijlst(j, field_w) s = 0 for ni in i_lst: for nj in j_lst: s += field[ni, nj] s -= field[i,j] if s < 2: out[i,j] = 0 elif s == 2: out[i,j] = field[i,j] elif s == 3: out[i,j] = 1 elif s >= 4: out[i,j] = 0 else: raise Exception def update_field(pair_lst, field_w, field_h): for i in range(field_h): for j in range(field_w): update_cell(i, j, pair_lst[0], pair_lst[1], field_w, field_h) pair_lst.append(pair_lst.pop(0)) class LifeGame: def __init__(self, field_w, field_h, seed=0): self.field_w = field_w self.field_h = field_h self.random = np.random.RandomState(seed) self.initialize() def initialize(self): field = (self.random.rand(self.field_h, self.field_w) > 0.9 ).astype(np.int16) out = np.zeros(shape=(self.field_h, self.field_w)).astype(np.int16) self.pair_lst = [field, out] def __iter__(self): self.initialize() return self def __next__(self): self.update_field() return self.pair_lst[0] def update_field(self): update_field(self.pair_lst, self.field_w, self.field_h) def main(): lifegame = LifeGame(60, 40) img = plt.imshow(lifegame.pair_lst[0]) for x in lifegame: img.set_data(x) plt.pause(0.001) print(x) if __name__ == "__main__": main()
スポンサーリンク
使い方
上の二つのファイルを同一ディレクトリに置き、
$ python main.py 127
のように端末から実行します(コマンドライン引数は乱数seedで省略不可)。ターミナルエミュレータの場合、できるだけ大きい画面(全画面など)にしておいてください。また、フィールドの幅と高さはmain.pyを数箇所書き換えれば調整できます。
なお、ANSIエスケープシーケンスによる制御を受け付けない端末も中にはあるはずなので、注意が必要です。コマンドプロンプトとかはできないはず。emacsのshellも無理でした。
うまく実行されれば、同じ位置で画面が書き換わりながらライフゲームが描画されるのを楽しめます。
実行画面の例を以下に貼ります。動画は編集が大変なので、静止画ですが・・・。
いけてないところ
- いかんせん文字の縦横比が1:1ではないので、表示が縦長な感じになる
- Ctrl-Cなどで途中で止めると画面にゴミが残ることがある(clearコマンドで消してください)
縦長になるのはともかく、後者はもう少しなんとかしてあげたいところです。
まとめ
CUIで動かすのは思いの外簡単にできました。その気になればけっこう遊べるものも作れると思います(簡単なゲームくらいはいける?)。