Ruby Quiz wrote:
> This week's Ruby Quiz is to write a program that accepts up to three parameters:
> the rule as an integer in decimal, the number of steps to simulate, and the
> starting state of the cells as a String of ones and zeros.

Slightly different/renamed options and no graphics:

  ruby cellular_automaton.rb -r 41 -s 20 -w 30 100100
                          X  X
  XXXXXXXXXXXXXXXXXXXXXXX      X
  X                       XXXX
    XXXXXXXXXXXXXXXXXXXXX X    X
  X X                    X  XX
   X  XXXXXXXXXXXXXXXXXX    X  X
      X                  XX
  XXX   XXXXXXXXXXXXXXXX X  XXXX
  X   X X               X   X
    X  X  XXXXXXXXXXXXX   X   XX
  X       X             X   X X
    XXXXX   XXXXXXXXXXX   X  X
  X X     X X           X      X
   X  XXX  X  XXXXXXXXX   XXXX
      X       X         X X    X
  XXX   XXXXX   XXXXXXX  X  XX
  X   X X     X X           X  X
    X  X  XXX  X  XXXXXXXXX
  X       X       X         XXXX
    XXXXX   XXXXX   XXXXXXX X
  X X     X X     X X      X  XX

== Code

#!/usr/bin/env ruby
# == Usage
#
# cellular_automaton [OPTIONS] CELLS
#
# -h, --help:
#   show help
#
# -r, --rule RULE:
#   specified the rule to use as a decimal integer, defaults to 30
#
# -s, --steps STEPS:
#   specifies the number of steps that should be shown, defaults to 20
#
# -w, --width WIDTH:
#   specifies the number of cells that should be shown per step,
#   defaults to 20
#
# CELLS: The initial cell state that should be used. Must be given as a
#   string of 0 and 1.

require 'getoptlong'
require 'rdoc/usage'
require 'enumerator'

# Describes a state in a cellular automaton.
class CellularAutomatonState
  attr :cells

  private

  # All the possible neighbourhoods of size 3.
  NEIGHBOURHOODS = [[true, true, true], [true, true, false],
    [true, false, true], [true, false, false], [false, true, true],
    [false, true, false], [false, false, true], [false, false, false]]

  public

  # Creates a new state using the specified +rule+ given in decimal.
  # +inital_state+ holds an array of booleans describing the initial
  # state.
  def initialize(rule, initial_state)
    @cells = initial_state

    # Decode the rule into a hash map. The map is then used when
    # computing the next state.
    booleans = rule.to_s(2).rjust(
      NEIGHBOURHOODS.size, '0').split(//).map{ |x| x == '1' }
    if booleans.size > NEIGHBOURHOODS.size
      raise ArgumentError, 'The rule is too large.'
    end
    @rules = {}
    NEIGHBOURHOODS.each_with_index do |neighbourhood, i|
      @rules[neighbourhood] = booleans[i]
    end
  end

  # Updates the automaton one step.
  def step!
    @new_cells = []
    # Regard the endings as false.
    ([false] + @cells + [false]).each_cons(3) do |neighbourhood|
      @new_cells << @rules[neighbourhood]
    end
    @cells = @new_cells
  end

  def to_s
    @cells.map{ |x| x ? 'X' : ' ' }.join
  end
end

# Defaults
rule = 30
steps = 20
width = 20

# Options
opts = GetoptLong.new(
  ['--help', '-h', GetoptLong::NO_ARGUMENT],
  ['--rule', '-r', GetoptLong::REQUIRED_ARGUMENT],
  ['--steps', '-s', GetoptLong::REQUIRED_ARGUMENT],
  ['--width', '-w', GetoptLong::REQUIRED_ARGUMENT])
opts.each do |opt, arg|
  case opt
    when '--help': RDoc::usage
    when '--rule': rule = arg.to_i
    when '--steps': steps = arg.to_i
    when '--width': width = arg.to_i
  end
end

if ARGV.size != 1
  abort "Incorrect usage, see --help"
end

# Turn the provided state into an array of booleans, pad if needed.
cells = ARGV.shift.rjust(width,'0').split(//).map!{ |cell| cell == '1' }

# Create the initial state and then step the desired number of times.
state = CellularAutomatonState.new(rule, cells)
puts state.to_s
steps.times do
  state.step!
  puts state.to_s
end

__END__

-- 
Andreas Launila