hag icon indicating copy to clipboard operation
hag copied to clipboard

Create RNG module

Open JohnathonNow opened this issue 6 years ago • 6 comments

Right now many of the elements of RNG are hardcoded. I suggest a new RNG module, perhaps rng.c and rng.h.

There should be the following functions, at least:

int rng_rand(int max); //returns a random number in the range [0, max]
int rng_roll(int droprate); //returns 1 with probability 1/droprate, else 0

rng_rand should be sure to account for the limitations of rand in C. Namely, if the argument to rng_rand is greater than RAND_MAX but less than INT_MAX, it should piece together the result from multiple rand calls.

Further, rng_rand and rng_roll should both use rejection sampling to ensure that the randomness is uniform across their respective ranges. That is, rng_rand(max) should return every number in [0, max] with equal probability, and rng_roll(droprate) should always yield 1 with probability 1/droprate in the long run.

Finally, rng.h should define the constants for various probabilities.

JohnathonNow avatar Oct 11 '19 05:10 JohnathonNow

I would like to help with this

Apoorv-Gaurav-Agarwal avatar Oct 11 '19 05:10 Apoorv-Gaurav-Agarwal

What will be the constants for the probabilities. Can you provide example?

Apoorv-Gaurav-Agarwal avatar Oct 14 '19 16:10 Apoorv-Gaurav-Agarwal

Also, random number generated should be both 0 and max inclusive?

Apoorv-Gaurav-Agarwal avatar Oct 14 '19 17:10 Apoorv-Gaurav-Agarwal

Yes, it should return in the range [0, max].

JohnathonNow avatar Oct 15 '19 20:10 JohnathonNow

Can you better explain the last two paragraphs?

Pacheco95 avatar Oct 16 '19 22:10 Pacheco95

rng_rand should be sure to account for the limitations of rand in C. Namely, if the argument to rng_rand is greater than RAND_MAX but less than INT_MAX, it should piece together the result from multiple rand calls.

Say RAND_MAX is 32767, which is the lowest number it is allowed to be. Now say we want something to happen 1 in 50000 times. If we do rand() % 50000 and check if the result is 0, it will be 1 in 32767 times instead, as rand() will not return anything greater than RAND_MAX. This is sad, because wanting something that rare is perfectly reasonable, and 50000 is nowhere near the maximum size 4 bytes can get you. A common solution is to then piece together the results of rand() by using bit shifting to create a wider range of return values. This is not currently done, but is low priority, as almost everybody will be compiling with GCC, where RAND_MAX == INT_MAX.

Further, rng_rand and rng_roll should both use rejection sampling to ensure that the randomness is uniform across their respective ranges. That is, rng_rand(max) should return every number in [0, max] with equal probability, and rng_roll(droprate) should always yield 1 with probability 1/droprate in the long run.

rand() can return any value in [0, RAND_MAX] This is problematic if we wanted, say, a uniformly random number in [0, 5] (that is, one of 0, 1, 2, 3, 4, 5, or what you'd expect from a fair die roll where the die was painted by a programmer) and RAND_MAX % 6 != 5 . To see why, consider if RAND_MAX was something small, like 9 (it can't be that low, but this is just an example).

In that case, rand() could return 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. If we do the usual thing of rand() % 6 to get our number, then this is our distribution of rolls:

Roll Value Probability rand() return values
0 2 / 10 0 or 6
1 2 / 10 1 or 7
2 2 / 10 2 or 8
3 2 / 10 3 or 9
4 1 / 10 4
5 1 / 10 5

As you can see, in this case, simply moding the result of rand() by the number of elements in the range we want would lead to a biased distribution. One solution is to re-call rand if it returns something that is in that last "incomplete" section of return values, which in our case is anything above 5. Doing that, we would re-roll anytime we saw a 6 or greater, and every side of the die would have a 1/6 chance of being returned. This is what we want.

This was addressed in #110, so no need to worry about it anymore.

Finally, rng.h should define the constants for various probabilities.

There should be a header file for various constants used for things that happen with a chance. For example, item.c scales the maximum power of a weapon by 10*(floor_number + 1) - this 10 here could be a constant in rng.h such as RNG_WEAPON_POWER_SCALE.
Or how main.c checks luck against rand() % 20000 for a crit chance. That 20000 should be a constant with a name like RNG_CRIT_THRESHOLD. This is not done, and is of a sensible priority level.

JohnathonNow avatar Oct 17 '19 21:10 JohnathonNow