LoginSignup
13
12

More than 3 years have passed since last update.

レトロゲームエンジン Pyxel でライフゲーム

Last updated at Posted at 2018-12-17

レトロゲームエンジン Pyxel でプログラミングを始めよう! を見て何か作りたいなという気分になっていたので、ライフゲームをシミュレーションできるやつを作ってみました。

ライフゲームとは

名前の通り生き物をシミュレーションしたようなもので、Wikipediaの記事を見てもらうのが早いです。
実は今年のアドベントカレンダーでも、既に(今これを書いている12/7時点で)

と2つもライフゲームに関する記事を観測していて、早くもネタ被りが怖い。

できたもの

こんな感じ。
pyxel-181207-225400.gif

pyxel-181209-131104.gif

コード

こんな感じで書いてます。gist: https://gist.github.com/HelloRusk/09e255522b6827fe3f4abb3df377d4f4

import pyxel


class App:
    def __init__(self):
        pyxel.init(100, 110, caption="Pyxel Life Game")
        self.is_alive = [[False] * 50 for i in range(50)]
        self.is_alive_next = [[False] * 50 for i in range(50)]

        self.is_moving = False

        pyxel.mouse(True)
        pyxel.run(self.update, self.draw)

    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()

        if pyxel.btnp(pyxel.MOUSE_LEFT_BUTTON):
            x_n, y_n = pyxel.mouse_x, pyxel.mouse_y

            if 101 <= y_n <= 110:
                if 59 <= x_n <= 78:
                    self.is_moving = False
                elif 79 <= x_n <= 100:
                    self.is_moving = True

            if 0 <= x_n <= 99 and 0 <= y_n <= 99:
                self.is_alive[x_n // 2][y_n // 2] = not self.is_alive[x_n // 2][y_n // 2]
                self.is_alive_next[x_n // 2][y_n // 2] = not self.is_alive_next[x_n // 2][y_n // 2]

        if self.is_moving:

            x = [1, 1, 0, -1, -1, -1, 0, 1]
            y = [0, 1, 1, 1, 0, -1, -1, -1]

            for i in range(1, 49):
                for j in range(1, 49):
                    alive_num = 0

                    for k in range(8):
                        if self.is_alive[i + x[k]][j + y[k]]:
                            alive_num += 1

                    if self.is_alive[i][j]:
                        if alive_num <= 1:  # 過疎による死滅
                            self.is_alive_next[i][j] = False
                        elif alive_num >= 4:  # 過密による死滅
                            self.is_alive_next[i][j] = False
                    else:
                        if alive_num == 3:  # 誕生
                            self.is_alive_next[i][j] = True

            for i in range(1, 49):
                for j in range(1, 49):
                    self.is_alive[i][j] = self.is_alive_next[i][j]

    def draw(self):
        pyxel.cls(0)

        for i in range(50):
            for j in range(50):
                if self.is_alive[i][j]:
                    pyxel.pset(2*i, 2*j, 8)
                    pyxel.pset(2*i, 2*j+1, 8)
                    pyxel.pset(2*i+1, 2*j, 8)
                    pyxel.pset(2*i+1, 2*j+1, 8)

        pyxel.text(1, 102, "Time: {:0>6}".format(pyxel.frame_count), 10)
        pyxel.text(60, 102, "STOP", 6 if self.is_moving else 3)
        pyxel.text(80, 102, "START", 3 if self.is_moving else 6)


App()

今回は、分かりやすさのために、100ピクセル * 100ピクセルに対して2ピクセル * 2ピクセルを1単位としてプログラムを書いています。なので、動けるのは50マス * 50マスという狭い範囲になってしまっています。
Pyxelでは256ピクセル * 256ピクセルが最大でサポートされているので、より広範囲で繁殖する物体を観察する場合はpyxel.init()のプロパティやself.is_aliveの配列の大きさを適宜変えてみましょう。

さまざまなライフゲームの物体

ここからはもうプログラミングの記事ではないです。Wikipediaの記事を見ながら面白そうなものをいくつか試してみました。

パルサー

周期3の振動子

pyxel-181209-131846.gif

ペンタデカスロン

周期15の振動子

pyxel-181209-132133.gif

スイッチ機関車

長寿型の物体

pyxel-181209-133422.gif

シャトル

特にこれは女王蜂とも言われる

pyxel-181209-134047.gif

宇宙船の衝突

軽量級宇宙船の衝突により2つのグライダーが発生

pyxel-181209-132743.gif

中量級宇宙船の衝突によりパルサーが発生

pyxel-181209-133103.gif

グライダー銃

グライダーを永遠に打ち出し続ける

pyxel-181209-134644.gif

13
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
12