From: Ruben Medellin Date: 2007-03-12T15:22:11+09:00 Subject: Re: SimFrost (#117) James Gray wrote: > The three rules of Ruby Quiz: Woah, this quiz was very entertaining. I enjoyed a lot doing it, and still enjoy watching it every time :D As the console version wouldn't let me be happy, I tried to do it using OpenGL. I got it at the end, although it's pretty slow (runs at decent speed for size of <200*200, obviously the greater values the slowest), but I'm happy with it for being my first try using GL. I'll probably spend another little time tuning it up (as I stated again that premature optimization is evil), and perhaps designing the 3D view someone suggested :O. Thanks again for the great quizes! ____________ # Quiz 117 : SimFrost # Ruben Medellin # Usage> ruby quiz117.rb width height vapor_density #Based on OpenGL require 'opengl' require 'glut' # Each pixel represents an element. class Element attr_accessor :element def initialize(element) @element = element end #Just a change of state. Don't forget to decrement the vapor number. def freeze if @element == :vapor $VAPOR -= 1 @element = :ice end end end # Main class class Freeze_Simulator #Standard initializer. It prepares the windows to be called. def initialize $WIDTH = ARGV[0] && ARGV[0].to_i || 100 $HEIGHT = ARGV[1] && ARGV[1].to_i || 100 $WIDTH += 1 if $WIDTH % 2 != 0 $HEIGHT += 1 if $HEIGHT % 2 != 0 $DENSITY = ARGV[2] && ARGV[2].to_i || 30 $VAPOR = 0 # We create a matrix, assigning randomly (according to the # percentage) the density of vapor. Vacuum is represented by nil. $GRID = Array.new($HEIGHT) do Array.new($WIDTH) do if rand(100) > $DENSITY nil else # We need this counter if we want a nice quick # checker for all_frozen? method $VAPOR += 1 Element.new(:vapor) end end end #We set the center to be frozen ($GRID[$HEIGHT/2][$WIDTH/2] = Element.new(:vapor)).freeze $TICK_COUNT = 0 $PIXELS = [] #Standard GL methods GLUT.Init GLUT.InitDisplayMode(GLUT::SINGLE) GLUT.InitWindowSize($WIDTH, $HEIGHT) GLUT.InitWindowPosition(100, 100) GLUT.CreateWindow('SimFrost : Quiz #117 - by CHubas') GL.ShadeModel(GL::FLAT) make_matrix GLUT.DisplayFunc(method(:display).to_proc) GLUT.KeyboardFunc(Proc.new{|k, x, y| exit if k == 27}) GLUT.ReshapeFunc(method(:reshape).to_proc) # IdleFunc takes a proc object and calls it continously whenever it can. GLUT.IdleFunc(method(:tick).to_proc) end # Here we create the pixel information. # Open GL takes an array of integers and splits it in groups of three # that represent one color component each. def make_matrix for i in 0..$HEIGHT-1 for j in 0..$WIDTH-1 index = (i * $WIDTH + j) * 3 if particle = $GRID[i][j] case particle.element when :vapor # A blue-ish color $PIXELS[index] = 50 $PIXELS[index+1] = 100 $PIXELS[index+2] = 255 when :ice # White ice $PIXELS[index] = 255 $PIXELS[index+1] = 255 $PIXELS[index+2] = 255 end else # Black $PIXELS[index] = 0 $PIXELS[index+1] = 0 $PIXELS[index+2] = 0 end end end end # Some basic window behavior def reshape(width, height) GL.PixelZoom(width / $WIDTH.to_f, height / $HEIGHT.to_f) display end # Draws the pixel bitmap def display GL.DrawPixels($WIDTH, $HEIGHT, GL::RGB, GL::UNSIGNED_BYTE, $PIXELS.pack("C*")) GL.Flush end # We split the board into 2*2 squares with this method def each_square( tick_number ) start = 0 w_end = $WIDTH h_end = $HEIGHT if tick_number % 2 != 0 #odd start -= 1 w_end -= 1 h_end -= 1 end (start...h_end).step(2) do |row| (start...w_end).step(2) do |column| s = yield *get_square_at(row, column) set_square_at(row, column, s) end end end # Checks for each 2*2 square and does the proper transformation def tick each_square( ($TICK_COUNT += 1) ) do |*square| if square.any?{|e| e != nil && e.element == :ice} for e in square e.freeze end square else rotate(square) end end # Having modified the matrix, now we have to rebuild the pixel map make_matrix GLUT.PostRedisplay GLUT.SwapBuffers #Stop doing this if everything is frozen already GLUT.IdleFunc(nil) if all_frozen? end # Some dirty methods def get_square_at(row, column) [$GRID[row][column],$GRID[row][column+1],$GRID[row+1][column],$GRID[row+1][column+1]] end def set_square_at(row, column, new_square) $GRID[row][column],$GRID[row][column+1],$GRID[row+1][column],$GRID[row+1][column+1] = new_square end # Rotates elements in # | 0 1 | # | 2 3 | def rotate(square) if rand(2) == 0 square.values_at(1,3,0,2) else square.values_at(2,0,3,1) end end # Validates if there is any vapor particle def all_frozen? if $VAPOR > 0 return false else puts "Welcome to the ice age!" puts "All frozen in #{$TICK_COUNT} thicks" return true end end # Starts the main loop def start GLUT.MainLoop end end #Let the fun begin Freeze_Simulator.new.start ________ Now I wonder how to make a movie :O -- Posted via http://www.ruby-forum.com/.