番外編で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らしい書き方ができるかもしれないですねー。
コメント