[QUIZ] Diamond-Square (#198)

D

Daniel Moore

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this
quiz until 48 hours have elapsed from the time this message was
sent.

2. Support Ruby Quiz by submitting ideas and responses
as often as you can!
Visit: <http://rubyquiz.strd6.com/suggestions>

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem
helps everyone on Ruby Talk follow the discussion. Please reply to
the original quiz message, if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

## Diamond-Square (#198)

Guten Tag Rubyists,

Following in the same vein as last week's quiz I present to you the
[diamond-square algorithm][1]. Instead of one dimensional terrain as
last week, the random terrain generated by this algorithm is in two
dimensions. Visual displays are again encouraged.

The secret to this algorithm is that there are two phases for
generating the new random values: from the diagonals (diamond) and
from the adjacents (square). This makes the generated terrain smoother
and prevents artifacts from generating solely by the adjacents.

Once equipped with these terrain generation algorithms, making ruby
based Elf/Goblin/Tanks simulations will be a breeze!

There is now a suggestion form up on the Ruby Quiz website:
http://rubyquiz.strd6.com/suggestions . I'm looking forward to all of
your great suggestions!

[1]: http://gameprogrammer.com/fractal.html#diamond

Have Fun!
 
D

Daniel Moore

Since there were no solutions submitted on the mailing list this week
I've included a solution of my own.

class DiamondSquare
def self.rando
rand() - 0.5
end

def self.go(times)
arrays = [[0.5]]

ratio = 2

times.times do
arrays.map! do |array|
insert_nils(array)
end
arrays = insert_arrays(arrays)
compute_from_diagonals(arrays) {|a, b, c, d| (a + b + c +
d)/4 + rando*ratio}
compute_from_adjacents(arrays) {|a, b, c, d| (a + b + c +
d)/4 + rando*ratio}
ratio *= 0.5
end

return arrays
end

def self.insert_arrays(arrays)
new_arrays = []
arrays.size.times do |i|
array = arrays
new_arrays.push array, Array.new(array.size, 0.0)
end
return new_arrays
end

def self.insert_nils(array)
new_array = []
array.size.times do |i|
new_array.push(array, 0.0)
end
return new_array
end

def self.compute_from_adjacents(arrays)
n = arrays.size
n.times do |row|
n.times do |col|
next if (row + col) % 2 == 0
arrays[row][col] =
yield(arrays[(row-1)%n][col], arrays[row][(col-1)%n],
arrays[row][(col+1)%n], arrays[(row+1)%n][col])
end
end
end

def self.compute_from_diagonals(arrays)
n = arrays.size
n.times do |row|
next if row % 2 == 0
n.times do |col|
next if col % 2 == 0
arrays[row][col] =
yield(arrays[(row-1)%n][(col-1)%n], arrays[(row-1)%n][(col+1)%n],
arrays[(row+1)%n][(col-1)%n], arrays[(row+1)%n][(col+1)%n])
end
end
end

def self.print_arrays(arrays)
i = 0
arrays.each do |array|
print "%4d: " % i
array.each do |ele|
print '%1.3f ' % ele
end
i += 1
puts
end
puts '---------------------------------------'
end
end

I actually wrote it almost a year ago to generate the terrain for
[Dungeon Farmer][1]. It's kind of clunky, but it has its merits. Some
of the interesting things about this solution are that rather than
start with a full sized array it starts with a small array (very small
actually, 1x1) and inserts empty elements to be filled in before each
step. This causes the array to double in width and height at each
iteration.

The `compute_from_diagonals` and `compute_from_adjacents` methods
yield the four adjacent or diagonal cells so that you can easily
change the nature of the computation. The cells wrap around in a
toroidal manner to make it easy to compute the values on the edges.

It was fun to make and I hope you find it interesting!

[1]: http://code.google.com/p/strd6/wiki/DungeonFarmer
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top