1use crate::dice_types::*;
18use rand::{distributions::Uniform, Rng};
19use std::convert::TryInto;
20
21#[cfg(feature = "logging")]
22use log::debug;
23
24#[derive(Debug, PartialEq, Eq)]
25pub enum EvaluationErrors {
26 DivideByZero,
27 Timeout,
28 Overflow,
29}
30
31impl std::fmt::Display for EvaluationErrors{
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 write!(f,"{}",match *self {
34 Self::DivideByZero=>"division by zero occurred",
35 Self::Timeout=>"timeout reached",
36 Self::Overflow=>"overflow occurred"
37 })
38 }
39}
40
41impl std::error::Error for EvaluationErrors{
42 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
43 None
44 }
45
46 fn cause(&self) -> Option<&dyn std::error::Error> {
47 self.source()
48 }
49}
50
51pub trait DiceEvaluate {
52 fn evaluate<T: FnMut() -> bool, R: Rng>(
53 &self,
54 timeout_f: &mut T,
55 rng: &mut R,
56 ) -> Result<(Vec<i64>, Vec<i64>), EvaluationErrors>;
57}
58
59impl DiceEvaluate for Dice {
60 fn evaluate<T: FnMut() -> bool, R: Rng>(
61 &self,
62 timeout_f: &mut T,
63 rng: &mut R,
64 ) -> Result<(Vec<i64>, Vec<i64>), EvaluationErrors> {
65 if timeout_f() {
66 return Err(EvaluationErrors::Timeout);
67 }
68 let mut rolls: Vec<i64> = Vec::with_capacity(self.throws.try_into().unwrap());
69 let mut roll_counter: u8 = 0;
70 match self.dice {
71 DiceType::Number(faces) => {
72 let dist = Uniform::new_inclusive(1, faces as i64);
73 for _ in 0..self.throws {
74 roll_counter = roll_counter.wrapping_add(1);
75 if roll_counter == 0 && timeout_f() {
76 return Err(EvaluationErrors::Timeout);
77 }
78 rolls.push(rng.sample::<i64, _>(dist));
79 }
80 }
81 DiceType::Fudge => {
82 let dist: Uniform<i64> = Uniform::new_inclusive(-1, 1);
83 for _ in 0..self.throws {
84 roll_counter = roll_counter.wrapping_add(1);
85 if roll_counter == 0 && timeout_f() {
86 return Err(EvaluationErrors::Timeout);
87 }
88 rolls.push(rng.sample(dist));
89 }
90 }
91 DiceType::Multiply(base_faces) => {
92 let dist = Uniform::new_inclusive(1, base_faces as i64);
93 for _ in 0..self.throws {
94 roll_counter = roll_counter.wrapping_add(1);
95 if roll_counter == 0 && timeout_f() {
96 return Err(EvaluationErrors::Timeout);
97 }
98 rolls.push(
99 rng.sample(dist)
100 .checked_mul(rng.sample(dist))
101 .ok_or(EvaluationErrors::Overflow)?,
102 );
103 }
104 }
105 }
106
107 #[cfg(feature = "logging")]
108 {
109 debug!("Dice roll result for {} is {:?}", &self, &rolls);
110 }
111
112 let rolls_copy = rolls.clone();
113 Ok((rolls, rolls_copy))
114 }
115}
116
117impl DiceEvaluate for FilteredDice {
118 fn evaluate<T: FnMut() -> bool, R: Rng>(
119 &self,
120 timeout_f: &mut T,
121 rng: &mut R,
122 ) -> Result<(Vec<i64>, Vec<i64>), EvaluationErrors> {
123 let result = match self {
124 FilteredDice::Simple(dice) => dice.evaluate(timeout_f, rng),
125 FilteredDice::Filtered(dice, filter, target) => {
126 dice.evaluate(timeout_f, rng).map(|original| {
127 (
128 original
129 .0
130 .into_iter()
131 .filter(match filter {
132 Filter::Bigger => {
133 Box::new(|i: &i64| i > &(target.to_owned() as i64))
134 as Box<dyn Fn(&i64) -> bool>
135 }
136 Filter::BiggerEq => {
137 Box::new(|i: &i64| i > &(target.to_owned() as i64))
138 as Box<dyn Fn(&i64) -> bool>
139 }
140 Filter::Smaller => {
141 Box::new(|i: &i64| i < &(target.to_owned() as i64))
142 as Box<dyn Fn(&i64) -> bool>
143 }
144 Filter::SmallerEq => {
145 Box::new(|i: &i64| i <= &(target.to_owned() as i64))
146 as Box<dyn Fn(&i64) -> bool>
147 }
148 Filter::NotEq => {
149 Box::new(|i: &i64| i != &(target.to_owned() as i64))
150 as Box<dyn Fn(&i64) -> bool>
151 }
152 })
153 .collect(),
154 original.1,
155 )
156 })
157 }
158 };
159 #[cfg(feature = "logging")]
160 {
161 debug!("rolled {:?} for filtered dice {}", &result, &self)
162 }
163 result
164 }
165}
166
167impl DiceEvaluate for SelectedDice {
168 fn evaluate<T: FnMut() -> bool, R: Rng>(
169 &self,
170 timeout_f: &mut T,
171 rng: &mut R,
172 ) -> Result<(Vec<i64>, Vec<i64>), EvaluationErrors> {
173 let result = match self {
174 SelectedDice::Unchanged(dice) => dice.evaluate(timeout_f, rng),
175 SelectedDice::Selected(dice, selector, max_size) => {
176 dice.evaluate(timeout_f, rng)
177 .map(|original: (Vec<i64>, Vec<i64>)| {
178 if original.0.len() > max_size.to_owned() as usize {
179 let range = match selector {
180 Selector::Higher => {
181 (original.0.len() - max_size.to_owned() as usize)
182 ..original.0.len()
183 }
184 Selector::Lower => (0..(max_size.to_owned() as usize)),
185 };
186 let mut source = original;
187 source.0.sort_unstable();
188 (source.0[range].to_vec(), source.1)
189 } else {
190 original
191 }
192 })
193 }
194 };
195 #[cfg(feature = "logging")]
196 {
197 debug!("rolled {:?} for selected dice {}", &result, &self)
198 }
199 result
200 }
201}
202
203pub trait TermEvaluate {
204 fn evaluate<T: FnMut() -> bool, R: Rng>(
205 &self,
206 timeout_f: &mut T,
207 rng: &mut R,
208 ) -> Result<(i64, Vec<i64>), EvaluationErrors>;
209}
210
211impl TermEvaluate for Term {
212 fn evaluate<T: FnMut() -> bool, R: Rng>(
213 &self,
214 timeout_f: &mut T,
215 rng: &mut R,
216 ) -> Result<(i64, Vec<i64>), EvaluationErrors> {
217 let result = match self {
218 Term::Constant(i) => Ok((i.to_owned(), Vec::new())),
219 Term::DiceThrow(dice) => dice.evaluate(timeout_f, rng).map(|roll_results| {
220 (
221 roll_results.0.into_iter().reduce(|a, b| a + b).unwrap_or(0),
222 roll_results.1,
223 )
224 }),
225 Term::SubTerm(term) => term.evaluate(timeout_f, rng),
226 Term::Calculation(left, op, right) => {
227 let left_r = left.evaluate(timeout_f, rng)?;
228 let right_r = right.evaluate(timeout_f, rng)?;
229 let result = match op {
230 Operation::Add => left_r
231 .0
232 .checked_add(right_r.0)
233 .ok_or(EvaluationErrors::Overflow),
234 Operation::Sub => left_r
235 .0
236 .checked_sub(right_r.0)
237 .ok_or(EvaluationErrors::Overflow),
238 Operation::Mul => left_r
239 .0
240 .checked_mul(right_r.0)
241 .ok_or(EvaluationErrors::Overflow),
242 Operation::Div => left_r
243 .0
244 .checked_div(right_r.0)
245 .ok_or(EvaluationErrors::DivideByZero),
246 }?;
247 Ok((result, [left_r.1, right_r.1].concat()))
248 }
249 };
250 #[cfg(feature = "logging")]
251 {
252 debug!("got {:?} for term {}", &result, &self)
253 }
254 result
255 }
256}
257
258impl TermEvaluate for Box<Term> {
259 fn evaluate<T: FnMut() -> bool, R: Rng>(
260 &self,
261 timeout_f: &mut T,
262 rng: &mut R,
263 ) -> Result<(i64, Vec<i64>), EvaluationErrors> {
264 self.as_ref().evaluate(timeout_f, rng)
265 }
266}
267
268pub trait ExpressionEvaluate {
269 fn evaluate<T: FnMut() -> bool, R: Rng>(
270 &self,
271 timeout_t: &mut T,
272 rng: &mut R,
273 ) -> Result<Vec<(i64, Vec<i64>)>, EvaluationErrors>;
274}
275
276impl ExpressionEvaluate for Expression {
277 fn evaluate<T: FnMut() -> bool, R: Rng>(
278 &self,
279 timeout_f: &mut T,
280 rng: &mut R,
281 ) -> Result<Vec<(i64, Vec<i64>)>, EvaluationErrors> {
282 match self {
283 Expression::Simple(term) => term.evaluate(timeout_f, rng).map(|res| vec![res]),
284 Expression::List(count, term) => {
285 let size: usize = (*count).try_into().expect("failed to convert u32 to usize");
286 let mut result_collector: Vec<(i64, Vec<i64>)> = Vec::with_capacity(size);
287 for _ in 0..size {
288 result_collector.push(term.evaluate(timeout_f, rng)?);
289 }
290 Ok(result_collector)
291 }
292 }
293 }
294}