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 <chubas7 / gmail.com>
# 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/.