1use rand::Rng;
2use std::fmt;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct DiceRoll {
7 pub num_dice: u32,
9 pub sides: u32,
11 pub modifier: i32,
13 pub rolls: Vec<u32>,
15 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
29pub 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
64pub fn roll_simple(sides: u32) -> u32 {
74 let mut rng = rand::thread_rng();
75 rng.gen_range(1..=sides)
76}
77
78pub fn roll_advantage(sides: u32) -> u32 {
88 let roll1 = roll_simple(sides);
89 let roll2 = roll_simple(sides);
90 roll1.max(roll2)
91}
92
93pub 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}