juliaでライフゲームを作る

julia

番外編でRustで作ったライフゲームをJuliaに移植してみました。

まぁ、Juliaらしい書き方ができてるかどうかは別として、まぁ、Juliaで書いたって感じです

rust版は

過去の記事をご覧ください

Juliaで作るライフゲーム

では、やっていきましょう。

まずは、列挙型と構造体を使って、セル状態とフィールド状態の構築します

@enum Cell begin
    Dead = 0
    Alive =1
end

mutable struct Field
    width
    height
    cells::Vector{Cell}
end

ここら辺はRustと一緒ですね。

関数を作っていく

次に各種関数を作っていきましょう。

新しいフィールドを作るnew()、インデックス番号を取得するget_index()、周囲のセルの状態を取得する状態の更新をするnext_gen()を作ります。

フィールド生成

function new()
    width = 10
    height =10
    cells = [
        0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,1,0,0,0,0,0,
        0,0,0,0,0,1,0,0,0,0,
        0,0,0,1,1,1,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,
    ].|>(x -> x==1 ? Alive : Dead)
    Field(width,height,cells)
end

juliaらしく?パイプライン演算子とドット演算子を使ってフィールドのデータを変換する

インデックスの取得

function get_index(field::Field,row,column)
    (row-1)*field.width+column
end

Juliaの場合は、配列のインデックスは1からスタートするので、そこら辺を考慮して行を調整してます。

自分のセルの周りのセルの状態を取得する関数

function get_alive_cells(field::Field,row,column)
    count = 0
    for y = [-1,0,1], x=[-1,0,1]
            y_idx = row+y
            x_idx = column+x
            if y_idx > 0 && x_idx>0 && y_idx<=field.height && x_idx<=field.width
                idx = get_index(field,y_idx,x_idx)
                cell = Int(field.cells[idx])
                count = count + cell
        end
    end
    if Int(field.cells[get_index(field,row,column)])==1
        count = count-1
    end
    count
end

これもJuliaらしく?

for文を一つにまとめてみましたよ。

二重ループも”for y = [-1,0,1], x=[-1,0,1 ]”こんな感じに1つにまとめられるんだよねー

状態更新

function next_gen(field::Field)
    next_cells = copy(field.cells)
    for row=1:field.height,column=1:field.width
        idx = get_index(field,row,column)
        cell = field.cells[idx]
        alive_cells = get_alive_cells(field,row,column)
        if cell == Alive && alive_cells < 2
            next_cells[idx]=Dead
        elseif cell==Alive && alive_cells ==3|2 
            next_cells[idx]=Alive
        elseif cell == Alive && alive_cells>3
            next_cells[idx]=Dead
        elseif cell == Dead && alive_cells==3
            next_cells[idx]=Alive
        end
    end
    field.cells = copy(next_cells)
end

こちらも forを一つにまとめてます。

ifがいっぱいになっちゃったねー。パターンマッチングのパッケージがあったと思うけど、ひとまず、if文で作ってみましたよ。

発展

一応ここまでで、ライフゲームの基礎ができました。

あとは、表示したりする準備をしていきます。

行列変換

これ1次元配列で作っているので、これを行列(matrix)にします。

その関数を作ります、

関数と言ってもちょっと変換するのをまとめてるぐらいです、

function get_matrix(field::Field)
    reshape(field.cells,(field.height,field.width)).|>Int|>transpose
end

ひとまず1次元配列を高さと幅で、reshapeして行列に変換します。それをIntに変換します。

そして、最後に転置。

なんで最後に転置かっていうと、reshapeで行列変換すると、縦に1,2,3,・・・って並んでるので、それを横向きに1,2,3,・・と並ぶようにしています。

まぁ、Intもtransposeも引数1つなので、パイプライン演算子を使って値を渡していきます。そして、Intへの変換は行列の配列一つ一つそれぞれに対して適応させるので、ドット演算子も付け加えてます。

Plotsのheatmapで表示してみる

最後にPlotsを使って表示してみましょう。

using Plots
game = new()
cell_matrix = get_matrix(x)
heatmap(cell_matrix)

Plotsをインポートして

ゲームの生成

そして、行列に変換して、ヒートマップで描画

こんな感じになります。

残念なことに上下は逆(笑)

まとめ

こんな感じでJuliaでもライフゲーム実装できましたね。

表示に関してはもうちょっと工夫が必要だけどね(笑)

RustのプログラムをそのままJuliaに変換したので、本当ならもっとJuliaらしい書き方ができるかもしれないですねー。

コメント

タイトルとURLをコピーしました