package bjc.utils.gen; import java.util.Random; import bjc.utils.data.experimental.IHolder; import bjc.utils.data.experimental.IPair; import bjc.utils.data.experimental.Identity; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IFunctionalList; /** * Represents a random number generator where certain results are weighted * more heavily than others. * * @author ben * * @param * The type of values that are randomly selected. */ public class WeightedRandom { /** * The list of probabilities for each result */ private IFunctionalList probabilities; /** * The list of possible results to pick from */ private IFunctionalList results; /** * The source for any needed random numbers */ private Random source; private int totalChance; /** * Create a new weighted random generator with the specified source of * randomness * * @param src * The source of randomness to use. */ public WeightedRandom(Random src) { probabilities = new FunctionalList<>(); results = new FunctionalList<>(); if (src == null) { throw new NullPointerException( "Source of randomness must not be null"); } source = src; } /** * Add a probability for a specific result to be given. * * @param chance * The chance to get this result. * @param result * The result to get when the chance comes up. */ public void addProbability(int chance, E result) { probabilities.add(chance); results.add(result); totalChance += chance; } /** * Generate a weighted random value. * * @return A random value selected in a weighted fashion. */ public E generateValue() { IHolder randomValue = new Identity<>( source.nextInt(totalChance)); IHolder currentResult = new Identity<>(); IHolder valuePicked = new Identity<>(true); probabilities.forEachIndexed((itemIndex, itemProbability) -> { if (valuePicked.unwrap(bool -> bool)) { if (randomValue .unwrap((number) -> number < itemProbability)) { currentResult.transform( (result) -> results.getByIndex(itemIndex)); valuePicked.transform((bool) -> false); } else { randomValue.transform( (number) -> number - itemProbability); } } }); return currentResult.unwrap((result) -> result); } /** * Return a list of values that can be generated by this generator * * @return A list of all the values that can be generated */ public IFunctionalList getResults() { return results; } /** * Return a list containing values that can be generated paired with * the probability of those values being generated * * @return A list of pairs of values and value probabilities */ public IFunctionalList> getValues() { return probabilities.pairWith(results); } }