これを見て作りたくなったからつくった。
あれれ、130行になったぞ…… まあ、使っていないコードもあるのと、割と丁寧に書いてはいる気がするのでこんなもんで……
life.rb
require 'singleton'
FPS = 30.0 # 60.0
SIZE = 75
def mainloop
loop do
update
sleep 1.000 / FPS
end
end
def clear
puts "\e[H\e[2J"
end
def update
field = Field.instance
field.each do |cell|
count = cell.around_self.map{|x, y| field.get(x, y) }.count(&:alive?)
new_cell = case
when (2..3).cover?(count) && cell.alive?
cell.to_alive
when count == 3 && cell.dead?
cell.to_alive
else
cell.to_dead
end
field.set(new_cell)
end
field.commit
clear
puts field.to_s
end
class Field
include Enumerable
include Singleton
def initialize
@field =
get_mock_field.map.with_index do |row, y|
row.map.with_index{|_, x| Cell.gen(x, y) }
end
clear_tmp_field
end
attr_reader :field, :tmp_field
def to_s
each_row.map do |row|
row.map(&:to_s).join(" ")
end.join("\n")
end
def get(x, y)
return nil if x < 0 or x > SIZE or y < 0 or y > SIZE
field[y][x]
end
def set(cell)
tmp_field[cell.y][cell.x] = cell
end
def commit
@field = tmp_field
clear_tmp_field
end
def each
return to_enum unless block_given?
@field.flatten.each do |cell|
yield cell
end
end
def each_row
return enum_for(:each_row) unless block_given?
@field.each do |row|
yield row
end
end
private
def clear_tmp_field
@tmp_field = get_mock_field
end
def get_mock_field
([[nil] * SIZE] * SIZE).map{|r| r.clone } # change pointer
end
end
class Cell < Struct.new(:cont, :x, :y)
def self.gen(x, y)
new [true, false].sample, x, y
end
def to_s
cont ? "@" : "_"
end
alias alive? cont
def dead?; !alive? end
def to_toggled
Cell.new(!cont, x, y)
end
def to_alive
Cell.new(true, x, y)
end
def to_dead
Cell.new(false, x, y)
end
def around_self
return enum_for(:around_self) unless block_given?
x_min = [0 , x - 1].max
x_max = [SIZE - 1, x + 1].min
y_min = [0 , y - 1].max
y_max = [SIZE - 1, y + 1].min
[*x_min..x_max].product([*y_min..y_max]).each do |_x, _y|
next if x == _x and y == _y
yield _x, _y
end
end
end
mainloop