LoginSignup
32
28

More than 5 years have passed since last update.

Ruby でライフゲーム

Posted at

これを見て作りたくなったからつくった。
あれれ、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

スクリーンショット 2013-09-14 18.11.30.png

32
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
28