• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Lua] Procedural Random Number Generator

Very useful if you need randomness that is async.
Lua:
do
    RNG = {seed = 0, mult = 1140671485, mod = 16777216, inc = 12820163}
    --[[
        Default RNG values with a seed of 0.  You can initialize a new RNG
        class with just the seed if you don't want to change the rest of the
        values.
        API:
        Class: RNG
            Arguments:
                seed - initial seed of the RNG table
                mult - multiplier value
                mod - modulo value
                inc - increment value
        Example:
        rng = RNG:new({
            seed = math.floor(os.clock()*100000),
            mult = 1140671485,
            mod = 16777216,
            inc = 12820163
        })
        To access the RNG class we have just created, call the table:
        rng() -- no arguments is a range betwee 0 and 1
        rng(100) -- only 1 argument means a range 1 - val (1-100 in this case)
        rng(5.0,25.0) -- two arguments means a range from low - high (int or float)
    ]]--

    function RNG:new(tbl)
        tbl = tbl or {}
        setmetatable(tbl,RNG)
        tbl.div = 1/tbl.mod
        return tbl
    end

    function RNG.__index(tbl,key)
        return RNG[key]
    end

    local d, m
    function RNG:__call(low,high)
        d = self.seed * self.mult + self.inc
        m = d - math.floor(d * self.div) * self.mod
        if m < 0 then
            m = m + self.mod
        end
        self.seed = m
        m = m * self.div
        if low then
            if high then
                m = m*(high-low)+low
            else
                m = m *(low-1) + 1
            end
            if math.type(low) == "integer" then
                m = m>=0 and math.floor(m+0.5) or math.ceil(m-0.5)
            end
        end
        return m
    end
end
 
Last edited:
Level 15
Joined
Mar 25, 2016
Messages
1,327
Very useful if you need randomness that is async.
As in every client gets different values as long as the seed itself is async?

Lua:
RNG:new(math.floor(os.clock()*100000),1140671485,16777216,12820163)
Can you explain how to choose these parameters? I assume os.clock() is used to get an async seed? What if a sync seed is desired. Do you use
GetRandomInt() instead?

Can you provide a source that explains how the pseudo random number generator works (e.g. how uniform it is, period lengths).
 
Level 10
Joined
Jan 13, 2017
Messages
88
As in every client gets different values as long as the seed itself is async?
What if a sync seed is desired. Do you use
GetRandomInt() instead?
Yes, os.clock() is the thing that makes this async due to it using some type of "Time since game client started". Using GetRandomInt() would make it synced random.

Can you explain how to choose these parameters?
As for the other 3 parameters, those would be the modifiers that choose how the next RNG number is generated. The ones used in this example would be the ones used for the old standard PRNG generator of Visual Basic (v6 and earlier).
So they most likely would not need much tweaking at all.
Can you provide a source that explains how the pseudo random number generator works (e.g. how uniform it is, period lengths).
The implementation looks to be a LCG generator, or a variation of it.
Linear congruential generator - Wikipedia
 
As in every client gets different values as long as the seed itself is async?

Lua:
RNG:new(math.floor(os.clock()*100000),1140671485,16777216,12820163)
Can you explain how to choose these parameters? I assume os.clock() is used to get an async seed? What if a sync seed is desired. Do you use
GetRandomInt() instead?

Can you provide a source that explains how the pseudo random number generator works (e.g. how uniform it is, period lengths).
Yeah you can have an async seed to start, or you can push the seed forward locally.

For example:
You want to have an effect displayed locally for an ability that has a random position - you can use this to set the effect position locally.

Or for my uses, I have a bunch of sounds for my music track. The track is randomly selected. New music starts when the old music stops playing (which is an async native). So then the next song selection is going to use async random.
 
Please include the API in the top of the script for documentation purposes to highlight things such as optional parameters. Additionally, or alternatively, if this used Emmy Annotation, someone with vsCode would know right away if a parameter is optional.
Okay I'll update it in a few.
Edit: done, should be clearer
 
Last edited:
Top