[Feature] Add `random()` method to `PRNG`
Closed this issue · 2 comments
Feature Description and Rationale
Is your feature request related to a problem? Please describe.
Currently, using pokemaster.prng.PRNG
to generate a random number requires manual calculations. This is error-prone. For example, in the following code, what I've written generates a random number between [0, 1] inclusive on both ends:
pokemaster/pokemaster/_database.py
Lines 146 to 152 in cc6e16c
Whereas random functions normally generate numbers between [0, 1), a left-close-right-open range.
Describe the solution you'd like
Adding a random()
method to pokemaster.prng.PRNG
class will make getting random numbers with that PRNG much easier.
We can also add:
PRNG().choice(items)
randomly selects an item from items;PRNG().shuffle(items)
returns the elements in items but in random order;PRNG().randint(a, b)
returns a random integer between a and b.
and more functions from the random
standard library, if needed.
Usage Example
>>> from pokemaster.prng import PRNG
>>> prng = PRNG()
>>> prng.random()
0.0
>>> prng.random()
0.912078857421875
>>> prng.uniform(0.85, 1.0)
0.87911376953125
Suggested Implementation/Change
In pokemater.prng
, add the following methods to PRNG
class:
class PRNG:
...
def random(self) -> float:
"""Return a random number from the uniform distribution [0, 1)."""
return self.next() / 0x10000
def uniform(
self, min: Union[int, float] = None, max: Union[int, float] = None
) -> float:
"""Return a random number from the uniform distribution [min, max)
Usage::
PRNG.uniform() -> a random number between [0, 1)
PRNG.uniform(n) -> a random number between [0, n)
PRNG.uniform(m, n) -> a random number between [m, n)
"""
if min is not None and not isinstance(min, Real):
raise TypeError(f"'min' must be an int or a float.")
if max is not None and not isinstance(max, Real):
raise TypeError(f"'max' must be an int or a float.")
if min is None and max is None:
# PRNG.uniform() -> [0, 1)
return self.random()
elif (min is not None and max is None) or (
min is None and max is not None
):
# PRNG.uniform(n) -> [0, n)
cap = min or max
return self.random() * cap
else:
# PRNG.uniform(m, n) -> [m, n)
if max <= min:
raise ValueError("'max' must be strictly greater than 'min'.")
return self.random() * (max - min) + min
PRNG().uniform()
is especially useful for calculating the random part of the damage modifier.
Release 0.2.1 fixes this.