use std::time::{SystemTime, UNIX_EPOCH};
use sha2::{Sha512, Digest};
pub struct Seed {
bytes: Vec<u8>,
index: usize,
bit: u8,
}
pub enum Roll {
Hit(u8),
Miss(u8),
}
impl Seed {
pub fn from_str(seed: &str) -> Seed {
let bytes = Seed::hash(seed);
let index = 0;
let bit = 0;
Seed {bytes, index, bit}
}
pub fn from_time() -> Result<Seed, String> {
if let Ok(unix) = SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(Seed::from_str(&unix.as_secs().to_string()))
} else {
Err("Could not read system time.".to_string())
}
}
pub fn next_bool(&mut self) -> bool {
if 7 < self.bit {
self.bit = 0;
self.index += 1;
}
if self.bytes.len() - 1 < self.index {
self.index = 0;
self.next_hash_iteration();
}
let byte = self.bytes[self.index];
let shifted = byte >> self.bit;
let new_byte = shifted & 1;
self.bit += 1;
new_byte == 1
}
pub fn next_u8(&mut self) -> u8 {
let mut num: u8 = 0;
for _ in 0..8 {
num = num << 1;
num = if self.next_bool() {
num | 1
} else {
num | 0
};
}
num
}
pub fn roll(&mut self, max: u8) -> Roll {
let roll = self.next_u8();
if roll < max {
Roll::Hit(roll)
} else {
Roll::Miss(roll)
}
}
pub fn roll_index<ElementType>(&mut self, elements: &Vec<ElementType>) -> usize {
let delimiter = 256.0 / elements.len() as f32;
let roll = self.next_u8();
let index = roll as f32 / delimiter;
index.floor() as usize
}
fn hash(seed: &str) -> Vec<u8> {
let mut hasher = Sha512::new();
hasher.update(seed);
hasher.finalize().to_vec()
}
fn next_hash_iteration(&mut self) {
let mut iteration = String::from("");
for byte in &self.bytes {
iteration.push_str(&byte.to_string());
}
self.bytes = Seed::hash(&iteration);
}
}