ライフ・ゲーム

ライフ・ゲームに出会ったのは30年以上も前の事だ。BASIC, Pascal そして Emacs lisp と異なる言語で何度も作り直した。演算を局所化し分散させるなどの最適化もした。

自分を含めた 3x3 = 9 の領域でのみ生き死にが決まる。HDL で書いても簡単にできそうではある。

処理を簡単にするために、すでに誰かがラインバッファで３ライン分供給してくれるモジュールを書いているものとしよう。入力は 3bit + 1bit で、出力は 1 bit だ。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/99921/aedfab61-436c-3f90-b544-eeb38a023533.png)


付加情報の 1 bit はラインの終わりを示す(End Of Line)。EOL を認識すると内部の状態がリセットされる。

EOL のような実際のピクセルデータ以外のもの、OOB(Out-of-band) 的な情報の扱いは現在のところ Polyphony でうまく表現できていない。仕方がないので 4 bit で一塊となる。意味の区分けはコードの表記としてわかりやすく表現できないのが難点だ。

OOB な情報を例えば Tuple などで表せれば便利になるだろう。ただし、これは時間的概念つまり、同時性を表現することになる。本来 Python がもっていない意味論の拡張になりはしないか？Tuple はかなりそれに近いと言えるが、導入には現時点で慎重になっている。

構文シュガーも含めて、言語にどのような機能を付け加えるのかはかなり慎重にならないといけないだろう。やみくもに流行りのスタイルを取り入れたり、高位合成として便利だからと機能拡張すれば、結果として使いづらい言語＋ライブラリが出来上がるだろう。

```python:life_one.py
import polyphony
from polyphony import module, pure
from polyphony import testbench
from polyphony.io import Port
from polyphony.typing import bit, uint3, uint4, List
from polyphony.timing import clksleep, clkfence, wait_rising, wait_falling



@module
class life:
    def __init__(self):
        self.i_bit4  = Port(uint4, 'in', protocol='valid')
        self.o_bit  = Port(bit, 'out', protocol='valid')
        self.append_worker(self.life_worker, self.i_bit4, self.o_bit)

    def life_worker(self, i_bit4, o_bit):
        bit3_to_n = [ 0, 1, 1, 2, 1, 2, 2, 3 ]
        bit3_to_m = [ 0, 1, 0, 1, 1, 2, 1, 2 ] 
        n_to_o = [0, 0, 1, 1, 0, 0, 0, 0,
                  0, 0, 0, 0, 0, 0, 0, 0] 
        mat = [0] * 3 # 

        while polyphony.is_worker_running():
            v = i_bit4()
            #print("mat", mat)
            #print("v", v)
            if v == 8 : 
                mat2_old = mat[2]
                mat[0] = 0
                mat[1] = 0
                mat[2] = 0
            else:
                v0 = bit3_to_n[v]
                v1 = bit3_to_m[v]
                mat0_old = mat[0]
                mat1_old = mat[1]
                mat2_old = mat[2]
                mat[0] = 16 + v0
                mat[1] = mat0_old + v1
                mat[2] = mat1_old + v0
            #print("mat2_old:", mat2_old)
            if (mat2_old & 16) == 16 :
                out_v = n_to_o[mat2_old & 15]
                o_bit.wr(out_v)

m = life()

@testbench
def test(m):
    m.i_bit4.wr(0)
    clksleep(5)
    m.i_bit4.wr(0)
    clksleep(5)
    m.i_bit4.wr(1)
    v = m.o_bit.rd()
    clksleep(5)

    if 1 :
        m.i_bit4.wr(0)
        clksleep(5)
        print("outv:", v)

    if 0:
        m.i_bit4.wr(0)
        clksleep(5)
        v = m.o_bit.rd()
        print("outv:", v)

    if 0 :
        m.i_bit4.wr(4)
        v = m.o_bit.rd()
        print("outv:", v)

        m.i_bit4.wr(3)
        v = m.o_bit.rd()
        print("outv:", v)

        m.i_bit4.wr(0)
        v = m.o_bit.rd()
        print("outv:", v)

        m.i_bit4.wr(0)
        v = m.o_bit.rd()
        print("outv:", v)

        m.i_bit4.wr(8)
        v = m.o_bit.rd()
        print("outv:", v)

        print("-")
        clksleep(10)
        #
        m.i_bit4.wr(0)
        m.i_bit4.wr(0)
        m.i_bit4.wr(2)

        m.i_bit4.wr(1)
        v = m.o_bit.rd()
        print("outv:", v)

        m.i_bit4.wr(1)
        v = m.o_bit.rd()
        print("outv:", v)

        m.i_bit4.wr(1)
        v = m.o_bit.rd()
        print("outv:", v)

        m.i_bit4.wr(7)
        v = m.o_bit.rd()
        print("outv:", v)

        m.i_bit4.wr(0)
        v = m.o_bit.rd()
        print("outv:", v)

        m.i_bit4.wr(0)
        v = m.o_bit.rd()
        print("outv:", v)

        m.i_bit4.wr(8)
        v = m.o_bit.rd()
        print("outv:", v)

test(m)
```