NedoP
NedoP

Reputation: 13

Java Random.nextInt() repeating numbers only in a loop

I am trying to procedurally generate a world on a 2D grid. For the random numbers generation I am using a single global java.utils.Random Instance and a seed.
Like this:

public class Game {

    private static final int SEED = 1111;
    private static final Random RANDOM = new Random(SEED);
    private static final int roomsCount = generateRandomNumberOfRooms();
}

Everything worked just fine untill I wrote this method:

public ArrayList<XYCoords> generateRandomCoordinates(){
        ArrayList<XYCoords> coords = new ArrayList<>(roomsCount);

        for(int i = 0; i < roomsCount; i+=1) {
            XYCoords xy = new XYCoords(RANDOM.nextInt(WIDTH),  RANDOM.nextInt(HEIGHT));
            coords.add(xy);
        }
        return coords;
    }

When I execute it I get the list of XYCoordinates but they all have the same two X and Y values, for example (11,20) or (12, 5)... etc. and all the rooms land on the same spot. It looks to me like the call to RANDOM.nextInt() in the for loop doesn't update the state of the instance RANDOM.
In all other functions that I call RANDOM.nextInt() it works fine. Some of them use for loops too. The problem is only with this function. It is used once directly in main() and not nested anywhere.
Does anyone have any sense of where the problem might be? I can upload more code if you need, but I think it is irrelevant.

Upvotes: 1

Views: 855

Answers (1)

Markus Steppberger
Markus Steppberger

Reputation: 638

Unwanted behaviour 1: each application run generates the same values in the same order. The reason is your RANDOM object which is instantiated with a seed. Try the following adaption: (Removing the seed, allowing Random to give you random values):

public class Game {

    private static final Random RANDOM = new Random();
    private static final int roomsCount = generateRandomNumberOfRooms();
}

If you need the seed for some reason, you could try to update the seed at each startup.

Clarification what the seed actually does:

(Note that the seed is also set from the constructor if you provide a seed)

setSeed

public void setSeed(long seed)

Sets the seed of this random number generator using a single long seed. The general contract of setSeed is that it alters the state of this random number generator object so as to be in exactly the same state as if it had just been created with the argument seed as a seed. The method setSeed is implemented by class Random by atomically updating the seed to (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1) and clearing the haveNextNextGaussian flag used by nextGaussian(). The implementation of setSeed by class Random happens to use only 48 bits of the given seed. In general, however, an overriding method may use all 64 bits of the long argument as a seed value.

Parameters: seed - the initial seed

Unwanted behaviour 2: the cords are all the same after the loop. The XYCoords class probably declares the x and y as static variables. Solution: Remove the static declaration. As the values are static, the objects share these values as they are bound to the class. The last values which are set are the values you will get if you iterate over all of your XYCoords objects.

In general it is a mixture of both things which lead to the result that you get the same values all the time.

Upvotes: 2

Related Questions