Smart Contract

PRNG

A.93615d25d14fa337.PRNG

Deployed

1d ago
Feb 26, 2026, 09:44:01 PM UTC

Dependents

0 imports
1/*
2    PRNG - A Pseudo-Random Number Generator 
3
4    Usage: 
5        let myPRNG = PNG.create(seed: UInt256) // creates a random number generator resource that provides functions for generating a stream of random numbers
6        let myPRNG = PNG.createFrom(blockHeight: UInt64, uuid: UInt64): // creates a random number generator resource seeded with a block height and uuid of a resource 
7
8        myPRNG.ufix64() // generates a random UFix64 number between 0 and 1
9
10        myPRNG.range(1,6) // dice roll
11        myPRNG.pickWeighted(['heads',tails'], weights:[50,50])
12        
13
14        to generate a random number
15 */ 
16
17
18pub contract PRNG {
19
20    pub resource Generator {
21        access(self) var seed: UInt256
22        
23        init (seed: UInt256) {
24            self.seed=seed
25            // create some inital entropy
26            self.g();
27            self.g();
28            self.g();
29        }
30
31        pub fun generate(): UInt256 { 
32            return self.g() 
33        }
34
35        pub fun g(): UInt256 {
36            self.seed = PRNG.random(seed: self.seed)
37            return self.seed
38        }
39
40        pub fun ufix64(): UFix64 { 
41            let s: UInt256 = self.g()
42            return UFix64(s / UInt256.max)
43        }
44
45        pub fun range(_ min :UInt256, _ max: UInt256): UInt256 {
46            return min + (self.g() % (max - min+ 1))
47        }
48
49        pub fun pickWeighted(_ choices: [AnyStruct], _ weights: [UInt256]): AnyStruct {
50            var weightsRange: [UInt256] = []
51            var totalWeight: UInt256 = 0     
52            for weight in weights {
53                totalWeight = totalWeight + weight
54                weightsRange.append(totalWeight)
55            } 
56            let p = self.g() % totalWeight
57            var lastWeight: UInt256 = 0
58
59            for i, choice in choices {    
60                if p >= lastWeight && p < weightsRange[i] {    
61                    // log("Picked Number: ".concat(p.toString()).concat("/".concat(totalWeight.toString())).concat(" corresponding to ".concat(i.toString())))
62                    return choice 
63                }
64                lastWeight = weightsRange[i]
65            }
66            return nil
67        }
68    
69    }
70
71    pub fun create(seed: UInt256): @Generator {
72        return <- create Generator(seed: seed)
73    }
74
75    // creates a rng seeded from blockheight salted with hash of a resource uuid (or any UInt64 value)
76    // can be used to define traits based on a future block height etc.  
77    pub fun createFrom(blockHeight: UInt64, uuid: UInt64): @Generator {
78        let hash = getBlock(at: blockHeight)!.id
79        let h: [UInt8] = HashAlgorithm.SHA3_256.hash(uuid.toBigEndianBytes())
80        var seed = 0 as UInt256
81        let hex : [UInt64] = []
82        for byte,i in hash {
83            let xor = (UInt64(byte) ^ UInt64(h[i%32]))
84            seed = seed << 2
85            seed = seed + UInt256(xor)
86            hex.append(xor)
87        }
88        return <- self.create(seed: seed)
89    }
90
91    pub fun random(seed: UInt256): UInt256 {
92        return self.lcg(modulus: 4294967296, a: 1664525, c: 1013904223, seed: seed)
93    }
94
95    pub fun lcg(modulus: UInt256, a: UInt256, c: UInt256, seed: UInt256): UInt256 {
96        return (a * seed + c) % modulus
97    }
98}