From: Daniel Finnie Date: 2007-03-12T08:06:33+09:00 Subject: Re: [QUIZ] SimFrost (#117) A little trick you can use is setting a class method for class Torus that will instantiate a new Torus object: class Torus def self.[](*args) self.new(*args) end end And then you can just do Torus[arguments here] Dan Robert Dober wrote: > It doesn't get any faster > at least I got it shorter > > 649/150 > cat torus.rb > # vim: sw=2 sts=2 nu tw=0 expandtab nowrap: > # > # > > ICE = Class.new > VAPOR = Class.new > VACUUM = Class.new > > > ############################################################### > # > # a small reference to Python ;) > # > ############################################################### > def Torus( rows, cols, vapors, start = nil ) > Torus_.new( rows.to_i, cols.to_i, vapors.to_f, start ) > end > > class Torus_ > > ############################################################### > # > # Torus_ > # > ############################################################### > attr_reader :lines, :columns > attr_reader :generation > attr_accessor :formatter, :name > def initialize rows, cols, vapors, start > @lines = rows > @columns = cols > @vapors = vapors > @generation = 0 > if start then > @start = start.split("@").map{|e| e.to_i} > else > @start ||= [ rows/2, cols /2 ] > end > @nhoods = [] # we will store neighborhoods identified by > # their upper left corner index, odd is for even generations > # and even is for odd generations, which might seem odd. > reset_values > set_vapors > end > > def [] line, col=nil > return @values[line] unless col > @values[line][col] > end # def [](line, col=nil) > def []= line, col, val > @values[line][col] = val > end > > def each_line > @values.each{ |line| yield line } > end > > def each_nbh > r = c = @generation % 2 > loop do > yield @nhoods[ r * @lines + c ] ||= > Neighborhood.new( self, r, r.succ % @lines, c, c.succ % @columns ) > c += 2 > r += 2 unless c < @columns > return unless r < @lines > c %= @columns > r %= @lines > end > end > > def start_sim > until no_more_vapor? do > tick > write > end > end # def start_sim > > def tick > puts "Simulation #{@name} generation #{@generation}:" > @generation += 1 > each_nbh do > | nbh | > nbh.recalc > end > end > > private > > def no_more_vapor? > ! @values.any?{ |line| > line.any?{ |v| v == VAPOR } > } > end > > def reset_values > @values = Array.new(@lines){ > Array.new(@columns){ > VACUUM > } > } > end > def set_vapors > total = @lines * @columns > v = ( @vapors * (total-1) ).to_i > x = [*0..total-2] > at = [] > v.times do > at << x.delete_at( rand(x.size) ) > end > at.each do > | index | > @values[index/@lines][index%@lines] = VAPOR > end > @values[@lines-1][@columns-1] = @values[@start.first][@start.last] > @values[@start.first][@start.last] = ICE > end # def set_vapors > > def write > @formatter.to_file self, "output/#{@name}.%08d" % @generation > end # def write > > end # class Torus_ > > ############################################################### > # > # Neighborhood is implementing a 2x2 window to any object > # that responds to #[]n,m and #[]=n,m,value > # It implements the operation of rotation. > # > ############################################################### > class Neighborhood > include Enumerable > > # Neighborhood gives us the following indexed view to the underlying > # torus > # +---+---+ +-----------+-----------+ > # | 0 | 1 | | @top,@lft | @top,@rgt | > # +---+---+ +-----------+-----------+ > # | 3 | 2 | | @bot,@lft | @bot,@rgt | > # +---+---+ +-----------+-----------+ > # > > def initialize *args > @torus, @top, @bottom, @left, @right = *args > @names = [ [@top, @left], [@top, @right], [@bottom, @right], > [@bottom, @left] ] > end > > def [] n > @torus[ *@names[n%4] ] > end > def []= n, val > @torus[ *@names[n%4] ] = val > end > > def each > 4.times do > | idx | > yield self[idx] > end > end > > def recalc > if any?{|v| v == ICE} then > 4.times do > | idx | > self[ idx ] = ICE if self[ idx ] == VAPOR > end > else > rotate( rand(2) ) > end > end > > def rotate dir > x = self[0] > 3.times do > | n | > self[ n + 2*dir*n ] = self[ n + 1 + dir*2*n.succ ] > end # 3.times do > self[ 3 + 2 * dir ] = x > end # def rotate dir > > end # class Neighborhood > 650/151 > cat ppm.rb > # vim: sw=2 sts=2 nu tw=0 expandtab nowrap: > > > class Formatter > > @@default = { ICE => "255/255/255", > VAPOR => "255/0/255", > VACUUM => "0/0/0" > } > > def initialize colors={} > @colors = {} > colors.each do > | element, color | > color ||= @@default[element] > @colors[ element ] = " " << color.gsub("/", " ") << " " > end # colors.each do > end # def initialize colors={} > > def to_file( source, file, comment = nil ) > comment ||= file > File.open( "#{file}.ppm", "w" ) do > | f | > f.puts "P3 #{source.columns} #{source.lines} 255" > f.puts "#" > f.puts "# #{comment}" > f.puts "#" > source.each_line{ > |line| > count = 0 > line.each do > | cell | > s = @colors[cell] > if count + s.size > 70 then > f.puts > count = 0 > end > count += s.size > f.print s > end > f.puts unless count.zero? > } > end > end > > end > 651/152 > cat run.rb > # vim: sw=2 sts=2 nu tw=0 expandtab: > # > require 'fileutils' > require 'torus' > > def usage msg = nil > $stderr.puts msg if msg > $stderr.puts <<-EOS > usage: > #{$0} [options] height width vapor_probability > > options and their defaults > -s|--start /2@/2 where to put the initial freezer > please use Smalltalk syntax here > -n|--name run-- name of the output file > -v|--vapor 255/0/255 rgb value for PPM > O use strings for ASCII > -0|--vacuum 0/0/0 idem > > -i|--ice 255/255/255 idem > * > -f|--format ppm ppm or ascii are supported > write your own plugins ;) > > have fun > EOS > exit -1 > end > > @start = @name = nil > @vapor = nil > @vacuum = nil > @ice = nil > @format = "ppm" > options = { /^-f|^--format/ => :format, > /^-s|^--start/ => :start, > /^-n|^--name/ => :name, > /^-v|^--vapor/ => :vapor, > /^-0|^--vacuum/ => :vacuum, > /^-i|^--ice/ => :ice } > loop do > break if ARGV.empty? > break if ARGV.first == "--" > break unless /^-/ === ARGV.first > illegal_option = true > options.each do > | opt_reg, opt_sym | > if opt_reg === ARGV.first then > usage "Missing argument for option #{ARGV}" if ARGV.length < 2 > instance_variable_set( "@#{opt_sym}", ARGV[1] ) > ARGV.slice!( 0, 2 ) > illegal_option = false > end > end > usage ARGV.first if illegal_option > end > usage ARGV.join(", ") unless ARGV.size == 3 > > require @format rescue usage > > begin > mkdir( "output" ) unless File.directory?( "output" ) > rescue > $stderr.puts 'Cannot create output directory "output"' > usage > end > > t = Torus( *(ARGV << @start) ) > t.name = @name || "run-#{ARGV[0..1].join("-")}" > t.formatter = Formatter.new( ICE => @ice, VAPOR => @vapor, VACUUM => > @vacuum ) > t.start_sim