Smart Contract
PRNG
A.93615d25d14fa337.PRNG
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}