Log In  


There's currently no way to get the existing random seed, since srand() doesn't return anything.

Test/demo:

Cart #yudodekegi-0 | 2020-03-29 | Code ▽ | Embed ▽ | No License
2

Could it return the existing seed so we can temporarily change to a sub-system's ongoing seed, then restore it? Like this, for instance:

function handle_ai(ai)
  local old_seed=srand(ai.seed)
  ⋮
  (stuff)
  ⋮
  srand(old_seed)
end

It's minor, since you can always keep your seeds in shadow values from the outset and manually set them each time, but it'd still be handy.

Thanks! :)

2


1

Unfortunately the PRNG state is 64-bit and is not mapped into RAM, while the API only lets you provide a 32-bit seed. So in order to reset the state to what it was previously you’d need to call srand() but also call rnd() as many times as it had been called previously.

I personally would love to see this solved by having the PRNG state mapped to RAM.


@samhocevar Is that true? I was getting some inconsistent results with seeded randoms earlier, but couldn't produce a minimal reproducing example. That could explain the inconsistencies.


@Xii sure it’s true :-) here’s a full software reimplementation if you’re interested; rnd2() and srand2() should behave exactly like their system counterparts (edit: except for negative numbers):

do
    local a,b = 0,0
    local function _() b = a + rotl(b,16) a += b end
    function rnd2(x)
        _()
        x = x or 1
        if x == 0 then return 0 end
        local b,n = b,x
        while x>0 and b<0 do
            -- hack: simulate unsigned modulo
            while n + n > 0 do n += n end
            b -= n
        end
        return b % x
    end
    function srand2(x)
        a = x or 0xdead.beef
        b = bxor(a, 0xbead.29ba)
        for n=1,32 do _() end
    end
end

@samhocevar

How do you know it's a 64-bit seed?

Anyway, I guess srand() could start taking an optional second value for a full 64 bits and likewise return a tuple of the internal seed.

But yeah, memory-mapping it would probably be good.


@Felice you can find the PRNG algorithm by searching for -1095947846 (0xbead29ba) in any HTML export. You’ll see it uses two 32-bit numbers as its state (the a and b variables in my above code).


@samhocevar

Huh, okay. That means it's absolutely impossible to use rnd() with more than one parallel stream of numbers in the same app.

Like, srand() serves no purpose other than making ONE call to establish determinism for ALL code in an entire run.

That... sucks.

Hmm, but maybe I'm spoiled by the system we used in one engine I worked on, which admittedly didn't provide ideal RNG, but was simplified so that the seed and the state were the same thing. It probably had lousy periodicity and distribution, but it was quite fast.


I agree, sucks if this is the case. We need @zep in here!


5

@Felice, @samhocevar agreed, this should be mapped to RAM. I've added the 8 bytes at 0x5f44

So, it's possible to back up and restore the seed with: memcpy(0x4300,0x5f44,8), memcpy(0x5f44, 0x4300, 8)

Not exactly a solution for the same problem, but when I need a separate but variable number of random values without interrupting the sequence, I just use:

temp = rnd(0x8000)
srand(temp) -- if you want the same seed twice
for i=1,var_num do
 something_else(rnd(0x8000))
end
srand(temp)

1

@zep great news, thanks!

Note that this allows the user to break the PRNG by setting all the bits to zero, causing rnd() to return zero forever. Also anything with too little entropy will take a few iterations before showing the randomness properties again.


1

@zep

Thank you! That's awesome! :)

Thanks also to Sam for suggesting the memory-mapping solution. I think that was probably the ideal fix, given the circumstances. :)



[Please log in to post a comment]