39ギター

35年ぶりに弾き始めたクラシックギター
神経痛と戦いながら
どこまでバッハに迫れるか
蝶も花もアーチェリーもあるよ

ドットマトリクスLEDでラズパイでライフゲーム

2019-08-15 20:42:57 | 日記

こんばんは

 

電子工作

いよいよライフゲームのプログラム部分の製作です

ライフゲームの初期パターンには色々ありますが、とりあえず「rペントミノ」を中央に置いてそこから始めます

パターンを中央に置くのは昨日の時点でできているので、今度はそれを1世代進めることにします

そのためにはパターンを表示するためのviewDatag配列と次の世代を作るshiftData配列を作ります

これらは初期値としてゼロが入っているんですがそれぞれの配列の大きさをどれぐらいにするのかで処理能力との関係でライフの成長速度が決まってきます

とりあえず100x100で作って、最終的に表示するのはviewData配列の中央部分の16x16のエリアとします

viewData配列の中央にパターンを置いて次の世代をライフの生成・消滅規則に従って作りshiftData配列に格納します

shisftData配列が完成したらそれをviewData配列にそっくり写し、もうできている表示ルーチンに渡します

ライフの生成・消滅規則はある位置の周囲に何個のライフがあるのかを数えることから始まります

そこでviewData配列からviewData[i-1:i+2,j-1:j+2]で3x3の配列を切り出してその合計をとります

例えば切り出した配列が

[[0 0 0]

 [0 1 0]

 [1 1 1]]

の時はsumをとると4になります

中央の値は周りのライフではないので引いて3になります

ということで

dots = np.sum(viewData[i-1:i+2,j-1:j+2]) - viewData[i][j]

がある位置の周囲のライフの数ということになります

⑴ライフがないところの周囲に3個のライフがあるとその位置には次の世代でライフが発生します

⑵ライフがあるところの周囲に3個のライフがあるとその位置のライフは生き延びます

⑶ライフがあるところの周囲に2個のライフがあるとその位置のライフは生き延びます

⑷それ以外はその位置にライフの発生することはなく、またその位置のライフは消えてしまいます

以上の4条件から次の世代でその位置にライフが存在する条件は

if dots == 3 or (dots == 2 and viewData[i][j] == 1):

となります

それをshitData配列の伊那路位置に格納し、100x100の位置全部について計算し終わったらshiftData配列の値をそっくり全部viewData配列コピーします

こんな感じで出来上がったプログラムがこれ

# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
import numpy as np
import copy
from threading import (Event,Thread,Timer)
from time import sleep

def showData(colData, rowData, ser, rclk, srclk):
    GPIO.output(rclk, GPIO.LOW)
    GPIO.output(srclk, GPIO.LOW)    
    outColData = colData        
    for i in range(COLS):
        GPIO.output(ser, outColData & 0x01 )
        GPIO.output(srclk, GPIO.HIGH)
        GPIO.output(srclk, GPIO.LOW)
        outColData >>=1      
    outRowData = rowData
    for i in range(8,16):
        GPIO.output(ser, int(outRowData[i]))
        GPIO.output(srclk, GPIO.HIGH)
        GPIO.output(srclk, GPIO.LOW)
    for i in range(0,8):
        GPIO.output(ser, int(outRowData[i]))
        GPIO.output(srclk, GPIO.HIGH)
        GPIO.output(srclk, GPIO.LOW)
    GPIO.output(rclk, GPIO.HIGH)
    GPIO.output(rclk, GPIO.LOW)

def initLife():
    global viewData
    global shiftData
    zero=np.int8(0)
    viewData = np.array([[zero for i in range(SPACE_WIDTH + 1)] for j in range(SPACE_WIDTH + 1)],dtype=np.int8)
    shiftData = np.array([[zero for i in range(SPACE_WIDTH + 1)] for j in range(SPACE_WIDTH + 1)],dtype=np.int8)
    viewData[VIEW_BOUNDS + 6][VIEW_BOUNDS + 8]=1
    viewData[VIEW_BOUNDS + 7][VIEW_BOUNDS + 6]=1
    viewData[VIEW_BOUNDS + 7][VIEW_BOUNDS + 7]=1
    viewData[VIEW_BOUNDS + 7][VIEW_BOUNDS + 8]=1
    viewData[VIEW_BOUNDS + 8][VIEW_BOUNDS + 7]=1

def shiftLife():
    global viewData
    global shiftData
    for i in range(1,SPACE_WIDTH + 1):
        for j in range(1,SPACE_WIDTH + 1):
            ret=np.int8(0)
            dots = np.sum(viewData[i-1:i+2,j-1:j+2]) - viewData[i][j]
            if dots == 3 or (dots == 2 and viewData[i][j] == 1):
                ret = np.int8(1)
            shiftData[i][j] = ret
    viewData = copy.copy(shiftData)

def timerLoad():
    global work
    work = not work
    timer.cancel()

#メイン
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
#シフトレジスタPIN設定
SPIRST = 26
SER = 25
RCLK = 24
SRCLK = 23
GPIO.setup(SPIRST, GPIO.OUT)
GPIO.setup(SER, GPIO.OUT)
GPIO.setup(RCLK, GPIO.OUT)
GPIO.setup(SRCLK, GPIO.OUT)
#
LED_PIECES = 2
COLS = LED_PIECES * 8
SPACE_WIDTH = 50
VIEW_BOUNDS = int(SPACE_WIDTH / 2) - 8
#シフトレジスタのリセット
GPIO.output(SPIRST,GPIO.LOW)
GPIO.output(SPIRST,GPIO.HIGH)
#LEDTimerの設定
interval = 0.1
timer = Timer(interval,timerLoad)
work = True
#
viewData = ""
shiftData = ""
outData = ""
initLife()
try:
    while True:
        outData = viewData[VIEW_BOUNDS: VIEW_BOUNDS + COLS , VIEW_BOUNDS: VIEW_BOUNDS + COLS]
        del timer
        timer = Timer(interval,timerLoad)
        work = True
        timer.start()
        while work:        
            colData=2**(COLS - 1)
            for j in range(COLS):          
                showData(colData, outData[j], SER, RCLK, SRCLK)
                sleep(0.0001)
                colData >>= 1
                showData(0x00, [GPIO.LOW for i in range(COLS)], SER, RCLK, SRCLK)             
        shiftLife()
except KeyboardInterrupt:
    pass
showData(0x00, [GPIO.LOW for i in range(COLS)], SER, RCLK, SRCLK)
GPIO.output(SPIRST,GPIO.LOW)
GPIO.cleanup()

ちょっと長いですけど興味のある人は読んでみてください

 

動かしてみると

LEDの上でどんどんライフが生成・消滅を繰り返して、あたかも顕微鏡の中で乳酸菌が増えていくような感じになります

ある場面でこんなパターンになりました

花火がピカッ

ドーン

パラパラ

連続する3世代です

面白いですね

えっ?・・・なにが?・・・

といった感じもありますね

 

ところが問題が一つあり、やはり100x100ではラズパイのCPUの処理速度の限界でどうしても世代間の移り変わりでドットの点滅があり滑らかに表示できません

世代間の時間間隔はTimerのインターバルを設定しているので調整できるのですが、計算そのものに以外と時間がかかっています

仕方がないので50x50ぐらいにするとなんとか耐えられる早さになりました

仮想空間が狭いと世代が進むにつれて一番端で消えてしまうライフの影響が出てくるので、できれが仮想空間は広ければ広いほどいいのです

 

本当は1000x1000ぐらいの仮想空間でLEDも4x4の32x32ドットぐらいでやりたかったのですが、ちょっと無理のようですね

まだこれで終わるのももったいないので、今はせめて横方向だけでも拡張できないかなと考えています

LEDを買い足して横4x縦2の32x16ドットで仮想空間は100x50ぐらい

これぐらいならまだなんとかなるような気がします

これぐらいの仕様変更はプログラム中の定数を変えるだけで対応できると思うので、あとはまた中国からLEDを取り寄せるだけです

米中貿易戦争が激しくなる前になんとかしないと手に入らなくなるかも・・・

 

台風10号は広島の上空を通過しましたが、雨も風も大したことなく普通の梅雨の日の雨降り程度でした

暴風圏が台風の中心からずれているからかもしれませんね


最新の画像もっと見る

コメントを投稿