From: "Eric I." Date: 2007-03-12T03:25:05+09:00 Subject: Re: SimFrost (#117) [solution] Here's my solution. I only provide text console output, which is pretty effective when you pause a little bit between "frames". Glancing over some of the other solutions, the one thing I may have done differently is pre-compute the two grid overlays. Eric ---- Are you interested in on-site Ruby training that's been highly reviewed by former students? http://LearnRuby.com ==== class SimFrost # A Cell keeps track of its contents. It is essentially a mutable # Symbol with some extra knowledge to convert into a string. class Cell attr_accessor :contents @@strings = { :space => ' ', :ice => '*', :vapor => '-' } def initialize(contents) @contents = contents end def to_s @@strings[@contents] end end # class SimFrost::Cell # A Grid overlays the space dividing it up into 2-by-2 Boxes. # Different Grids can cover the same space if the offsets are # different. class Grid # A Box is a 2-by-2 slice of the space containing 4 cells, and a # Grid contains a set of Boxes that cover the entire space. class Box def initialize @cells = [] end # Appends a cell to this box def <<(cell) @cells << cell end # Adjust the cell contents by the following rules: if any cell # contains Ice then all vapor in the Box will be transformed to # ice. Otherwise rotate the four cells clockwise or # counter-clockwise with a 50/50 chance. def tick if @cells.any? { |cell| cell.contents == :ice } @cells.each do |cell| cell.contents = :ice if cell.contents == :vapor end else if rand(2) == 0 # rotate counter-clockwise @cells[0].contents, @cells[1].contents, @cells[2].contents, @cells[3].contents = @cells[1].contents, @cells[3].contents, @cells[0].contents, @cells[2].contents else # rotate clockwise @cells[0].contents, @cells[1].contents, @cells[2].contents, @cells[3].contents = @cells[2].contents, @cells[0].contents, @cells[3].contents, @cells[1].contents end end end end # class SimFrost::Grid::Box # Creates a Grid over the space provided with the given offset. # Offset should be either 0 or 1. def initialize(space, offset) @boxes = [] rows = space.size cols = space[0].size # move across the space Box by Box (rows / 2).times do |row0| (cols / 2).times do |col0| # create a Box and add it to the list box = Box.new @boxes << box # add the four neighboring Cells to the Box (0..1).each do |row1| (0..1).each do |col1| # compute the indexes and wrap around at the far edges row_index = (2*row0 + row1 + offset) % rows col_index = (2*col0 + col1 + offset) % cols # add the indexed Cell to the Box box << space[row_index][col_index] end end end end end # Tick each box in this Grid. def tick() @boxes.each { |box| box.tick } end end # class SimFrost::Grid # Creates the space and the two alternate Grids and initializes the # time counter to 0. def initialize(rows, columns, vapor_rate) # argument checks raise ArgumentError, "rows and columns must be positive" unless rows > 0 && columns > 0 raise ArgumentError, "rows and columns must be even" unless rows % 2 == 0 && columns % 2 == 0 raise ArgumentError, "vapor rate must be from 0.0 to 1.0" unless vapor_rate >= 0.0 && vapor_rate <= 1.0 # Create the space with the proper vapor ratio. @space = Array.new(rows) do Array.new(columns) do Cell.new(rand <= vapor_rate ? :vapor : :space) end end # Put one ice crystal in the middle. @space[rows/2][columns/2].contents = :ice # Create the two Grids by using different offsets. @grids = [Grid.new(@space, 0), Grid.new(@space, 1)] @time = 0 end # Returns true if there's any vapor left in @space def contains_vapor? @space.flatten.any? { |cell| cell.contents == :vapor } end # Alternates which Grid is used during each tick and adjust the # Cells in each Box. def tick @grids[@time % 2].tick @time += 1 end def to_s @space.map do |row| row.map { |cell| cell.to_s }.join('') end.join("\n") end end # class SimFrost if __FILE__ == $0 # choose command-line arguments or default values rows = ARGV[0] && ARGV[0].to_i || 30 columns = ARGV[1] && ARGV[1].to_i || 60 vapor_rate = ARGV[2] && ARGV[2].to_f || 0.15 pause = ARGV[3] && ARGV[3].to_f || 0.025 s = SimFrost.new(rows, columns, vapor_rate) puts s.to_s while s.contains_vapor? sleep(pause) s.tick puts "=" * columns # separator puts s.to_s end end