Wikipediaに載っているライフゲーム亜種ぜんぶ試す
はじめに
一般的に知られているライフゲームは B3/S23 と定義されている。これは周囲の生存点が 3 つで誕生し、2 つか 3 つのときに生存するというルールを簡潔に表記したものになる。
その表記を用いたいろいろなパターンが Wikipedia に載っているのだが、具体的なパターンについては文章で説明が書いてあるだけで視覚的にどのようなパターンになるのかよくわからない。そこで簡単に確認できるようにした。
説明文は翻訳したのをのっけているが意味がよくわからないのもある。
また説明内にあるパターンの特徴が見られないものは、おそらくそのパターンを意図した初期配置から始めないといけないのでないかと考えられる。(以下のデモはすべてランダムな配置から開始している)
Replicator (B1357/S1357)
すべてのパターンは最終的にそのパターン自体の複数のコピーに置き換えられる。
Seeds (B2/S)
すべてのパターンはフェニックスで、すべての生きたセルは直ちに死に、多くのパターンが爆発的なカオスの成長を引き起こす。しかし、複雑な振る舞いを持つ一部の設計されたパターンが知られている。
名称不明 (B25/S4)
このルールは小さな自己複製パターンをサポートしている。これと小さなグライダーパターンを組み合わせるとグライダーが疑似ランダムウォークで行ったり来たりする。
Life without Death (B3/S012345678)
インクスポットやフレークなどとも呼ばれる。生きているセルはけっして死なない。カオス的な成長と任意のブーリアン回路をシミュレートするために使用できるより構造化された階段状のパターンを組み合わせる。
Life (B3/S23)
これが本家。非常に複雑な振る舞いを持つ。
34 Life (B34/S34)
最初は Life の安定した代替品と考えられていたが、コンピュータシミュレーションによって、大きなパターンは爆発的に成長することが明らかになった。多くの小さなオシレーターと宇宙船を持つ。
Diamoeba (B35678/S5678)
大きなダイヤモンドを形成し、その境界線はカオス的に揺れ動く。最初に Dean Hickerson によって研究され、1993年に彼は生きているセルで空間を埋め尽くすパターンを見つけるために $50 の賞金を提供し、この賞金は1999年に David Bell によって獲得された。
2x2 (B36/S125)
パターンが 2x2 のブロックで構成されていれば、それは同じ形で進化を続ける。これらのブロックを 2 の大きなパワーにグループ化すると、同じ振る舞いが見られるが、進化が遅くなる。高周期の複雑なオシレーターと小さなグライダーを持つ。
HighLife (B36/S23)
Life と似ているが、小さな自己複製パターンがある。
Day & Night (B3678/S34678)
オンオフの反転に対して対称になる。非常に複雑な振る舞いを示す設計されたパターンを持つ。
Morley (B368/S245)
Stephen Morley の名にちなんで名付けられた。非常に高い周期と遅い宇宙船をサポートしている。
Anneal (B4678/S35678)
ねじれた多数決ルールとも呼ばれる。オンオフの反転に対して対称になる。生きたセルと死んだセルの境界線上で曲線短縮フローを近似する。
コード
開く
require "#{__dir__}/../../物理/ベクトル/base"
Base::Palette[:background] = nil
module CA
class Cell
NEIGHBORS = [[-1, -1], [0, -1], [1, -1], [-1, 0], [1, 0], [-1, 1], [0, 1], [1, 1]]
class << self
def value_to_write
true
end
end
attr_accessor :state
def initialize(v)
@v = v
@state = state_default(v)
end
def new_state_update
c = neighbors.count(&:state)
@new_state = @state ? (c == 2 || c == 3) : (c == 3)
end
def state_update
@state = @new_state
end
def draw
Gosu.draw_rect(@v.x, @v.y, 1, 1, color)
end
private
def color
Gosu::Color.from_hsv(0, 0, brightness)
end
def brightness
if @state.kind_of?(Numeric)
@state
else
@state ? 1.0 : 0.0
end
end
def neighbors
@neighbors ||= NEIGHBORS.collect { |e|
pos = @v + V[*e]
if toroidal
pos = pos.modulo($app.world_wh)
else
if pos.min_element.negative?
pos = nil
end
end
if pos
$app.field.dig(*pos)
end
}.compact
end
def state_default(v)
[true, false].sample
end
def toroidal
true
end
end
class App < ::Base
attr_accessor :field
def show
reset
super
end
def world_wh
@world_wh ||= window_wh / cell_wh
end
private
def reset
@field = Array.new(world_wh.x) do |x|
Array.new(world_wh.y) do |y|
cell_class.new(V[x, y])
end
end
end
def button_down(id)
super
if id == Gosu::KB_R
reset
end
end
def update
super
mouse_click_then_write
@field.each { |e| e.each(&:new_state_update) }
@field.each { |e| e.each(&:state_update) }
end
def mouse_click_then_write
if button_down?(Gosu::MS_LEFT)
v = (mouse_v / cell_wh).floor
@field.dig(*v)&.state = cell_class.value_to_write
end
end
def draw
super
Gosu.scale(*cell_wh) do
@field.each { |e| e.each(&:draw) }
end
if ENV["FPS"]
vputs Gosu.fps
end
end
def cell_class
Cell
end
def cell_wh
@cell_wh ||= V.splat(cell_px)
end
def cell_px
10
end
def window_size_default
V[800, 600]
end
def fps_default
30
end
end
end
if $0 == __FILE__
CA::App.show
end
require "#{__dir__}/../セルオートマトン関連まとめ/ca"
module LifeLike
def self.show(rule)
App.instance.tap { |e|
e.rule = rule
}.show
end
class Cell < CA::Cell
def new_state_update
c = neighbors.count(&:state)
if !@state
@new_state = $app.backet_b[c]
else
@new_state = $app.backet_s[c]
end
end
end
class App < CA::App
attr_accessor :backet_b
attr_accessor :backet_s
def rule=(str)
b, s = str.match(%r{B(\d*)/S(\d*)}).captures
@backet_b = backet_build(b)
@backet_s = backet_build(s)
end
def cell_class
Cell
end
private
def backet_build(s)
if s.empty?
return []
end
a = s.chars.collect(&:to_i)
backet = Array.new(a.max.next, false)
a.each { |e| backet[e] = true }
backet
end
end
end
if $0 == __FILE__
LifeLike.show("B3/S234")
end
参照
Discussion