Random generates same number in loop on mobile

while wait() do
	print(Random.new():NextInteger(1,6))
end

Prints the exact same number on mobile every time, this is game breaking for me as I have a recursive function in my game which I use to get a different number every time so there can’t be two numbers in a row, but because Random() always produces the same number the game hangs as it’s on an infinite loop.

My code which causes the game to freeze on mobile:

local previousNum = 1

function getNum()
	local Num = Random.new():NextInteger(1,6)
	if Num == previousNum then
		return getNum()
	else
		previousNum = Num
		return Num
	end
end

This only happens when playing on an actual mobile device, not in studio. The device I’m using is a Samsung Galaxy S9+.

3 Likes

I tried this on a OnePlus5T and was unable to reproduce your observed behaviour.

As a general point, you should probably define Random.new() once at the start of a script rather than creating a new Random object each time.
You should also see if initialising it with tick() makes any difference in case the way Random generates a seed on its own is somehow broken on mobile.

local rand = Random.new(tick())

while wait() do
	print(rand:NextInteger(1,6))
end
2 Likes

Right, so my friend tested the original code with his iPhone 6s+ and Samsung Galaxy S6 Edge and it worked fine on his iPhone 6+, but it did not work on his Samsung Galaxy S6 Edge.

I have also tried your code on my own Galaxy S9+ and it worked fine, so it seems like this has something to do with the way Random() generates a seed on its own on Samsung devices.

math.randomseed had the same issue across all platforms I believe.

Either way, Random isn’t designed to be using a new object every time.
Setting it to a variable outside of the repeating-code’s scope is both more performant, and it allows for NextInteger to do its work properly (because it has a frame of reference).

You’re spawning a new random number generator with each call, and you’re not seeding it, and it looks like it’s using the same default seed. It’s only meant to be used the way @MasterDaniel shows. That said, the documentation indicates that, when not seeded explicitly, Random.new() should use a seed “pulled from an internal entropy source.” Clearly that’s not happening on the platform returning all 4.

Also, just for readability’s sake, consider converting your function to a while or repeat loop. A tail-recursive function like this can be converted to a loop that doesn’t even need a stack, just something like:

local prev = 0
local RNG = Random.new(tick())

local function GetNum()
	local num
	repeat
		num = RNG:NextInteger(1,6)
	until num~=prev
	prev = num
	return num
end

Once converted to a loop, the original bug you posted about would still be a problem without the manual seeding of Random using tick(). It would just have caused an infinite loop instead of a call stack overflow. You can really easily safeguard against hanging your game with a cap on how many times you loop. Consider your example of needing numbers from 1 to 6. If you cap the loop at say, 15 tries, the chance of hitting that cap and returning a duplicate value with no bug is math.pow(1/6, 15), which is 2 in 1 trillion. You’re more likely to win a tri-state lottery than hit 15 iterations under normal operation, yet 15 calls to NextInteger takes a few microseconds, so you’d catch something like the original bug as near instantly as makes no difference. :slight_smile: If those odds aren’t long enough, just cap it at 100; the sun will have engulfed the earth and Mars pigs will have evolved wings before it returns a duplicate under normal operation of NextInteger. The code would just have something like this:

local function GetNum()
	local count = 0
	local num
	repeat
		count = count + 1
		num = RNG:NextInteger(1,6)
	until num~=prev or count > 14
	if count > 14 then
		warn("GetNum exceeded allowed loop count, possibly returning a duplicate value")
	end
	prev = num
	return num
end
7 Likes

Clearly, this is the source code for the mobile entropy source:

More seriously…,

The thread is marked as solved by @AllYourBlox’s post, which is absolutely right that you should not instantiate a Random every time you want one random number, but there is still a bug if Random.new() provides the exact same seed every time when the doc says it pulls from entropy. A developer should be able to call Random.new() and not get the same sequence every time (in a tight loop maybe, but across multiple frames?)

5 Likes

Thanks for the report. This seems to be an issue with the seeding function that only occurs on Android devices afaict. We’ll try to fix this.

This should be fixed in a week or so. Thanks for the report.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.