Help us understand the problem. What is going on with this article?

Ruby でライフゲーム

More than 5 years have passed since last update.

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

udzura
投稿しているコードは、指定が無い限り MIT とします。
http://udzura.hatenablog.jp
pepabo
「いるだけで成長できる環境」を標榜し、エンジニアが楽しく開発できるWebサービス企業を目指しています。
https://pepabo.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away