veloren/common/src/lottery.rs

70 lines
1.9 KiB
Rust
Raw Normal View History

use crate::assets::{self, Asset};
2020-07-03 13:49:17 +00:00
use rand::prelude::*;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
2020-07-03 13:49:17 +00:00
use std::{fs::File, io::BufReader};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
2020-07-03 13:49:17 +00:00
pub struct Lottery<T> {
items: Vec<(f32, T)>,
2020-07-03 13:49:17 +00:00
total: f32,
}
impl<T: DeserializeOwned + Send + Sync> Asset for Lottery<T> {
2020-07-03 13:49:17 +00:00
const ENDINGS: &'static [&'static str] = &["ron"];
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
ron::de::from_reader::<BufReader<File>, Vec<(f32, T)>>(buf_reader)
.map(|items| Lottery::from_rates(items.into_iter()))
.map_err(assets::Error::parse_error)
2020-07-03 13:49:17 +00:00
}
}
impl<T> Lottery<T> {
pub fn from_rates(items: impl Iterator<Item = (f32, T)>) -> Self {
2020-07-03 13:49:17 +00:00
let mut total = 0.0;
let items = items
.map(|(rate, item)| {
2020-07-03 13:49:17 +00:00
total += rate;
(total - rate, item)
})
.collect();
Self { items, total }
}
pub fn choose_seeded(&self, seed: u32) -> &T {
let x = ((seed % 65536) as f32 / 65536.0) * self.total;
&self.items[self
.items
.binary_search_by(|(y, _)| y.partial_cmp(&x).unwrap())
.unwrap_or_else(|i| i.saturating_sub(1))]
.1
2020-07-03 13:49:17 +00:00
}
pub fn choose(&self) -> &T {
self.choose_seeded(thread_rng().gen())
}
pub fn iter(&self) -> impl Iterator<Item = &(f32, T)> { self.items.iter() }
}
#[cfg(test)]
mod tests {
use crate::{
assets,
comp::inventory::item::{lottery::Lottery, Item},
};
#[test]
fn test_loot_table() {
let test = assets::load_expect::<Lottery<_>>("common.loot_table");
let test = test;
for (_, item) in test.iter() {
assert!(
assets::load::<Item>(item).is_ok(),
"Invalid loot table item '{}'",
item
);
}
}
}