use {
rand::Rng,
};
#[derive(Clone)]
struct Choice<T> {
weight: f64,
value: T,
}
#[derive(Clone)]
pub struct RandomSelector<T> {
choices: Vec<Choice<T>>,
total_weight: f64,
}
impl<T> Default for RandomSelector<T> {
fn default() -> Self {
Self {
choices: Vec::new(),
total_weight: 0.0,
}
}
}
impl<T> RandomSelector<T> {
pub fn with(mut self, weight: f64, value: T) -> Self {
self.choices.push(Choice { weight, value });
self.total_weight += weight.abs();
self
}
pub fn with_none(mut self, weight: f64) -> Self {
self.total_weight += weight.abs();
self
}
pub fn with_none_up_to(mut self, total_weight: f64) -> Self {
self.total_weight = total_weight.abs();
self
}
pub fn select(&self) -> Option<&T> {
let mut rng = rand::rng();
self.select_with_rng(&mut rng)
}
pub fn select_with_rng<R: Rng>(&self, mut r: R) -> Option<&T> {
if self.total_weight == 0.0 {
return None;
}
let random_value: f64 = r.random_range(0.0..self.total_weight);
let mut cumulative_weight = 0.0;
for choice in &self.choices {
cumulative_weight += choice.weight;
if random_value < cumulative_weight {
return Some(&choice.value);
}
}
None
}
}