1use crate::filtermodifier::FilterModifier;
2use rand_core::RngCore;
3use std::num::NonZeroU64;
4
5#[derive(Debug, Clone)]
6pub struct Roll {
7 pub vals: Vec<u64>,
8 pub total: i64,
9 pub sides: NonZeroU64,
10}
11
12pub fn roll_die(
13 times: u64,
14 sides: NonZeroU64,
15 fm: FilterModifier<u64>,
16 mut rng: impl RngCore,
17) -> Roll {
18 let mut rolls = Vec::new();
19 let range = sides.get();
20 for _ in 0..times {
21 let roll = (rng.next_u64() % range) + 1;
22 rolls.push(roll);
23 }
24
25 rolls.sort_unstable();
26
27 match fm {
28 FilterModifier::KeepLowest(i) => {
29 rolls.truncate(i as usize);
30 }
31 FilterModifier::KeepHighest(i) => {
32 rolls.reverse();
33 rolls.truncate(i as usize);
34 }
35 FilterModifier::DropLowest(i) => {
36 rolls.reverse();
37 rolls.truncate(rolls.len() - i.min(rolls.len() as u64) as usize)
38 }
39 FilterModifier::DropHighest(i) => {
40 rolls.truncate(rolls.len() - i.min(rolls.len() as u64) as usize)
41 }
42 FilterModifier::None => {}
43 }
44
45 if !rolls.is_empty() {
47 let range = rolls.len() as u64;
48 for _ in 0..rolls.len() + 1 {
49 let a = rng.next_u64() % range + 1;
50 let b = rng.next_u64() % range + 1;
51 rolls.swap(a as usize - 1, b as usize - 1);
52 }
53 }
54
55 Roll {
56 total: rolls.iter().sum::<u64>() as i64,
57 vals: rolls,
58 sides,
59 }
60}
61
62const DIR: &[&str] = &[
63 "North",
64 "North East",
65 "East",
66 "South East",
67 "South",
68 "South West",
69 "West",
70 "North West",
71 "Stay",
72];
73
74pub fn roll_direction(rng: impl RngCore) -> String {
75 let value = roll_die(
76 1,
77 NonZeroU64::new(DIR.len() as u64).unwrap(),
78 FilterModifier::None,
79 rng,
80 );
81 DIR[value.total as usize - 1].to_string()
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use rand_core::{Error, RngCore};
88
89 struct DeterministicRng {
90 value: i64,
91 }
92
93 impl DeterministicRng {
94 pub fn new() -> Self {
95 Self { value: -1 }
96 }
97 }
98
99 impl RngCore for DeterministicRng {
100 fn next_u32(&mut self) -> u32 {
101 self.value += 1;
102 self.value as u32
103 }
104
105 fn next_u64(&mut self) -> u64 {
106 self.value += 1;
107 self.value as u64
108 }
109
110 fn fill_bytes(&mut self, _: &mut [u8]) {
111 unimplemented!()
112 }
113
114 fn try_fill_bytes(&mut self, _: &mut [u8]) -> Result<(), Error> {
115 unimplemented!()
116 }
117 }
118
119 #[test]
120 fn test_kl() {
121 let roll = roll_die(
122 6,
123 NonZeroU64::new(6).unwrap(),
124 FilterModifier::KeepLowest(3),
125 DeterministicRng::new(),
126 );
127
128 assert_eq!(roll.vals.len(), 3);
129 assert_eq!(roll.total, 6);
130 }
131
132 #[test]
133 fn test_dl_overflow() {
134 let roll = roll_die(
135 100,
136 NonZeroU64::new(6).unwrap(),
137 FilterModifier::DropLowest(300),
138 DeterministicRng::new(),
139 );
140 assert_eq!(roll.vals.len(), 0);
141 assert_eq!(roll.total, 0);
142 }
143}