Search code examples
nearprotocolnear-sdk-as

How can I make sure my RNG numbers are unique?


I'm trying to select 2 random items out of a list using the RNG class. The problem is occasionally I get the same 2 numbers and I'd like them to be unique. I tried using a while loop to get another number if the it's the same as the last one but adding even a simple while loop results in an "Exceeded prepaid gas" error. What am I not understanding?

//simplified for posting question
var lengthOfList = 10
var numItemsWanted = 2
//Get more rng numbers than I need incase of duplicates
const rng = new RNG<u32>(lenghtOfList, lengthOfList)

for(let i = 0; i < numItemsWanted; i++) {
    var r = rng.next()
    while (r == rng.last()) {
        r = rng.next()
    }
    newList.push(oldList[r])
}

Working:

//simplified for posting question
var lengthOfList = 10
var numItemsWanted = 2
//Get more rng numbers than I need incase of duplicates
const rng = new RNG<u32>(lenghtOfList, lengthOfList)
let r = rng.next()
let last = r + 1
for(let i = 0; i < numItemsWanted; i++) {
    newList.push(oldList[r])
    last = r
    r = rng.next()
    while (r == last) {
        r = rng.next()
    }
}


Solution

  • this is about near-sdk-as, the smart contract development kit for AssemblyScript on the NEAR platform

    you can see how RNG is used in this example https://github.com/Learn-NEAR/NCD.L1.sample--lottery/blob/ff6cddaa8cac4d8fe29dd1a19b38a6e3c7045363/src/lottery/assembly/lottery.ts#L12-L13

    class Lottery {
      private chance: f64 = 0.20
    
      play(): bool {
        const rng = new RNG<u32>(1, u32.MAX_VALUE);
        const roll = rng.next();
        logging.log("roll: " + roll.toString());
        return roll <= <u32>(<f64>u32.MAX_VALUE * this.chance);
      }
    }
    

    and how the constructor is implemented here: https://github.com/near/near-sdk-as/blob/f3707a1672d6da6f6d6a75cd645f8cbdacdaf495/sdk-core/assembly/math.ts#L152

    the first argument is the length of the buffer holding random numbers generated from the seed. you can use the next() method to get more numbers from this buffer with each call

    export class RNG<T> {
      constructor(len: u32, public max: u32 = 10_000) {
        let real_len = len * sizeof<T>();
        this.buffer = math.randomBuffer(real_len);
        this._last = this.get(0);
      }
    
      next(): T {}
    }