NAME
Role::Random::PerInstance - A role for dealing with random values, per instance
SYNOPSIS
package Some::Class;
use Moose;
with 'Role::Random::PerInstance';
# later , with an instance of Some::Class
if ( $self->random < .65 ) {
...
}
# same thing ...
if ( $self->attempt(.65) ) {
...
}
DESCRIPTION
This role allows you to use random numbers, maintaining separate random numbers for each instance.
METHODS
attempt($chance)
if ($self->attempt(0.6)) {
# 60% chance of success
}
Perform a random test which has a chance of success based on the $chance value, where $chance is a value between 0 and 1. A $chance value of 0 will always return false, and a $chance value of 1 or more will always return true.
random($min, $max, $step)
my $gain = $self->random(0.1, 0.5, 0.1 );
# $gain will contain one of 0.1, 0.2, 0.3, 0.4 or 0.5
my $even = $self->random(100, 200, 2 );
Generate a random number from $min to $max inclusive, where the resulting random number increments by a value of $step starting from $min. If step
is not supplied, this method behaves like rand
, but from $min
to $max
.
By default (if no arguments are passed), this method will work the same as the built in 'rand' function, which is to return a value from 0 to 1, but not including 1. The number includes seven digits after the decimal point (e.g., 0.5273486
).
random_seed
package Some::Package {
use Moose;
with 'Role::Random::PerInstance';
...
}
my $object = Some::Package->new(
random_seed => $integer_seed
);
If an object consuming this role passes in an integer random seed to the constructor, all "random" methods in this role will use the deterministic_rand()
method instead of the built in rand()
function.
In other words, if random_seed
is not supplied to the constructor, the random numbers will not be repeatable.
deterministic_rand
my $rand = $object->deterministic_rand;
$rand = $object->deterministic_rand;
$rand = $object->deterministic_rand;
$rand = $object->deterministic_rand;
This method returns pseudo-random numbers from 0 to 1, with up to seven digits past the decimal point (e.g., "0.1417026"), but is deterministic. This is not cryptographically secure, but the numbers are evenly distributed.
$self->random_seed
must be set in the object constructor to ensure deterministic randomness.
The algorithm is the Linear Congruential Generator.
We've tried merely calling srand(seed)
, but it turned out to not be as deterministic as promised and also doesn't allow us fine-grained "per instance" control.
random_int($min, $max)
my @items = qw(one two three four five);
my $item = $items[ $self->random_int(0, $#items) ];
Generate a random integer from $min to $max inclusive.
weighted_pick
my %weights = (
foo => 1, # 5% chance of being chosen
bar => 17, # 85% chance of being chosen
baz => 2, # 10% chance of being chosen
quux => 0, # will never be chosen
);
my $choice = $self->weighted_pick( \%weights ); # will usually return 'bar'
This function accepts a hash reference whose keys are the values you wish to choose from and whose values are the relative weights assigned to those values. A single value from the hash will be returned. The higher its "key" value, the more likely it is to be returned. Note that if you wanted an even chance of all values, ensure that all keys have the same value (but at that point, a straight rand()
would be more efficient.
BACKGROUND
The narrative sci-fi game, Tau Station, needed a way to have repeatable random numbers, with different instances of objects creating their own series of random numbers. Perl's rand function is global, and seeding it with srand turns out to not be as deterministic as we had hoped. Math::Random is also global. Hence, our own module.
Not only does this give you repeatable (via random_seed
) random numbers, it gives you non-repeatable random numbers (just don't provide a seed) and many useful random utilities.
We implemented a Linear Congruential Generator and you get seven digits after the decimal point, so each number has a 1 in ten million chance of occuring. That is perfect for our needs. It may not be perfect for yours.
Also, while the Linear Congruential Generator is fairly efficient and random, it's not cryptographically secure.
CAVEAT
Note that if $Config{use64bitint}
(from the Config module) is false, you can still get deterministic "random" numbers, but they may be slightly different than those generated by Perls compiled to use 64 bit integers. Otherwise, the output from this module is portable. See the t/random.t test for examples.
AUTHOR
Curtis "Ovid" Poe, <curtis.poe at gmail.com>
BUGS
Please report any bugs or feature requests via the Web interface at https://github.com/Ovid/role-random-perinstance/issues. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Role::Random::PerInstance
You can also look for information at:
Bug Tracker
Search CPAN
SEE ALSO
Role::Random::PerInstance
was developed for the narrative sci-fi game Tau Station. We like it because the syntax is simple, clear, and intuitive (to us). However, there are a few alternatives on the CPAN that you might find useful:
ACKNOWLEDGEMENTS
This code was written to help reduce the complexity of the narrative sci-fi adventure, Tau Station. As of this writing, it's around 1/3 of a million lines of code (counting front-end, back-end, tests, etc.), and anything to reduce that complexity is a huge win.
LICENSE AND COPYRIGHT
This software is Copyright (c) 2019 by Curtis "Ovid" Poe.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)