ID:1477076
 
(See the best response by Ter13.)
Code:
proc
Plasma_Generate(iWidth,iHeight)
var/c1
var/c2
var/c3
var/c4

c1 = rand(0,10)/10 // Creates A's height //A B
c2 = rand(0,10)/10 // Creates B's height
c3 = rand(0,10)/10 // Creates C's height
c4 = rand(0,10)/10 // Creates D's height //C D
DivideGrid(0, 0, iWidth, iHeight,c1,c2,c3,c4)

DivideGrid(x, y, width, height,c1,c2,c3,c4)
var/e1
var/e2
var/e3
var/e4
var/m

var/newWidth = width / 2
var/newHeight = height / 2

if(width > 1 || height > 1)
m = (c1+c2+c3+c4) / 4 + Displace(newWidth + newHeight)
e1 = ((c1 + c2) / 2)
e2 = ((c2 + c3) / 2)
e3 = ((c3 + c4) / 2)
e4 = ((c4 + c1) / 2)

if(m < 0)
m = 0
else if(m > 1.0)
m = 1.0
DivideGrid(x, y, newWidth, newHeight, c1,e1 ,m ,e4)
DivideGrid(x + newWidth, y, newWidth, newHeight, e1, c2 ,e2 ,m)
DivideGrid(x + newWidth, y + newHeight, newWidth, newHeight, m, e2 ,c3 , e3)
DivideGrid(x, y + newHeight, newWidth, newHeight, e4, ,m , e3, c4)
else
var c = (c1 + c2 + c3 + c4) / 4
var/turf/newturf
if(c >= 0.6)
newturf = new/turf/snow(locate(x, y, 1))
else if(c >= 0.5)
newturf = new/turf/stone(locate(x, y , 1))
else if(c >= 0.4)
newturf = new/turf/dirt(locate(x, y, 1))
else if(c >= 0.3)
newturf = new/turf/sand(locate(x, y, 1))
else if(c >= 0.2)
newturf = new/turf/water(locate(x, y , 1))
else if(c >= 0.1)
newturf = new/turf/deepwater(locate(x, y , 1))
newturf.name = "[newturf.name] - [c]"
Displace(num)
var/Max = num / (world.maxx + world.maxy) * 3
return (rand(0,10)/10 - 0.5) * Max


Problem description:
Ok so i was playing dwarf fortress and i was like: "i wonder how they randomly generate this?" so i searched it up and it lead me to making a height map out of plasma fractals, so i searched it up a bunch and tried to piece together my own code, didnt turn out as well, above is what i DID get but the problem is is that when i run it(i have a verb that calls it) it puts together this mashed up map of random tiles, somewhere in there i made each tile say what "height" it was at or "c" and they all correspond to eachother pretty well but its all jumpy, could anyone provide an explanation to what im doing wrong? or what error i made if i was doing it right( doubt it )?? Please and thank you!
Best response
Oh man, this is a good question!

By the way, hello fellow Armokian! Mind your cats.

This is a FANTASTIC first attempt at this problem. Complete breath of fresh air on this board.

First, a few pointers:

if(m < 0)
m = 0
else if(m > 1.0)
m = 1.0


Simplify:

m = min(max(m,0),1)


AKA:
//put the below definition somewhere in your code at the top-level
#define clamp(m,a,z) min(max((m),(a)),(z))

clamp(m,0,1)


There's this, as well:

Switches are going to give you a much-needed speed boost.

if(c >= 0.1)
newturf = new/turf/snow(locate(x, y, 1))
else if(c >= 0.5)
newturf = new/turf/stone(locate(x, y , 1))
else if(c >= 0.4)
newturf = new/turf/dirt(locate(x, y, 1))
else if(c >= 0.3)
newturf = new/turf/sand(locate(x, y, 1))
else if(c >= 0.2)
newturf = new/turf/water(locate(x, y , 1))
else if(c >= 0.1)
newturf = new/turf/deepwater(locate(x, y , 1))


Becomes:

switch(c)
if(0 to 0.1)
newturf = new/turf/deepwater(locate(x,y,1))
if(0.1 to 0.2)
newturf = new/turf/water(locate(x,y,1))
if(0.2 to 0.3)
newturf = new/turf/sand(locate(x,y,1))
if(0.3 to 0.4)
newturf = new/turf/dirt(locate(x,y,1))
if(0.4 to 0.5)
newturf = new/turf/stone(locate(x,y,1))
if(0.6 to 1)
newturf = new/turf/snow(locate(x,y,1))


I'd also heavily suggest widening your ranges based on what you want to consider to be ideal map usage.

In my opinion, you should probably have about 30% of your map covered by water, and all but the very shores should probably be deep water, so a very narrow shelf.

Then, to open up the map, sand should be relatively narrow as well, and dirt/grass should be about 30-50% of your map, depending on how much you want to focus gameplay on dwarfy cliffs.

Let's just... Take a look at these new numbers:

deep water: 0.0 to 0.25
water: 0.25 to 0.3
sand: 0.3 to 0.35
dirt: 0.35 to 0.6
stone: 0.6 to 0.85
snow: 0.85 to 1


rand(0,10)/10


is slower than, but does roughly the same thing as:

rand()


You should also note that Dwarf fortress uses multiple maps to get values against, so you can generate a stratified map where the equator is near the center increasing the value of c as you get closer to the center along the y axis, but having the north/south edges scale in from 0. This will give you both elevation and temperature to choose from when generating your terrain.

I think Dwarf Fortress uses 4 or 5 values to generate its worlds: Elevation, Temperature, Volcanism, and Evil are the ones I know about for sure though.


Alright, so let's actually talk about the actual algorithm you are using.

You are essentially using fractal subdivision. It's really simplistic, but it's going to give you a lot of jagged edges until you apply a smoothing algorithm. This can be interpolation, or it can be a physical erosion simulator like Dwarf Fortress uses.


I'm not sure, but your math might be off here:

    Displace(num)
var/Max = num / (world.maxx + world.maxy) * 3
return (rand(0,10)/10 - 0.5) * Max


That * 3 is a little wonky. Not really sure what it's doing in there.

I'm pretty sure that Max should be roughly:

proc/Displace(num)
. = rand () * num / (world.maxx + world.maxy)


That * 3 is going to increase the displacement factor quite a lot and make things really disjointed.


Last, I'd strongly recommend not using locate() like you currently are. Better to grab the entire area before generating, and then stash the positions of the objects in a list prior to initializing the new turfs.

generator
var/tmp
list/turfs
proc
PlasmaGenerate(iWidth,iHeight,z)
turfs = block(locate(1,1,z),locate(world.maxx,world.maxy,z))
DivideGrid(0, 0, iWidth, iHeight,rand(),rand(),rand(),rand())
turfs = null


Now, instead of doing new/turf/deepwater(locate()), you can do this:

new/turf/deepwater(turfs[(y-1)*world.maxx + x])


This will speed up your generator a LOT.


Another point here, though, generating down to w=1/w=h might give you some granularity issues. You might want to stop generating a a higher subdivision, and then interpolate the heights from the corners of your displacement subdivision.

This will give you vaguely smoother results.

Interpolation:

//where dx = localx / cellwidth
//where dy = localy / cellheight
//where c1 = top left corner height value
//where c2 = top right corner height value
//where c3 = bottom left corner height value
//where c4 = bottom right corner height value

h = c3 + (dx * (c2 - c1) - dx * (c4 - c3) ) * dy


Show us some results of your generator next time. It'd be quite nice to see.
Also, if I could, I would vote your question here #1 of the year. This is one of the best first attempts at something I've seen on this board in a long while.
Thanks for the help, and i had to go through around 3 days of reading 4-5 sites before i understood the basics of generating the plasma fractals, and only then did i start, felt really crappy about my code not being up to par after the "studying" :P. Here are some links to screenshots of the code before and after i followed your advice ;-;.

Edit:I lied i didnt see the very last part ;-; where do i put h = c3 + (dx * (c2 - c1) - dx * (c4 - c3) ) * dy ? set it as m or c?

Before: https://imageshack.com/i/giott3p
After: https://imageshack.com/i/5bf9m9p

i noticed how they are all is square patterns, is there something wrong with the "cells" of the fractal(not sure if they themselves are the fractals or if the whole is a fractal)
P.S. sorry for the late response my internet went out for a week ;-;
Yeah, there's definitely a problem with the interpolation we are using...

I had a similar problem back in 2010:

http://www.byond.com/members/ Ter13?command=view_post&post=139795&first_unread=1&hl=fracta l&hla=Ter13

I wound up fixing it, but I've since deleted the source and found way better ways of handling it than the code shown above.

I was using a single-pass fractal turbulence, while you are using a multi-pass.

I'm not exactly sure where your inaccuracy is coming from, but it seems to be tilted to one corner like mine was.
hmm well thanks for your help, ill continue looking around for more info :P