From: Jesse Merriman Date: 2007-08-12T22:13:36+09:00 Subject: [SOLUTION][QUIZ] Cellular Automata (#134) --Boundary-00=_pdwvGh1C3FtoFsb Content-Type: Multipart/Mixed; boundary="Boundary-00=_pdwvGh1C3FtoFsb" --Boundary-00=_pdwvGh1C3FtoFsb Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Content-Disposition: inline Here's my fairly straightforward, no bells-and-whistles solution. Each state is represented as an Array of Strings. I originally just used a String, and split() when needed, but keeping it as an Array makes it a bit faster and does simplify the code in a few places. transformer() builds a Proc that takes a neighborhood as its argument (as an Array of Strings) and returns the transformed cell state (as a String). A Hash could have been used instead of a Proc. step() takes a state (as an Array of Strings), tacks [0,0] onto both ends, and calls each_cons(3) to iterate through the neighborhoods, which are passed to the transformer Proc. $ ./cellular_automata.rb -r 110 -s 1 -n 15 X XX XXX XX X XXXXX XX X XXX XX XX X XXX XXXXXXX X XX XXX XXX XX X XX X XXXXX XXXXX XX X XX X XXX XX XXX XXXX X XXX XX X XX XXXXX X -- Jesse Merriman jessemerriman@warpmail.net http://www.jessemerriman.com/ --Boundary-00=_pdwvGh1C3FtoFsb Content-Type: application/x-ruby; name="cellular_automata.rb" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="cellular_automata.rb" #!/usr/bin/env ruby # Ruby Quiz 134: Cellular Automata require 'enumerator' require 'getoptlong' Draw = { :blank => ' ', '0' => ' ', '1' => 'X' } Edge = [0, 0] NeighborhoodSize = 3 # Build a Proc that takes a neighborhood as its argument and returns the # transformed cell state. def transformer rule_num rule = rule_num.to_s 2 rule = ('0' * (2**NeighborhoodSize - rule.length) + rule).reverse.split(//) lambda { |hood| rule[hood.join.to_i(2)] } end # Takes the current state and a transformation Proc, and returns the next # state. def step state, trans new_state = [] (Edge + state + Edge).each_cons(NeighborhoodSize) do |hood| new_state << trans[hood] end new_state end # Outputs the current state. The current step number and total step number are # needed to calculate how far to indent. def puts_state state, step, total_steps puts Draw[:blank] * (total_steps - step) + state.map { |x| Draw[x] }.join end if __FILE__ == $0 Opts = GetoptLong.new( [ '--rule', '-r', GetoptLong::REQUIRED_ARGUMENT ], [ '--state', '-s', GetoptLong::REQUIRED_ARGUMENT ], [ '--steps', '-n', GetoptLong::REQUIRED_ARGUMENT ] ) # defaults rule = 110 state = %w{ 1 } steps = 20 Opts.each do |opt, arg| case opt when '--rule'; rule = arg.to_i when '--state'; state = arg.split(//) when '--steps'; steps = arg.to_i end end trans = transformer(rule) puts_state state, 0, steps steps.times do |s| state = step(state, trans) puts_state state, s+1, steps end end --Boundary-00=_pdwvGh1C3FtoFsb-- --Boundary-00=_pdwvGh1C3FtoFsb--