use crate::core::{Dice, DiceContext, DieRoll, RollResult};
use crate::error::{DiceError, DiceResult};
use rand::RngExt;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub struct FateDice {
pub count: u32,
}
impl FateDice {
pub fn new(count: u32) -> Self {
Self { count }
}
pub fn validate(&self) -> DiceResult<()> {
if self.count == 0 {
return Err(DiceError::InvalidDice("Must roll at least 1 die".into()));
}
Ok(())
}
}
impl Dice for FateDice {
fn roll(&self) -> RollResult {
self.validate().unwrap();
let mut ctx = DiceContext::new();
let mut rolls = Vec::with_capacity(self.count as usize);
let mut total = 0i64;
for _ in 0..self.count {
let roll_value = ctx.rng.random_range(1..=3);
let fate_value = match roll_value {
1 => -1,
2 => 0,
3 => 1,
_ => 0, };
let roll = DieRoll::new(roll_value, 3);
rolls.push(roll);
total += fate_value as i64;
}
let description = if self.count == 4 {
"4dF".to_string()
} else {
format!("{}dF", self.count)
};
RollResult::new(rolls, total, description)
}
fn describe(&self) -> String {
if self.count == 4 {
"4dF (standard FATE dice)".to_string()
} else {
format!("{}dF (FATE dice)", self.count)
}
}
fn expected_value(&self) -> f64 {
0.0
}
fn min_value(&self) -> i64 {
-(self.count as i64)
}
fn max_value(&self) -> i64 {
self.count as i64
}
}
impl fmt::Display for FateDice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.describe())
}
}