"""
Usually it's recommend to the Python's standard library `random` module
instead of this one.
However, you will need to use these generators to get deterministic results
from the :any:`Noise` and :any:`BSP` classes.
"""
import random
from typing import Any, Hashable, Optional
import tcod.constants
from tcod.loader import ffi, lib
MERSENNE_TWISTER = tcod.constants.RNG_MT
COMPLEMENTARY_MULTIPLY_WITH_CARRY = tcod.constants.RNG_CMWC
MULTIPLY_WITH_CARRY = tcod.constants.RNG_CMWC
[docs]class Random(object):
"""The libtcod random number generator.
`algorithm` defaults to Mersenne Twister, it can be one of:
* tcod.random.MERSENNE_TWISTER
* tcod.random.MULTIPLY_WITH_CARRY
`seed` is a 32-bit number or any Python hashable object like a string.
Using the same seed will cause the generator to return deterministic
values. The default `seed` of None will generate a random seed instead.
Attributes:
random_c (CData): A cffi pointer to a TCOD_random_t object.
.. versionchanged:: 9.1
Added `tcod.random.MULTIPLY_WITH_CARRY` constant.
`algorithm` parameter now defaults to `tcod.random.MERSENNE_TWISTER`.
"""
def __init__(
self,
algorithm: int = MERSENNE_TWISTER,
seed: Optional[Hashable] = None,
):
"""Create a new instance using this algorithm and seed."""
if seed is None:
seed = random.getrandbits(32)
self.random_c = ffi.gc(
ffi.cast(
"mersenne_data_t*",
lib.TCOD_random_new_from_seed(
algorithm, hash(seed) % (1 << 32)
),
),
lib.TCOD_random_delete,
)
@classmethod
def _new_from_cdata(cls, cdata: Any) -> "Random":
"""Return a new instance encapsulating this cdata."""
self = object.__new__(cls) # type: "Random"
self.random_c = cdata
return self
[docs] def randint(self, low: int, high: int) -> int:
"""Return a random integer within the linear range: low <= n <= high.
Args:
low (int): The lower bound of the random range.
high (int): The upper bound of the random range.
Returns:
int: A random integer.
"""
return int(lib.TCOD_random_get_i(self.random_c, low, high))
[docs] def guass(self, mu: float, sigma: float) -> float:
"""Return a random number using Gaussian distribution.
Args:
mu (float): The median returned value.
sigma (float): The standard deviation.
Returns:
float: A random float.
"""
return float(
lib.TCOD_random_get_gaussian_double(self.random_c, mu, sigma)
)
[docs] def inverse_guass(self, mu: float, sigma: float) -> float:
"""Return a random Gaussian number using the Box-Muller transform.
Args:
mu (float): The median returned value.
sigma (float): The standard deviation.
Returns:
float: A random float.
"""
return float(
lib.TCOD_random_get_gaussian_double_inv(self.random_c, mu, sigma)
)
[docs] def __getstate__(self) -> Any:
"""Pack the self.random_c attribute into a portable state."""
state = self.__dict__.copy()
state["random_c"] = {
"algo": self.random_c.algo,
"distribution": self.random_c.distribution,
"mt": list(self.random_c.mt),
"cur_mt": self.random_c.cur_mt,
"Q": list(self.random_c.Q),
"c": self.random_c.c,
"cur": self.random_c.cur,
}
return state
[docs] def __setstate__(self, state: Any) -> None:
"""Create a new cdata object with the stored paramaters."""
try:
cdata = state["random_c"]
except KeyError: # old/deprecated format
cdata = state["cdata"]
del state["cdata"]
state["random_c"] = ffi.new("mersenne_data_t*", cdata)
self.__dict__.update(state)