• 13
• 16
• 19
• 27
• 9

# Island Generation -- Problem with Duplicates

This topic is 1905 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello all,

I have been working for the past few days on a perlin noise island generator (using libnoise). What I do is generate a terrain, and then apply an island mask to it.  I have added a seed to the island generation portion but I am getting similiar results each time even with a seed. Can someone help me so that I can generate a truely random island each time? Here is what I am currently getting (seeds in parenthesis):

(1233212)

(999555857)

So, as you can see, the maps are very similar. I really don't know why. If needed, here is my code:

(Steps I take)

1st Generate terrain

3rd Apply island mask to terrain

4th Draw the terrain

//Little program to generate test map with only 0 and 1 tile

#include <iostream>
#include <libnoise/noise.h>
#include "noiseutils.h"
#include <stdlib.h>
#include <string>

using namespace std;
using namespace noise;

int main()
{

module::Perlin myModule;

utils::NoiseMap heightMap;
utils::NoiseMapBuilderPlane heightMapBuilder;
heightMapBuilder.SetSourceModule (myModule);
heightMapBuilder.SetDestNoiseMap (heightMap);
heightMapBuilder.SetDestSize (512, 512);
heightMapBuilder.SetBounds (2.0, 6.0, 1.0, 5.0);
heightMapBuilder.Build ();

string seed;
int iseed;
//Add support for character conversion to ints and clip string to 8 numbers
cout << "Please enter a seed: ";
getline(cin,seed);

cout << seed << endl;
iseed = atoi(seed.c_str());
cout << iseed << endl;

float islandMap[512][512];

enum directions {LEFT = 0, UP, RIGHT, DOWN, NONE};

for(int count = 0; count < 1; count++)
{

//Initialize values for a new map
for (int i = 0; i < 512; i++)
{
for (int j = 0; j < 512; j++)
{
islandMap[j][i] = 0;
}
}

//Choose a random location, travel in random directions
int xRand = 0,yRand=0;
int xPos = 0,yPos=0;

srand((iseed*57)/(iseed*17));
cout << iseed;

xRand= rand()%512;
yRand= rand()%512;

xPos = xRand;
yPos = yRand;

islandMap[xRand][yRand] = 1;
directions direction = NONE;

srand((int)((iseed*iseed)/57));
for (int i = 0; i < 10000000; i++)
{
if(xPos == 511 || xPos == 0)
{
xPos = rand()%512;
islandMap[xPos][yPos] +=1;
}

if(yPos == 511 || yPos == 0)
{
yPos = rand()%512;
islandMap[xPos][yPos] +=1;
}

int oldValue;

oldValue = islandMap[xPos][yPos];

int temp;
temp = rand()%4;
direction = (directions)temp;
if(direction == LEFT)
{
xPos-=1;
}
else if(direction == UP)
yPos-=1;
else if(direction == RIGHT)
xPos+=1;
else if(direction == DOWN)
yPos+=1;

if(oldValue >= islandMap[xPos][yPos])
{
islandMap[xPos][yPos] +=1;
}

}

//find max value for normalizing
int max = 0;
int min = 0;
for (int i = 0; i < 512; i++)
{
for (int j = 0; j < 512; j++)
{
if(max < islandMap[i][j])
max = islandMap[i][j];

//cout << islandMap[i][j] << " ";

if(min > islandMap[i][j])
min = islandMap[i][j];
}
}

//Multiply values of original by new maps generated
for (int i = 0; i < 512; i++)
{
for (int j = 0; j < 512; j++)
{
float islandValue = (islandMap[i][j] - min) / (max-min);
//cout << islandValue;
float mapValue = (heightMap.GetValue(i,j) + 1) / (1+1);
float newValue = (-1 + (2/1)*(islandValue*mapValue));
heightMap.SetValue(i,j, newValue);
}
}
}

utils::RendererImage renderer;
utils::Image image;
renderer.SetSourceNoiseMap (heightMap);
renderer.SetDestImage (image);
renderer.EnableLight ();
renderer.SetLightContrast (3.0);
renderer.SetLightBrightness (2.0);
renderer.Render ();

utils::WriterBMP writer;
writer.SetSourceImage (image);
writer.SetDestFilename ("tutorial.bmp");
writer.WriteDestFile ();

return 0;
}

##### Share on other sites

To elaborate on SuperVGA, this line is the line that is fishy:

srand((iseed*57)/(iseed*17));

The iseed in the numerator is cancelled by the iseed in the denominator, causing the seed to always be 57/17, or 3, when the island mask is generated.

Edited by FLeBlanc

##### Share on other sites
57/17 isn't 3, 51/17 is. Depends how big the seed is, and if it overflows or not. Calling srand more than once is dodgy anyway, in this circumstance. The bitmaps aren't the same though, they look slightly different. Only call srand more than once to repeat a previous random sequence.

##### Share on other sites

I'd rebuild it, piece by piece.  That's obviously terrible advice, but its honestly what I would do.

Although I didn't look at your code, you might try this:
You might want to generate a constant perlin noise and a random location, instead of a random perlin noise and a constant location.

Also try adjusting the map maximums to some number which is coprime to the number you're using right now.  Avoid Mersenne numbers.  If that doesn't work, try again with a number coprime to both of those.  You might be generating a specific repeating pattern or something.  Well, no, the map would be offset I suppose.

Not sure.  Simplify and try to generate more basic structures.  Try to make those diverse.  Then add more depth.

Change each number one by one dude.  It's either a specific algorithm or a specific constant that's throwing you off.

##### Share on other sites

57/17 isn't 3, 51/17 is. Depends how big the seed is, and if it overflows or not. Calling srand more than once is dodgy anyway, in this circumstance. The bitmaps aren't the same though, they look slightly different. Only call srand more than once to repeat a previous random sequence.
[/quote]

Actually, 57/17 = 3 here, since we're talking about integers and thus integer division. But well-spotted for the overflow - that's really the only reason why the maps are different. Don't know what the rationale behind those numbers was, though.

##### Share on other sites

I just got done playing with his code. I took out the multiple calls to srand (now a single call: srand(iseed);), the do nothing for loop and various other oddities ((2 / 1) ?). It now creates very regular terrains. The generated terrains show a very strong rotational symmetry. Each 1/4 of the image is nearly an exact copy of each other, rotated 90°.

I think the entire algorithm needs to be rewritten.

Edited by MarkS

##### Share on other sites

The algorithm used looks like the same from: http://breinygames.blogspot.nl/2012/06/generating-terrain-using-perlin-noise.html

Just playing around a little (I did change some code so I could more easily read it), here is what I came up with:

//Little program to generate test map with only 0 and 1 tile

#include <iostream>
#include <noise/noise.h>
#include "noiseutils.h"
#include <stdlib.h>
#include <string>
#include <sstream>

using namespace std;
using namespace noise;

template <typename T> string tostring (T v) {
ostringstream ss;
ss << v;
return ss.str();
}

void generate_island(int iseed) {
module::Perlin myModule;

myModule.SetSeed (iseed);
utils::NoiseMap heightMap;
utils::NoiseMapBuilderPlane heightMapBuilder;
heightMapBuilder.SetSourceModule(myModule);
heightMapBuilder.SetDestNoiseMap(heightMap);
heightMapBuilder.SetDestSize(512, 512);
heightMapBuilder.SetBounds(2.0, 6.0, 1.0, 5.0);
heightMapBuilder.Build();

float islandMap[512][512];
srand(iseed);

for (int count = 0; count < 1; count++ ){
//Initialize values for a new map
for (int i = 0; i < 512; i++) {
for (int j = 0; j < 512; j++) {
islandMap[j][i] = 0;
}
}

//Choose a random location, travel in random directions
int x = 0, y = 0;
float previous = 0;

for (int i = 0; i < 1000000; i++) {
if(y == 511 || y == 0 || x == 511 || x == 0) {
x = rand() % 512;
y = rand() % 512;
}

if(previous >= islandMap[x][y]) {
islandMap[x][y] += 1;
}

previous = islandMap[x][y];

switch(rand() % 4) {
case 0:
x -= 1;
break;
case 1:
y -= 1;
break;
case 2:
x += 1;
break;
case 3:
y += 1;
break;
default:
break;
}
}

//find max value for normalizing
for (int i = 0; i < 512; i++) {
for (int j = 0; j < 512; j++) {
islandMap[i][j] = islandMap[i][j] * 2 - 1;
}
}

int max = 0;
int min = 0;
for (int i = 0; i < 512; i++) {
for (int j = 0; j < 512; j++) {
if(max < islandMap[i][j]) {
max = islandMap[i][j];
}

if(min > islandMap[i][j]) {
min = islandMap[i][j];
}
}
}

//Multiply values of original by new maps generated
for (int i = 0; i < 512; i++) {
for (int j = 0; j < 512; j++) {
float islandValue = (islandMap[i][j] - min) / (max - min);
float mapValue = (heightMap.GetValue(i, j) + 1) / 2;
float newValue = 2 * islandValue * mapValue - 1;
heightMap.SetValue(i, j, newValue);
}
}
}

utils::RendererImage renderer;
utils::Image image;
renderer.SetSourceNoiseMap (heightMap);
renderer.SetDestImage (image);
renderer.EnableLight ();
renderer.SetLightContrast (3.0);
renderer.SetLightBrightness (2.0);
renderer.Render ();

utils::WriterBMP writer;
writer.SetSourceImage (image);
writer.SetDestFilename ("tutorial" + tostring(iseed) + ".bmp");
writer.WriteDestFile ();
}

int main() {
for(int i=0; i<10; ++i){
generate_island(i);
}
return 0;
}


##### Share on other sites
Your seed is always the same... Always 57/17.
-What is the reason behind it, I wonder?

You're calling srand all time. -You should only be calling it once (it keeps the following numbers the same for every time it's invoked with the same seed. The behaviours you're experiencing is familiar to when i did it years ago.
To elaborate on SuperVGA, this line is the line that is fishy:

srand((iseed*57)/(iseed*17));

The iseed in the numerator is cancelled by the iseed in the denominator, causing the seed to always be 57/17, or 3, when the island mask is generated.

Thankyou for pointing that out! I have fixed that but now I am still getting similar maps.. :/. I am sure changing that helps though

I'd rebuild it, piece by piece.  That's obviously terrible advice, but its honestly what I would do.

Although I didn't look at your code, you might try this:
You might want to generate a constant perlin noise and a random location, instead of a random perlin noise and a constant location.

Also try adjusting the map maximums to some number which is coprime to the number you're using right now.  Avoid Mersenne numbers.  If that doesn't work, try again with a number coprime to both of those.  You might be generating a specific repeating pattern or something.  Well, no, the map would be offset I suppose.

Not sure.  Simplify and try to generate more basic structures.  Try to make those diverse.  Then add more depth.

Change each number one by one dude.  It's either a specific algorithm or a specific constant that's throwing you off.

I got lost at your third paragraph :/ Can you please elaborate on the thing that I should also try adjusting

I just got done playing with his code. I took out the multiple calls to srand (now a single call: srand(iseed);), the do nothing for loop and various other oddities ((2 / 1) ?). It now creates very regular terrains. The generated terrains show a very strong rotational symmetry. Each 1/4 of the image is nearly an exact copy of each other, rotated 90°.

I think the entire algorithm needs to be rewritten.

Sorry yeah the (2/1) thing gets the numbers into the range of -1 and 1. The 1 in that equation is the max number in the set of numbers. And what algorithm needs to be rewritten, the island masking one? :/ Because if that is so, I have no clue what to do. I just followed what this guy said he did on his blog.

The algorithm used looks like the same from: http://breinygames.blogspot.nl/2012/06/generating-terrain-using-perlin-noise.html

Just playing around a little (I did change some code so I could more easily read it), here is what I came up with:

//Little program to generate test map with only 0 and 1 tile

#include <iostream>
#include <noise/noise.h>
#include "noiseutils.h"
#include <stdlib.h>
#include <string>
#include <sstream>

using namespace std;
using namespace noise;

template <typename T> string tostring (T v) {
ostringstream ss;
ss << v;
return ss.str();
}

void generate_island(int iseed) {
module::Perlin myModule;

myModule.SetSeed (iseed);
utils::NoiseMap heightMap;
utils::NoiseMapBuilderPlane heightMapBuilder;
heightMapBuilder.SetSourceModule(myModule);
heightMapBuilder.SetDestNoiseMap(heightMap);
heightMapBuilder.SetDestSize(512, 512);
heightMapBuilder.SetBounds(2.0, 6.0, 1.0, 5.0);
heightMapBuilder.Build();

float islandMap[512][512];
srand(iseed);

for (int count = 0; count < 1; count++ ){
//Initialize values for a new map
for (int i = 0; i < 512; i++) {
for (int j = 0; j < 512; j++) {
islandMap[j][i] = 0;
}
}

//Choose a random location, travel in random directions
int x = 0, y = 0;
float previous = 0;

for (int i = 0; i < 1000000; i++) {
if(y == 511 || y == 0 || x == 511 || x == 0) {
x = rand() % 512;
y = rand() % 512;
}

if(previous >= islandMap[x][y]) {
islandMap[x][y] += 1;
}

previous = islandMap[x][y];

switch(rand() % 4) {
case 0:
x -= 1;
break;
case 1:
y -= 1;
break;
case 2:
x += 1;
break;
case 3:
y += 1;
break;
default:
break;
}
}

//find max value for normalizing
for (int i = 0; i < 512; i++) {
for (int j = 0; j < 512; j++) {
islandMap[i][j] = islandMap[i][j] * 2 - 1;
}
}

int max = 0;
int min = 0;
for (int i = 0; i < 512; i++) {
for (int j = 0; j < 512; j++) {
if(max < islandMap[i][j]) {
max = islandMap[i][j];
}

if(min > islandMap[i][j]) {
min = islandMap[i][j];
}
}
}

//Multiply values of original by new maps generated
for (int i = 0; i < 512; i++) {
for (int j = 0; j < 512; j++) {
float islandValue = (islandMap[i][j] - min) / (max - min);
float mapValue = (heightMap.GetValue(i, j) + 1) / 2;
float newValue = 2 * islandValue * mapValue - 1;
heightMap.SetValue(i, j, newValue);
}
}
}

utils::RendererImage renderer;
utils::Image image;
renderer.SetSourceNoiseMap (heightMap);
renderer.SetDestImage (image);
renderer.EnableLight ();
renderer.SetLightContrast (3.0);
renderer.SetLightBrightness (2.0);
renderer.Render ();

utils::WriterBMP writer;
writer.SetSourceImage (image);
writer.SetDestFilename ("tutorial" + tostring(iseed) + ".bmp");
writer.WriteDestFile ();
}

int main() {
for(int i=0; i<10; ++i){
generate_island(i);
}
return 0;
}


Thankyou very much for this! I like the randomness and I love the code but do you know if there is any way to make it less "holey". There are specks of water all over the terrain and what I am going for is more of a solid island surrounded by water

Edited by Riztro

##### Share on other sites

I just got done playing with his code. I took out the multiple calls to srand (now a single call: srand(iseed);), the do nothing for loop and various other oddities ((2 / 1) ?). It now creates very regular terrains. The generated terrains show a very strong rotational symmetry. Each 1/4 of the image is nearly an exact copy of each other, rotated 90°.

I think the entire algorithm needs to be rewritten.

Sorry yeah the (2/1) thing gets the numbers into the range of -1 and 1. The 1 in that equation is the max number in the set of numbers. And what algorithm needs to be rewritten, the island masking one? :/ Because if that is so, I have no clue what to do. I just followed what this guy said he did on his blog.

I apologize for that. I've been dealing with something in my personal life that has kind of clouded my judgement today. I shouldn't have posted that; it wasn't helpful. However, since I *did* post it, look at TrickyLogic's changes. They were small, but significant.