From 087e20c261cff599aa735d7b16be9ae1137ee949 Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Mon, 20 Apr 2020 13:44:18 -0600 Subject: [PATCH] get roller working correctly --- src/bin/dicebot-roll.rs | 14 ++- src/dice/parser.rs | 5 +- src/roll.rs | 231 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 230 insertions(+), 20 deletions(-) diff --git a/src/bin/dicebot-roll.rs b/src/bin/dicebot-roll.rs index 6eefa52..2a6b4b1 100644 --- a/src/bin/dicebot-roll.rs +++ b/src/bin/dicebot-roll.rs @@ -1,9 +1,13 @@ +use axfive_matrix_dicebot::dice::parser::parse_element_expression; +use axfive_matrix_dicebot::roll::{Roll, Rolled}; use std::error::Error; -fn main() -> Result<(), Box> { - let _roll_string = std::env::args().skip(1).collect::>().join(" "); - // first regex needs to be different because the sign is mandatory for the rest - //let expression = parse_expression(&roll_string); - //println!("{:?}", expression); +fn main() -> Result<(), String> { + let roll_string = std::env::args().skip(1).collect::>().join(" "); + let (_tail, expression) = match parse_element_expression(&roll_string) { + Ok(response) => response, + Err(e) => return Err(e.to_string()), + }; + println!("{}", expression.roll()); Ok(()) } diff --git a/src/dice/parser.rs b/src/dice/parser.rs index 7a1a672..46efe4d 100644 --- a/src/dice/parser.rs +++ b/src/dice/parser.rs @@ -7,7 +7,7 @@ use nom::{ tag, IResult, }; -use crate::dice::{Dice, Element, SignedElement, ElementExpression}; +use crate::dice::{Dice, Element, ElementExpression, SignedElement}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] enum Sign { @@ -78,7 +78,7 @@ fn parse_signed_element(input: &str) -> IResult<&str, SignedElement> { } // Parse a full element expression. Eats whitespace. -fn parse_element_expression(input: &str) -> IResult<&str, ElementExpression> { +pub fn parse_element_expression(input: &str) -> IResult<&str, ElementExpression> { named!(first_element(&str) -> SignedElement, alt!( parse_signed_element => { |e| e } | parse_element => { |e| SignedElement::Positive(e) } @@ -180,4 +180,3 @@ mod tests { ); } } - diff --git a/src/roll.rs b/src/roll.rs index 3738bd8..7c5a033 100644 --- a/src/roll.rs +++ b/src/roll.rs @@ -1,5 +1,8 @@ use rand::prelude::*; +use std::ops::{Deref, DerefMut}; use crate::dice; +use std::fmt; +use std::string::ToString; pub trait Roll { type Output; @@ -7,33 +10,237 @@ pub trait Roll { fn roll(&self) -> Self::Output; } -impl Roll for dice::Dice { - type Output = u32; +pub trait Rolled { + fn rolled_value(&self) -> i32; +} - fn roll(&self) -> u32 { +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct DiceRoll(Vec); + +impl DiceRoll { + pub fn rolls(&self) -> &[u32] { + &self.0 + } + + pub fn total(&self) -> u32 { + self.0.iter().sum() + } +} + +impl Rolled for DiceRoll { + fn rolled_value(&self) -> i32 { + self.total() as i32 + } +} + +impl fmt::Display for DiceRoll { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.rolled_value())?; + let rolls = self.rolls(); + let mut iter = rolls.iter(); + if let Some(first) = iter.next() { + write!(f, " ({}", first)?; + for roll in iter { + write!(f, " + {}", roll)?; + } + write!(f, ")")?; + } + Ok(()) + } +} + +impl Roll for dice::Dice { + type Output = DiceRoll; + + fn roll(&self) -> DiceRoll { let mut rng = rand::thread_rng(); - (0..self.count).map(|_| rng.gen_range(1, self.sides + 1)).sum() + let rolls = (0..self.count).map(|_| rng.gen_range(1, self.sides + 1)).collect(); + DiceRoll(rolls) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ElementRoll { + Dice(DiceRoll), + Bonus(u32), +} + +impl Rolled for ElementRoll { + fn rolled_value(&self) -> i32 { + match self { + ElementRoll::Dice(d) => d.rolled_value(), + ElementRoll::Bonus(b) => *b as i32, + } } } impl Roll for dice::Element { - type Output = u32; + type Output = ElementRoll; - fn roll(&self) -> u32 { + fn roll(&self) -> ElementRoll { match self { - dice::Element::Dice(d) => d.roll(), - dice::Element::Bonus(b) => *b, + dice::Element::Dice(d) => ElementRoll::Dice(d.roll()), + dice::Element::Bonus(b) => ElementRoll::Bonus(*b), + } + } +} + +impl fmt::Display for ElementRoll { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ElementRoll::Dice(d) => write!(f, "{}", d), + ElementRoll::Bonus(b) => write!(f, "{}", b), + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum SignedElementRoll { + Positive(ElementRoll), + Negative(ElementRoll), +} + +impl Rolled for SignedElementRoll { + fn rolled_value(&self) -> i32 { + match self { + SignedElementRoll::Positive(e) => e.rolled_value(), + SignedElementRoll::Negative(e) => -e.rolled_value(), } } } impl Roll for dice::SignedElement { - type Output = i32; + type Output = SignedElementRoll; - fn roll(&self) -> i32 { + fn roll(&self) -> SignedElementRoll { match self { - dice::SignedElement::Positive(e) => e.roll() as i32, - dice::SignedElement::Negative(e) => -(e.roll() as i32), + dice::SignedElement::Positive(e) => SignedElementRoll::Positive(e.roll()), + dice::SignedElement::Negative(e) => SignedElementRoll::Negative(e.roll()), } } } + +impl fmt::Display for SignedElementRoll { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SignedElementRoll::Positive(e) => write!(f, "{}", e), + SignedElementRoll::Negative(e) => write!(f, "-{}", e), + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct ElementExpressionRoll(Vec); + +impl Deref for ElementExpressionRoll { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ElementExpressionRoll { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Rolled for ElementExpressionRoll { + fn rolled_value(&self) -> i32 { + self.iter().map(Rolled::rolled_value).sum() + } +} + +impl Roll for dice::ElementExpression { + type Output = ElementExpressionRoll; + + fn roll(&self) -> ElementExpressionRoll { + ElementExpressionRoll(self.iter().map(Roll::roll).collect()) + } +} + +impl fmt::Display for ElementExpressionRoll { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.len() > 1 { + write!(f, "{}", self.rolled_value())?; + let mut iter = self.0.iter(); + if let Some(first) = iter.next() { + write!(f, " ({}", first)?; + for roll in iter { + match roll { + SignedElementRoll::Positive(e) => write!(f, " + {}", e)?, + SignedElementRoll::Negative(e) => write!(f, " - {}", e)?, + } + } + write!(f, ")")?; + } + Ok(()) + } else if self.len() > 0 { + // For a single item, just show the inner item to avoid redundancy + let first = self.0.iter().next().unwrap(); + write!(f, "{}", first) + } else { + write!(f, "0") + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn dice_roll_display_test() { + assert_eq!(DiceRoll(vec![1, 3, 4]).to_string(), "8 (1 + 3 + 4)"); + assert_eq!(DiceRoll(vec![]).to_string(), "0"); + assert_eq!(DiceRoll(vec![4, 7, 2, 10]).to_string(), "23 (4 + 7 + 2 + 10)"); + } + + #[test] + fn element_roll_display_test() { + assert_eq!(ElementRoll::Dice(DiceRoll(vec![1, 3, 4])).to_string(), "8 (1 + 3 + 4)"); + assert_eq!(ElementRoll::Bonus(7).to_string(), "7"); + } + + #[test] + fn signed_element_roll_display_test() { + assert_eq!(SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 3, 4]))).to_string(), "8 (1 + 3 + 4)"); + assert_eq!(SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 3, 4]))).to_string(), "-8 (1 + 3 + 4)"); + assert_eq!(SignedElementRoll::Positive(ElementRoll::Bonus(7)).to_string(), "7"); + assert_eq!(SignedElementRoll::Negative(ElementRoll::Bonus(7)).to_string(), "-7"); + } + + #[test] + fn element_expression_roll_display_test() { + assert_eq!( + ElementExpressionRoll(vec![ + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 3, 4]))), + ]).to_string(), "8 (1 + 3 + 4)"); + assert_eq!( + ElementExpressionRoll(vec![ + SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 3, 4]))), + ]).to_string(), "-8 (1 + 3 + 4)"); + assert_eq!( + ElementExpressionRoll(vec![ + SignedElementRoll::Positive(ElementRoll::Bonus(7)), + ]).to_string(), "7"); + assert_eq!( + ElementExpressionRoll(vec![ + SignedElementRoll::Negative(ElementRoll::Bonus(7)), + ]).to_string(), "-7"); + assert_eq!( + ElementExpressionRoll(vec![ + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 3, 4]))), + SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 2]))), + SignedElementRoll::Positive(ElementRoll::Bonus(4)), + SignedElementRoll::Negative(ElementRoll::Bonus(7)), + ]).to_string(), "2 (8 (1 + 3 + 4) - 3 (1 + 2) + 4 - 7)"); + assert_eq!( + ElementExpressionRoll(vec![ + SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 3, 4]))), + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 2]))), + SignedElementRoll::Negative(ElementRoll::Bonus(4)), + SignedElementRoll::Positive(ElementRoll::Bonus(7)), + ]).to_string(), "-2 (-8 (1 + 3 + 4) + 3 (1 + 2) - 4 + 7)"); + } +}