Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat
Randomness - Gideros Forum

Randomness

How can I make my random number sequence persistent between games?

I am making something that is procedurally generated using a fixed random seed. This is fine but if the player exits the game... how do I make the random sequence continue when they next load the game?

There doesn't seem to be any way to save the current state of Core.random
«1

Comments

  • hgy29hgy29 Maintainer
    edited December 2017 Accepted Answer
    Couldn’t you just save the current seed to a file ? core.randomSeed() shall return the current seed.
  • Well that's an easy solution, thanks. Reference guide does not say that it will return the current seed :)
  • antixantix Member
    edited December 2017
    So how does Core.randomSeed() actually work?
    seed = 12345678
     
    Core.randomSeed(0, seed)
    print("random seed = ", Core.randomSeed())
     
    local function advance() 
      seed = Core.randomSeed()
      print("advance using", seed)
    end
     
    stage:addEventListener(Event.KEY_DOWN, function(event) if event.keyCode == KeyCode.SPACE then advance() end end)
    Running the code above I get the following results...
    random seed = 12345678
    advance using 13956884
    advance using 14019841
    advance using 14081215
    advance using 14123875
    advance using 14154412
    advance using 14187182
    advance using 14207933
    advance using 14227556
    advance using 14247235

    Which seems to be terribly wrong in my mind because all I am attempting to do in advance() is get the current random seed. If that's syntactically correct then why am I getting different numbers when I haven't even called Core.random() at all?

    It seems like it is counting up like something is adding to it constantly.
  • That's odd, the pdf does...

    That's odd because the reference online does not. That's truly random :bz
  • olegoleg Member
    edited December 2017
     math.randomseed(1234)
     print(math.random(), math.random(), math.random())
     
      math.randomseed(1234)
     print(math.random(), math.random(), math.random())
     
     
      print(Core.randomSeed(0,1234),Core.random(),Core.random())
        print(Core.randomSeed(0,1234),Core.random(),Core.random())
    newfile.lua is uploading.
    Uploading finished.
    0.74087692971382 0.21453485694459 0.33819583400069
    0.74087692971382 0.21453485694459 0.33819583400069

    69342833 0.19151945016347 0.49766366626136
    -2034653606 0.19151945016347 0.49766366626136
    my games:
    https://play.google.com/store/apps/developer?id=razorback456
    мій блог по гідерос https://simartinfo.blogspot.com
    Слава Україні!
  • hgy29hgy29 Maintainer
    When you call Core.randomSeed without giving it a new seed, it uses the current time as the new seed, that’s why you see it incrementing
  • @oleg, interesting that math.random works as expected and Core.random does not.
  • olegoleg Member
    edited December 2017
    antix said:

    @oleg, interesting that math.random works as expected and Core.random does not.

     Core.randomSeed(0,1234)
     
      print(Core.random(),Core.random())
       Core.randomSeed(0,1234)
     
      print(Core.random(),Core.random())

    0.19151945016347 0.49766366626136
    0.19151945016347 0.49766366626136
    my games:
    https://play.google.com/store/apps/developer?id=razorback456
    мій блог по гідерос https://simartinfo.blogspot.com
    Слава Україні!
  • @oleg yes I see it's making the same number but if I use Core.randomSeed() it gives the wrong number, which is the seed I am trying to save.

    I would think that Core.randomSeed() should always return the correct seed, not something that seems, well, random.
  • olegoleg Member
    edited December 2017
    here is your example-everything works
    seed = 12345678
     
    Core.randomSeed(0, seed)
    print("random seed = ", Core.random())
     
    local function advance() 
    	Core.randomSeed(0, seed) 
      seed = Core.random()
      print("advance using", seed)
    end
     
    stage:addEventListener(Event.KEY_DOWN, function(event) if event.keyCode == KeyCode.SPACE then advance() end end)
    my games:
    https://play.google.com/store/apps/developer?id=razorback456
    мій блог по гідерос https://simartinfo.blogspot.com
    Слава Україні!
  • @oleg, nope, that is not working. It does not get the actual seed which is what I want to save and reload in subsequent games, it just creates a random number between 0 and 1.
  • I do not understand what you want
    save the seed = 12345678
    my games:
    https://play.google.com/store/apps/developer?id=razorback456
    мій блог по гідерос https://simartinfo.blogspot.com
    Слава Україні!
  • During the game I call Core.random(0) to get random numbers between 0 and 1.
    Internally this modifies the seed (I think).
    I call Core.randomSeed() to get the seed when I want to save it.

    The problem is that Core.randomSeed() does not return the actual seed, instead it seems to be returning something completely random, which is not what I require.

    I hope that makes it a bit clearer :)
  • hgy29 said:

    When you call Core.randomSeed without giving it a new seed, it uses the current time as the new seed, that’s why you see it incrementing

    Ahh I see now. So that's then totally useless for me :(

    I want to save the state of the random number generator so it can be used on next program load.

    I'll just not use it and find another solution. In future it might be good to have such a feature though :)
  • hgy29hgy29 Maintainer
    I don’t understand why it is useless. Even if you pass it a new random seed of your own or leave it use time as new seed, the function returns you the old current seed, which is what you want, no ?
  • olegoleg Member
    edited December 2017
    if I understood correctly
     seed=123
     
    	  Core.randomSeed(0, seed) 
     for i=1,3 do
     --sequence
     print( Core.random())
     
     end
     --save
        seed=123
     ----------------------------
     --extend the sequence
     Core.randomSeed(0, seed)
     
    --skip past sequence
     for i=1,3do
      Core.random()
      end
     
      --extend the sequence
     for i=1,5 do
     print( Core.random())
     
     end
     
     
     --test
     print("---------------------------------------")
       Core.randomSeed(0, 123) 
     for i=1,8 do
     print( Core.random())
     
     end
    my games:
    https://play.google.com/store/apps/developer?id=razorback456
    мій блог по гідерос https://simartinfo.blogspot.com
    Слава Україні!
  • hgy29 said:

    I don’t understand why it is useless. Even if you pass it a new random seed of your own or leave it use time as new seed, the function returns you the old current seed, which is what you want, no ?

    Let me try to explain as best I am able...

    - I am making a game where each level is procedurally generated.
    - I am using the new Core.random system so that the generated results are the same for all devices.. so if I have a random seed of 1234 then on Android, IOS, and Win32 I will generate the same level on all platforms.
    - The game never ends so when the player leaves the game it saves the random state.
    - When the game is run again it loads the saved random state and continues generating random levels from where it left off.
    - Using this system the game is the same on all platforms and the levels generated are all the same, between game sessions.

    So a player who played 50 sessions and made it to level 1000 on IOS sees exactly the same level as a player who played on Android for 5 sessions and reached level 1000.

    Now if the random seed that is returned by Core.randomSeed() is a time stamp then all subsequent games will differ wildly because depending on when the game was exited on any particular device.. the randomSeed returned by Core.randomSeed() will be different.

    Am I correct then at saying this is useless (not going to work) for me? or am I really not getting the point?
  • hgy29hgy29 Maintainer
    You misunderstood what Core.randomSeed actually does.
    It has two functions:
    1°) it returns you the current seed (so that you can resume an ongoing rrandom sequence)
    2°) it allows you to set a new seed

    both functions are executed at the same time during the call:
    oldSeed=Core.randomSeed(0,newSeed)
    if you don't specify a newSeed (or leave it nil), then os.timer() is used as the new seed. This has no effect on the oldSeed.

    So you can still use the oldSeed, save it, and resume your game with Core.randomSeed(0,oldSeed).

    You see ?

    Likes: oleg, SinisterSoft

    +1 -1 (+2 / -0 ) Share on Facebook
  • totebototebo Member
    edited December 2017
    Reading through this I thought there might be something I'm missing, because I plan to use Core.random myself to generate persistent procedural levels. I tested it quickly, and it works great.

    This saves and increments a seed, producing the same random number on any device (well, I tested it on Mac and Android, try it to see if it works for you!).
    	local seed
     
    	-- Load seed if available
    	local seed_filename = "|D|saved_seed.txt"
    	local file = io.open( seed_filename, "r")
    	if file then
    		local str = file:read("*all") 
    		seed = tonumber(str)
    		file:close()
    	else
    		seed = 0
    	end
     
    	-- Use seed
    	Core.randomSeed( 0, seed )
    	local random_number = Core.random()	
    	print( "Seed: " .. seed .. " Random number: " .. random_number )
     
    	-- Increment seed
    	seed += 1
     
    	-- Save seed
    	local file = io.open( seed_filename, "w")
    	file:write( seed )
    	file:close()
    Spitting out this:
    Uploading finished.
    Seed: 0 Random number: 0.54881350230426
    Uploading finished.
    Seed: 1 Random number: 0.41702199843712
    Uploading finished.
    Seed: 2 Random number: 0.43599490262568
    My Gideros games: www.totebo.com
  • @hgy29 yes I totally mis-understood how it work it seems :)

    @totebo that's pretty cool, just adjusting the seed by one. That would work but it does not save the true state of the random number generator.

    I know I'm being a bit "picky" but I want to save the state of the original seed as it unfolds. To this end I am looking for a pure lua based generator where I can get and set it's state as I please.

    Sorry to all that I confused :)
  • hgy29hgy29 Maintainer
    @antix, I think you are confused by the fact that running Core.randomSeed() repeatedly changes the current seed: it is true, but not an issue since what you want is the first result. You don't care if the seed is changed afterwise since you don't intend to generate more random numbers afterward.

    Try this:
    function getSeed()
     local seed=Core.randomSeed(0) --Grab current seed and sets a new time based seed
     Core.randomSeed(0,seed) -- Reset the old seed, just in case
     return seed
    end
     
    function setSeed(s)
     Core.randomSeed(0,s)
    end

    Likes: totebo

    +1 -1 (+1 / -0 ) Share on Facebook
  • @hgy29, I now get how it works, it just doesn't work how I want it to :)
  • hgy29hgy29 Maintainer
    Yes, this is because random generator states are not the seed. Basically the seed is expanded to a bigger structure on which the random generator works. Core.randomSeed return value is an extract of the state which could be reused to restart the generator in a state not too far than what is was. There is no guarantee that it will give the same sequence as if you let the initial random generator continue.
  • There is something pretty important you need to remember when dealing with a random number returned between 0 and 1, because of floating point differences they may actually be different between devices.

    If you want things to be exactly the same between devices then use random integers instead.
  • It is possible for a less random number function to be added that does return the seed in a 32 bit number. The core.random system has a parameter to pick a different random generator.
  • antixantix Member
    edited December 2017

    There is something pretty important you need to remember when dealing with a random number returned between 0 and 1, because of floating point differences they may actually be different between devices.

    If you want things to be exactly the same between devices then use random integers instead.

    @SinisterSoft that's very handy to know. So I suppose I can get a random number from 1 to 10,000 and then divide that by 10,000 which should produce exact results across platforms right? Or does that still allow for floating point errors?

    Likes: totebo

    +1 -1 (+1 / -0 ) Share on Facebook
  • SinisterSoftSinisterSoft Maintainer
    edited December 2017
    No - any floating point between systems could be different (slightly). The random function (I think) keeps everything as integer anyway until it's given to the user - so they won't be wildly out over time.
  • @SinisterSoft okay got it. I checked the random code that I found on github and it does seem to use integers internally and then spits back floating points when required.

    So now I have a basic random number generator that can persist between sessions I'm happy. This is it if anybody might find it useful..
    --[[
     
    Found this code here...
    <a href="https://github.com/linux-man/randomlua" target="_blank" rel="nofollow">https://github.com/linux-man/randomlua</a>
     
     
    Ripped it up, retaining MWC stuff, and added some 
    bits to allow getting and setting the random state
     
     
    Original credits...
    RandomLua v0.3.2
    Pure Lua Pseudo-Random Numbers Generator
    Under the MIT license.
    copyright(c) 2016 Caldas Lopes
     
    --]]
     
    Random = Core.class()
     
    function Random:init(seed)
      seed = seed or os.time()
      self.a, self.c, self.m = 214013, 2531011, 0x100000000 -- from MVC
    	self.ic = self.c
      self:randomSeed(seed)
    end
     
    function Random:random(a, b)
      local floor = math.floor
    	local m = self.m
    	local t = self.a * self.seed + self.c
    	local y = t % m
    	self.seed = y
    	self.c = floor(t / m)
    	if not a then return y / 0x100000000
    	elseif not b then
    		if a == 0 then return y
    		else return 1 + (y % a) end
    	else
    		return a + (y % (b - a + 1))
    	end
    end
     
    function Random:randomSeed(seed)
    	self.c = self.ic
    	self.seed = seed % 0x100000000
    end
     
    function Random:getState()
      return {
        seed  = self.seed,
        a     = self.a,
        c     = self.c,
        m     = self.m,
        ic    = self.ic,
      }
    end
     
    function Random:setState(state)
        self.seed  = state.seed
        self.a     = state.a
        self.c     = state.c
        self.m     = state.m
        self.ic    = state.ic
    end
     
    function Random:dumpState()
      print("Random State")
      print("seed " .. self.seed)
      print("a    " .. self.a)
      print("c    " .. self.c)
      print("m    " .. self.m)
      print("ic   " .. self.ic)
    end

    Likes: SinisterSoft

    +1 -1 (+1 / -0 ) Share on Facebook
  • self.c = floor(t / m)
    can be changed to
    self.c = t//m
    faster...

    Likes: antix

    +1 -1 (+1 / -0 ) Share on Facebook
Sign In or Register to comment.