dice_roller_rs/
lib.rs

1use rand::Rng;
2use std::fmt;
3
4/// Represents a dice roll result
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct DiceRoll {
7    /// The number of dice rolled
8    pub num_dice: u32,
9    /// The number of sides on each die
10    pub sides: u32,
11    /// The modifier added to the total
12    pub modifier: i32,
13    /// Individual roll results
14    pub rolls: Vec<u32>,
15    /// Total result including modifier
16    pub total: i32,
17}
18
19impl fmt::Display for DiceRoll {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        write!(
22            f,
23            "{}d{}{:+}: {:?} = {}",
24            self.num_dice, self.sides, self.modifier, self.rolls, self.total
25        )
26    }
27}
28
29/// Roll dice with the format NdS+M (e.g., 2d6+3)
30/// 
31/// # Arguments
32/// * `num_dice` - Number of dice to roll
33/// * `sides` - Number of sides on each die
34/// * `modifier` - Modifier to add to the total (can be negative)
35/// 
36/// # Examples
37/// ```
38/// use dice_roller_rs::roll;
39/// 
40/// // Roll 2d6+3
41/// let result = roll(2, 6, 3);
42/// assert_eq!(result.num_dice, 2);
43/// assert_eq!(result.sides, 6);
44/// assert!(result.total >= 5 && result.total <= 15);
45/// ```
46pub fn roll(num_dice: u32, sides: u32, modifier: i32) -> DiceRoll {
47    let mut rng = rand::thread_rng();
48    let rolls: Vec<u32> = (0..num_dice)
49        .map(|_| rng.gen_range(1..=sides))
50        .collect();
51    
52    let sum: u32 = rolls.iter().sum();
53    let total = sum as i32 + modifier;
54    
55    DiceRoll {
56        num_dice,
57        sides,
58        modifier,
59        rolls,
60        total,
61    }
62}
63
64/// Roll a simple die (e.g., d20, d6)
65/// 
66/// # Examples
67/// ```
68/// use dice_roller_rs::roll_simple;
69/// 
70/// let result = roll_simple(20);
71/// assert!(result >= 1 && result <= 20);
72/// ```
73pub fn roll_simple(sides: u32) -> u32 {
74    let mut rng = rand::thread_rng();
75    rng.gen_range(1..=sides)
76}
77
78/// Roll with advantage (roll twice, take higher)
79/// 
80/// # Examples
81/// ```
82/// use dice_roller_rs::roll_advantage;
83/// 
84/// let result = roll_advantage(20);
85/// assert!(result >= 1 && result <= 20);
86/// ```
87pub fn roll_advantage(sides: u32) -> u32 {
88    let roll1 = roll_simple(sides);
89    let roll2 = roll_simple(sides);
90    roll1.max(roll2)
91}
92
93/// Roll with disadvantage (roll twice, take lower)
94/// 
95/// # Examples
96/// ```
97/// use dice_roller_rs::roll_disadvantage;
98/// 
99/// let result = roll_disadvantage(20);
100/// assert!(result >= 1 && result <= 20);
101/// ```
102pub fn roll_disadvantage(sides: u32) -> u32 {
103    let roll1 = roll_simple(sides);
104    let roll2 = roll_simple(sides);
105    roll1.min(roll2)
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_roll_basic() {
114        let result = roll(2, 6, 0);
115        assert_eq!(result.num_dice, 2);
116        assert_eq!(result.sides, 6);
117        assert_eq!(result.modifier, 0);
118        assert_eq!(result.rolls.len(), 2);
119        assert!(result.total >= 2 && result.total <= 12);
120    }
121
122    #[test]
123    fn test_roll_with_modifier() {
124        let result = roll(1, 20, 5);
125        assert_eq!(result.num_dice, 1);
126        assert_eq!(result.sides, 20);
127        assert_eq!(result.modifier, 5);
128        assert!(result.total >= 6 && result.total <= 25);
129    }
130
131    #[test]
132    fn test_roll_with_negative_modifier() {
133        let result = roll(1, 6, -2);
134        assert!(result.total >= -1 && result.total <= 4);
135    }
136
137    #[test]
138    fn test_roll_simple() {
139        for _ in 0..100 {
140            let result = roll_simple(20);
141            assert!(result >= 1 && result <= 20);
142        }
143    }
144
145    #[test]
146    fn test_roll_advantage() {
147        for _ in 0..100 {
148            let result = roll_advantage(20);
149            assert!(result >= 1 && result <= 20);
150        }
151    }
152
153    #[test]
154    fn test_roll_disadvantage() {
155        for _ in 0..100 {
156            let result = roll_disadvantage(20);
157            assert!(result >= 1 && result <= 20);
158        }
159    }
160}