Edited at

二次元セルオートマトン(ライフゲーム)

More than 1 year has passed since last update.

一次元セルオートマトンの続き。

詳細は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)

lifegame_terminal.gif


end

ttygif使って見たけどいまいちカクカク。

実際はもう少しなめらかです。