SimFrost (#117)

On 3/11/07, Christoffer Lernö [email protected] wrote:

It’s interesting that it looks like everyone populated their grid
no Sir not me :wink: but my code is longer and I wonder if it was worth
it, now that I have looked at the outcomes of 55,60,65,70,75 and 80%
vapor
using a randomizer for each position in the grid.

This is is obviously fast, but for small grids (and low percentages),
the percentage of actual generated vapour particles may be off by
quite a bit.

For a 10x10 grid and 10% vapour, the amount of particles typically
range between 5 and 15, which in turn makes the finished frost look
very different from run to run.

I was thinking of ways to solve this. Obviously trying to randomly
put vapour particles into an array - and retry if it already
cointains vapur - is not ideal…
Almost interminable, I was using it too, look at Torus_#set_vapors in
my solution
(Array.new(vacuum, ’ ') + Array.new(vapour, ‘.’)).sort_by

Robert

Le 11 mars 2007 à 21:07, Christoffer Lernö a écrit :

Anyone else with a more elegant solution to the problem?

I’d use this (only moderately tested) :

#! /usr/local/bin/ruby

p = 0.3
w = 100
h = 100
t = h * w
v = (t * p).floor

g = Array.new(v, ‘.’) + Array.new(t - v, ‘.’)

0.upto(t - 1) do |i|
j = i + rand(t - i)
z = g[j] ; g[j] = g[i] ; g[i] = z

g[j], g[i] = g[i], g[j]

end

(And then split the grid in w chunks.)

The fun part is that the version using a parallel assignment (commented)
is actually much slower (about two times) than the version using the
temporary variable, to the point that it was beaten by sort_by (which is
supposed to be O(n*log(n)) versus O(n) for my versions).

200*200 :
user system total real
scramble / temp : 15.281250 0.000000 15.281250 ( 15.587794)
scramble / swap : 31.398438 0.007812 31.406250 ( 32.220921)
sort_by rand : 28.625000 0.101562 28.726562 ( 29.168337)

500*500 :
user system total real
scramble / temp : 97.195312 0.000000 97.195312 ( 98.037395)
scramble / swap : 297.937500 0.929688 298.867188 (302.746914)
sort_by rand : 213.906250 0.468750 214.375000 (217.172667)

Fred

On Mar 11, 2007, at 3:07 PM, Christoffer Lernö wrote:

It’s interesting that it looks like everyone populated their grid
using a randomizer for each position in the grid.

This is is obviously fast, but for small grids (and low
percentages), the percentage of actual generated vapour particles
may be off by quite a bit.

What’s a “small grid” and what’s “quite a bit”? :wink:

#!/usr/bin/env ruby -w

TRIALS = 10_000
SIZE = 80*22

sum = 0

high = low = nil

TRIALS.times do
grid = Array.new(SIZE) { rand < 0.3 ? “.” : " " }
percent = (grid.grep(/./).size / SIZE.to_f * 100).round

puts “Actual percentage: #{percent}” if $DEBUG

sum += percent
low = percent if low.nil? or percent < low
high = percent if high.nil? or percent > high
end

puts <<END_OVERALL
Average: #{sum / TRIALS}
High: #{high}
Low: #{low}
END_OVERALL

>> Average: 29

>> High: 35

>> Low: 26

James Edward G. II

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.
self.new(*args)
end
end

And then you can just do Torus[arguments here]

Dan

I’m gonna have to agree here that it matters very little whether the
proper ratio is enforced on the scale in which SimFrost produces
actually interesting results. +/- 5% in all 10,000 trials on a
terminal-window sized sim is plenty accurate for my purposes.

Harrison R.

On Mar 11, 2:43 pm, James Edward G. II [email protected]

Hi,

here is my solution. Runs in a Terminal.
A copy can be viewed here: http://sec.modprobe.de/quiz117.rb.html

Tom

On Mar 11, 3:36 pm, “Gordon T.” [email protected] wrote:

I don’t have time to write an entire implementation, but I wanted to
do some graphics. I borrowed Eric’s code and used the rubysdl library
with it. I’m sure there is large room for improvement, but my wife
wants me to clean out the garage:)

Cool! Thanks!

Eric

Are you interested in on-site Ruby training that uses well-designed,
real-world, hands-on exercises? http://LearnRuby.com

Right, here’s the graphical version, using RMagick (thanks for the idea,
Chris).
Much improved from the last try, with bits and pieces stolen from many
ppl.
BTW, ImageMagic does not like 200x200 grids written bit by bit, just
fyi…

#!/usr/bin/env ruby -w

class SimFrost

require ‘RMagick’
VACUUM=" " #Chris S. reminded me of constants…
VAPOR="+"
ICE="*"
ICECOLOR=‘blue’
VAPORCOLOR=‘grey’
VACUUMCOLOR=‘white’
attr_reader :grid, :vapor

def initialize (width=30,height=24, vapor_percent=30, showvapor=true)
@x_size=width/22 #this should take care of those odd numbers
@y_size=height/2
2
@vapor_percent=vapor_percent
@offset=1
@image = Magick::ImageList.new
@showvapor=showvapor
create_grid
end

def create_grid
@grid=Array.new(@x_size){Array.new(@y_size)}
@bitmap = Array.new(@x_size){Array.new(@y_size,0)}
@grid.each_with_index do |row, x|
row.each_with_index do |square, y|
if rand(100) < @vapor_percent
@grid[x][y]= VAPOR
@bitmap[x][y]=1 if @showvapor
else
@grid[x][y]= VACUUM
end
end
end
@grid[@x_size/2][@y_size/2]=ICE
@bitmap[@x_size/2][@y_size/2]=1
end

def check_neighborhoods #interesting bits shamelessly stolen from
Dave
Burt
@offset ^= 1
(@offset@x_size).step(2) do |x0|
(@offset@y_size).step(2) do |y0|
x1=(x0+1) % @x_size
y1=(y0+1) % @y_size
neighborhood=[@grid[x0][y0], @grid[x0][y1], @grid[x1][y0],
@grid[x1][y1]]
if neighborhood.include?(VAPOR)
if neighborhood.include?(ICE)
#there’s got to be a rubyer way of doing this…
if @grid[x0][y0] == VAPOR #top left corner
@grid[x0][y0] = ICE
@bitmap[x0][y0] = 1
end
if @grid[x0][y1] == VAPOR #one right
@grid[x0][y1] = ICE
@bitmap[x0][y1]
end
if @grid[x1][y0] == VAPOR #one down
@grid[x1][y0] = ICE
@bitmap[x1][y0]
end
if @grid[x1][y1] == VAPOR #right and down
@grid[x1][y1] = ICE
@bitmap[x1][y1] = 1
end
elsif rand(2)==1
@grid[x0][y0], @grid[x0][y1], @grid[x1][y0], @grid[x1][y1] =
@grid[x1][y0], @grid[x0][y0], @grid[x1][y1], @grid[x0][y1]
if @showvapor
@bitmap[x0][y0], @bitmap[x0][y1], @bitmap[x1][y0],
@bitmap[x1][y1] = 1, 1, 1, 1
end
else #It’s the correct sequence, maybe… I think…
@grid[x0][y0], @grid[x0][y1], @grid[x1][y0], @grid[x1][y1] =
@grid[x0][y1], @grid[x1][y1], @grid[x0][y0], @grid[x1][y0]
if @showvapor
@bitmap[x0][y0], @bitmap[x0][y1], @bitmap[x1][y0],
@bitmap[x1][y1] = 1, 1, 1, 1
end
end
end
end
end
end

def to_s
@grid.transpose.collect{|row| row.join}.join("\n")
end

def generate_gif
something = false
if @image.empty?
@image.new_image(@x_size, @y_size)
else
@image << @image.last.copy
end
frame = Magick::Draw.new
@grid.each_with_index do | row, x |
row.each_with_index do |square, y|
if @bitmap[x][y] == 1
if square == ICE
frame.fill(ICECOLOR).point(x,y)
something = true
elsif square == VAPOR
frame.fill(VAPORCOLOR).point(x,y)
something = true
elsif square == VACUUM
frame.fill(VACUUMCOLOR).point(x,y)
something = true
end
@bitmap[x][y] =0
end
end
end
frame.draw(@image) if something
puts “On to next frame”
end

def create_animation
@image.write(“frost_#{Time.now.strftime(”%H%M")}.gif")
end
end

s=SimFrost.new(200,200,40)
step = 0
puts “Sit back, this may take a while”
while s.grid.flatten.include?(SimFrost::VAPOR) #flatten inspired by
James
Edward G.
puts “Step #{step}: creating frame”
s.generate_gif
s.check_neighborhoods
step += 1
end
s.create_animation
puts “Done”

James G. wrote:

The three rules of Ruby Q.:

Woah, this quiz was very entertaining. I enjoyed a lot doing it, and
still enjoy watching it every time :smiley:

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 M. [email protected]

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 :open_mouth:

On Mar 12, 2007, at 3:19 AM, Christoffer Lernö wrote:

may be off by quite a bit.

What’s a “small grid” and what’s “quite a bit”? :wink:

For 10x10 with 10% vapour, the number of particles typically range
between 5 and 15, that’s between 50% and 150% of the desired
amount. That’s quite a bit, since the patterns with 15 vapour
particles are quite different from one with 5.

Sure, but a 10x10 isn’t a sure interesting simulation. :wink:

I’m not saying that this is necessarily a flaw in the solutions, I
just thought it was an interesting problem - randomly mixing
elements in a two-dimensional grid.

Yes, it is interesting.

James Edward G. II

On Mar 11, 2007, at 21:43 , James Edward G. II wrote:

On Mar 11, 2007, at 3:07 PM, Christoffer Lernö wrote:

It’s interesting that it looks like everyone populated their grid
using a randomizer for each position in the grid.

This is is obviously fast, but for small grids (and low
percentages), the percentage of actual generated vapour particles
may be off by quite a bit.

What’s a “small grid” and what’s “quite a bit”? :wink:

For 10x10 with 10% vapour, the number of particles typically range
between 5 and 15, that’s between 50% and 150% of the desired amount.
That’s quite a bit, since the patterns with 15 vapour particles are
quite different from one with 5.

The larger the grid, the more this value evens out (of course).

I’m not saying that this is necessarily a flaw in the solutions, I
just thought it was an interesting problem - randomly mixing elements
in a two-dimensional grid.

Christoffer

Any suggestions for a complete beginner in generating graphics for a
more
efficient way of doing this sim graphically? RMagick is real nice, but
it’s
not doing too well generating gif with a 320x240 grid.

On Mar 12, 2007, at 9:31 AM, Albert Ng wrote:

Any suggestions for a complete beginner in generating graphics for
a more
efficient way of doing this sim graphically? RMagick is real nice,
but it’s
not doing too well generating gif with a 320x240 grid.

Did you take a look at my solution that outputs PPM graphics? PPM is
a very easy graphic format, so I think it’s great for beginners.

James Edward G. II

James Edward G. II

I was hoping to write PPMs a while ago, but OS X’s Preview doesn’t seem
to support it.

Thanks James Edward G. II, that’s a lot faster.
Now to make a movie out of it…

On Mar 12, 2007, at 10:49 AM, [email protected] wrote:

James Edward G. II

I was hoping to write PPMs a while ago, but OS X’s Preview doesn’t
seem
to support it.

Yeah, I don’t think preview does. Graphic Converter does though.
It’s what I used to make the movie. I bet you can also get netpbm
installed on OS X, then you can just shell out to those programs to
convert the images or build a movie.

James Edward G. II

James Edward G. II:

Benjohn B.:

I was hoping to write PPMs a while ago, but OS X’s Preview doesn’t
seem
to support it.

Yeah, I don’t think preview does. Graphic Converter does though.
It’s what I used to make the movie. I bet you can also get netpbm
installed on OS X, then you can just shell out to those programs to
convert the images or build a movie.

Ah, good tips. Thanks!

On 3/12/07, James Edward G. II [email protected] wrote:

Yeah, I don't think preview does. Graphic Converter does though. It's what I used to make the movie. I bet you can also get netpbm installed on OS X, then you can just shell out to those programs to convert the images or build a movie.

James Edward G. II

Talking about netpbm, I used it from Linux and I had no problem to
create an mpeg from 705 frames (512x512) only that err… it is about
50 times too big.

30MB
No way to post this to the list, I continue to try making it smaller -
this is my first attempt of video compression.

Would you be interested in it for the site maybe, and if so what size
is acceptable?
~ 10MB?

So if someone wants the configuration file for ppmtompeg I can post
it(1), maybe we will get some help to make the files smaller too?
Cheers
Robert

(1) ASAI am home again :wink:

Whoop dee do, I spent half of yesterday setting up ImageMagick and
RMagick and now I learn that PPMs are better. :wink: Ah well.

On Mar 12, 10:01 am, James Edward G. II [email protected]
wrote:

On Mar 12, 2007, at 10:49 AM, [email protected] wrote:

On Mar 12, 2007, at 9:31 AM, Albert Ng wrote:

Any suggestions for a complete beginner in generating graphics for
a more
efficient way of doing this sim graphically? RMagick is real nice,
but it’s
not doing too well generating gif with a 320x240 grid.

Did you take a look at my solution that outputs PPM graphics? PPM is
a very easy graphic format, so I think it’s great for beginners.

James Edward G. II

I was hoping to write PPMs a while ago, but OS X’s Preview doesn’t
seem
to support it.

Yeah, I don’t think preview does. Graphic Converter does though.
It’s what I used to make the movie. I bet you can also get netpbm
installed on OS X, then you can just shell out to those programs to
convert the images or build a movie.

On Mar 12, 2007, at 5:53 PM, Harrison R. wrote:

Whoop dee do, I spent half of yesterday setting up ImageMagick and
RMagick and now I learn that PPMs are better. :wink: Ah well.

Oh not “better.” More just a poor man’s RMagick. :wink:

Well, for very simple graphics at least. RMagick does a LOT more.

James Edward G. II