一次元セルオートマトンの続き。
詳細はwikiへ。
二次元セルオートマトンの中でもこのルールのことをライフゲームと呼ぶようです。
Ruby
init
Array.new
にブロックを渡すと良しなに初期化してくれます。
class Lifegame
attr_accessor :cells
def initialize(y = 0,x = 0)
@cells = Array.new(y) { Array.new(x,0) }
end
rule
前回と違いパターンないしルールは固定。
自分の生死と周囲の生きている数を元に明日の生死が決まります。
孤独だと死。騒がしくても死。丁度良いと生き返ったりします。
def rule(*cs)
case cs
when [1, 2] then 1
when [1, 3] then 1
when [0, 3] then 1
else 0
end
end
apply
配列の外を参照しない様に確認しながら、
周囲の生きている数を数え上げ、ルールに適応します。
前回教えていただいたproductが使えスッキリしてます。
def apply
sy = @cells.size
sx = @cells[0].size
as = [-1,0,1]
@cells = @cells.map.with_index { |cy,iy|
cy.map.with_index { |c,ix|
rule(c,
as.product(as).map { |ay,ax|
if ay == 0 && ax == 0
0
else
y = iy + ay
x = ix + ax
if y >= 0 && y < sy && x >= 0 && x < sx
@cells[y][x]
else
0
end
end
}.reduce(:+)
)
}
}
end
random
セルを手で打つのはだるいのでランダムに任せます。
一応ブロックで調整できます。
コクのある乱数など試して見ましょう。
def random
@cells = @cells.map { |y|
y.map { |x|
if block_given?
yield x
else
rand(2)
end
}
}
end
show, run
clearコマンドでターミナルの画面でアニメーション(遅い)
その場合はsleepで調整。
def show
@cells.map { |y| y.map { |x| x == 0 ? " " : "O" }.join + "\n" }.join + "\n"
end
def run n = 200,m = 0.1
g = 0
for nn in 1 .. n
system "clear"
puts "Gen: #{g}\n#{show}"
g += 1
apply
sleep m
end
end
end
main
サイズが大きすぎると遅いので調整しましょう。
私のターミナルは縦25*横100しかないのでこれくらいで十分です。
めっちゃフォントを大きくしてます。
require "io/console"
y,x = IO.console.winsize
cs = Lifegame.new(y-3,x)
cs.random { (rand(2) + rand(2) + rand(2)) / 3 }
cs.run(50)
end
ttygif使って見たけどいまいちカクカク。
実際はもう少しなめらかです。