• 🏆 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!

Random Number Generator 1.2

This bundle is marked as pending. It has not been reviewed by a staff member yet.
JASS:
/*
    This is a linear congruential generator in vJass. You can have different setups and seeds in tandem.
    This wiki article contains a good list of numbers to plug in:
    https://en.wikipedia.org/wiki/Linear_congruential_generator#cite_note-Steele20-2
 
    These kind of generators can generate psuedo random numbers without repeating until it reaches the period.
    The period is just a term used to denote the number where the generator will reset and patterns can emerge if
    this period is too small.
 
    The maximum a period can be is based off of the .mod parameter. The period can never exceed the .mod, but it can be less
    than the .mod if you have bad parameters - hence the warning below.
 
    Combining two generators can increase the period by an order of magnitude of 9, meaning you can have really, really long periods
    without patterns emerging. (Like 5,290,000,000,000,000,000 long, as seen in the example. This may seem impossible in 32 bit, but
    there is some seriously math cool going on here)
 
    This can be useful for a number of things, for example:
        1. Creating seed based foliage
        2. Proceedural generation
        3. Using it like a weird noise algorithm https://en.wikipedia.org/wiki/RANDU
        4. Async usage - just be careful of thread limiting locally
        5. Random number usage without affecting vanilla gameplay RNG
    
    WARNING: Plugging in random numbers will give terrible results! Refer to the numbers list in the wiki at the top for
    consistent RNG.
    
    Methods:
        1. Create (seed, multiplier, modulus, increment) - returns your new RNG table
        2. Random - returns a number between 0 and 1
        3. Real (low bound range, high bound range) - returns within the range provided
        4. Int - same as above
 
    Combined
    Functions:
        1. RandomC (RNG 1, RNG 2) - combines two RNGs and returns a number between 0 and 1
        2. RandomRealC( RNG 1, RNG 2, low bound range, high bound range) - returns within the range provided
        3. RandomIntC - same as above
*/
library RandomNumberGenerator
    struct RNG
        integer seed
        integer mult
        integer mod
        integer inc
    
        static method create takes integer newSeed, integer newMult, integer newMod, integer newInc returns RNG
            local RNG this = RNG.allocate()
            set .seed = newSeed
            set .mult = newMult
            set .mod = newMod
            set .inc = newInc
            return this
        endmethod
    
        method push takes nothing returns integer
            local integer d = .seed * .mult + .inc
            local integer m = d - (d / .mod) * .mod
            if m < 0 then
                set m = m + .mod
            endif
            set .seed = m
            return 0
        endmethod
    
        method Random takes nothing returns real
            return .push() + I2R(.seed) / .mod
        endmethod
    
        method Real takes real lowBound, real highBound returns real
            return .Random() * (highBound - lowBound) + lowBound
        endmethod
    
        method Int takes integer lowBound, integer highBound returns integer
            return MathRound(.Random() * (highBound - lowBound) + lowBound)
        endmethod
    endstruct
 
    //Combined RNG
    function RandomC takes RNG rng1, RNG rng2 returns real
        local integer d = rng1.push() + rng2.push() + rng1.seed - rng2.seed
        local integer m = IMaxBJ(rng1.mod,rng2.mod)
        local integer r = d - (d / m) * m
        if r > 0 then
            return I2R(r) / m
        elseif r < 0 then
            return I2R(r) / m + 1
        else
            return I2R(m - 1) / m
        endif
    endfunction
 
    function RandomRealC takes RNG rng1, RNG rng2, real lowBound, real highBound returns real
        return RandomC(rng1,rng2) * (highBound - lowBound) + lowBound
    endfunction
 
    function RandomIntC takes RNG rng1, RNG rng2, integer lowBound, integer highBound returns integer
        return MathRound(RandomC(rng1,rng2) * (highBound - lowBound) + lowBound)
    endfunction
endlibrary
Lua:
do
    local this, d, m
  
    RNG = {seed = 0, mult = 0, mod = 0, inc = 0}
    function RNG:new(seed, mult, mod, inc)
        local this = {}
        setmetatable(this,self)
        self.__index = self
        self.seed = seed
        self.mult = mult
        self.mod = mod
        self.inc = inc
        self.div = 1/mod
        return this
    end
    function RNG:push()
        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
    end

    function RNG:random(low,high)
        self:push()
        m = self.seed * 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
Contents

Just another Warcraft III map (Map)

Level 23
Joined
Jan 1, 2011
Messages
1,504
I fail to see why one wouldn’t just use the built-in GetRandomInt and GetRandomReal. If JASS didn’t already have those functions, then this resource would have a purpose.
Because it is difficult to maintain seed consistency. If you have combat during dungeon generation for example, the layout will be slightly different because the internal seed gets pushed. This allows complete control over the seed without effecting other areas of gameplay
 
Level 23
Joined
Jan 1, 2011
Messages
1,504
native SetRandomSeed takes integer seed returns nothing ?
I'm well aware. But like I said, anything that pushes the seed forward will interrupt whatever sequence you are in. Setting the seed before every GetRandom call would be a hassle, not only that but it allows players to use different inputs to get variations in RNG
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I'm well aware. But like I said, anything that pushes the seed forward will interrupt whatever sequence you are in. Setting the seed before every GetRandom call would be a hassle, not only that but it allows players to use different inputs to get variations in RNG
Thanks, these points are worth including in your original post. Plain language that people unfamiliar with why you created this resource can understand and see how it could be useful.
 
Level 23
Joined
Jan 1, 2011
Messages
1,504
Is this going to be immensely more useful than GetRandomInt for a triggered Crit/Evasion ability or All Random hero pick system? And do I have to create my own RNG table before calling the generation function?
1. No, it is probably just about as equally "random" as the default RNG the engine uses. It may even have a smaller period because the engine is likely using a 64 bit PRNG. Except if you are using the combined RNG functions provided, those will almost certainly have a larger period. But it doesn't really matter for most use cases. Once you get a period in excess of like a billion, anything more is just masturbatory.

2. Yes, refer to the example for how to do that.

Edit: Added a lua version (in the form post only)
 
Last edited:
In the context of most spells, the "randomness" this library provides is virtually indistinguishable from the native random number generators. From what I can deduce from the design of the system, it basically converts the global-oriented native random number operations into multi-instanceable objects that each hold their own seed values, as well as their internal multipliers, increments, and divisors. That said, I think that the constructor function for the RNG class/struct does not provide enough protection/sanitization (through documentation/code) against negative values.

While the cases for using this library are quite niche from my perspective, it might be something to be treasured by users who are into random generation of world objects.

In a nutshell, the native seed value is global and can be changed anywhere. This system provides object-oriented seed values which can be used by specific systems without interference from other systems.

EDIT:
Damn enter button
 
Last edited:
Level 23
Joined
Jan 1, 2011
Messages
1,504
In the context of most spells, the "randomness" this library provides is virtually indistinguishable from the native random number generators. From what I can deduce from the design of the system, it basically converts the global-oriented native random number operations into multi-instanceable objects that each hold their own seed values, as well as their internal multipliers, increments, and divisors. That said, I think that the constructor function for the RNG class/struct does not provide enough protection/sanitization (through documentation/code) against negative values.

While the cases for using this library are quite niche from my perspective, it might be something to be treasured by users who are into random generation of world objects.

In a nutshell, the native seed value is global and can be changed anywhere. This system provides object-oriented seed values which can be used by specific systems without interference from other systems.

EDIT:
Damn enter button
It is rather niche, you're right. But I do a ton of things locally, like sounds/timers and having a prng available is really important.

I can add sanitization; it won't affect performance if it is just in the constructor.
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
We also just remarked in Discord that GetRandomInt/GetRandomReal should not be used in local blocks as it could lead to desyncs.

And yeah, with SetRandomSeed you would basically reset your randomizer, so interleaving different randomizers using that would destroy the good stochastic properties. The use of a specific randomizer might not be restricted to one point in time, so interleaving is quite probable.

Question: How is the performance in comparison to GetRandomInt/GetRandomReal?
 
Level 23
Joined
Jan 1, 2011
Messages
1,504
We also just remarked in Discord that GetRandomInt/GetRandomReal should not be used in local blocks as it could lead to desyncs.

And yeah, with SetRandomSeed you would basically reset your randomizer, so interleaving different randomizers using that would destroy the good stochastic properties. The use of a specific randomizer might not be restricted to one point in time, so interleaving is quite probable.

Question: How is the performance in comparison to GetRandomInt/GetRandomReal?
I haven't tested performance. With lua its likely faster. But math.random() is probably faster still.
 
Top