From 069ee47364ad6547dd4f500306947e6aa59d20b3 Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Thu, 16 Sep 2021 22:55:11 -0400 Subject: [PATCH] Adding drop function --- dicebot/src/basic/dice.rs | 13 ++++--- dicebot/src/basic/parser.rs | 70 +++++++++++++++++++++++++------------ dicebot/src/basic/roll.rs | 69 ++++++++++++++++++++++++------------ 3 files changed, 101 insertions(+), 51 deletions(-) diff --git a/dicebot/src/basic/dice.rs b/dicebot/src/basic/dice.rs index 695fefa..99e12be 100644 --- a/dicebot/src/basic/dice.rs +++ b/dicebot/src/basic/dice.rs @@ -13,21 +13,24 @@ pub struct Dice { pub(crate) count: u32, pub(crate) sides: u32, pub(crate) keep: u32, + pub(crate) drop: u32, } impl fmt::Display for Dice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.keep == self. count { - write!(f, "{}d{}", self.count, self.sides) - } else { + if self.keep != self.count { write!(f, "{}d{}k{}", self.count, self.sides, self.keep) + } else if self.drop != 0 { + write!(f, "{}d{}d{}", self.count, self.sides, self.drop) + } else { + write!(f, "{}d{}", self.count, self.sides) } } } impl Dice { - pub fn new(count: u32, sides: u32, keep: u32) -> Dice { - Dice { count, sides, keep } + pub fn new(count: u32, sides: u32, keep: u32, drop: u32) -> Dice { + Dice { count, sides, keep, drop } } } diff --git a/dicebot/src/basic/parser.rs b/dicebot/src/basic/parser.rs index 28d3967..9293f27 100644 --- a/dicebot/src/basic/parser.rs +++ b/dicebot/src/basic/parser.rs @@ -35,6 +35,7 @@ enum Sign { fn parse_dice(input: &str) -> IResult<&str, Dice> { // parse main dice expression let (mut input, (count, _, sides)) = tuple((digit1, tag("d"), digit1))(input)?; + // check for keep expression (i.e. D&D 5E Advantage) let keep; match tuple::<&str, _, (_, _), _>((tag("k"), digit1))(input) { @@ -46,16 +47,36 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> { // otherwise absent and keep all dice Err(_) => keep = count, }; + + // check for drop expression (i.e. D&D 5E Disadvantage) + let drop; + match tuple::<&str, _, (_, _), _>((tag("d"), digit1))(input) { + // if ok, drop expression is present + Ok(r) => { + input = r.0; + drop = r.1.1; + } + // otherwise absent and keep all dice + Err(_) => drop = "0", + }; + + let count: u32 = count.parse().unwrap(); + // don't allow keep greater than number of dice, and don't allow keep zero let mut keep: u32 = keep.parse().unwrap(); - let count: u32 = count.parse().unwrap(); if keep > count || keep == 0 { keep = count; } + // don't allow drop greater than or equal to number of dice + let mut drop: u32 = drop.parse().unwrap(); + if drop >= count { + drop = 0; + } + Ok(( input, - Dice::new(count, sides.parse().unwrap(), keep), + Dice::new(count, sides.parse().unwrap(), keep, drop), )) } @@ -127,26 +148,29 @@ mod tests { use super::*; #[test] fn dice_test() { - assert_eq!(parse_dice("2d4"), Ok(("", Dice::new(2, 4, 2)))); - assert_eq!(parse_dice("20d40"), Ok(("", Dice::new(20, 40, 20)))); - assert_eq!(parse_dice("8d7"), Ok(("", Dice::new(8, 7, 8)))); - assert_eq!(parse_dice("2d20k1"), Ok(("", Dice::new(2, 20, 1)))); - assert_eq!(parse_dice("100d10k90"), Ok(("", Dice::new(100, 10, 90)))); - assert_eq!(parse_dice("11d10k10"), Ok(("", Dice::new(11, 10, 10)))); - assert_eq!(parse_dice("12d10k11"), Ok(("", Dice::new(12, 10, 11)))); - assert_eq!(parse_dice("12d10k13"), Ok(("", Dice::new(12, 10, 12)))); - assert_eq!(parse_dice("12d10k0"), Ok(("", Dice::new(12, 10, 12)))); + assert_eq!(parse_dice("2d4"), Ok(("", Dice::new(2, 4, 2, 0)))); + assert_eq!(parse_dice("20d40"), Ok(("", Dice::new(20, 40, 20, 0)))); + assert_eq!(parse_dice("8d7"), Ok(("", Dice::new(8, 7, 8, 0)))); + assert_eq!(parse_dice("2d20k1"), Ok(("", Dice::new(2, 20, 1, 0)))); + assert_eq!(parse_dice("100d10k90"), Ok(("", Dice::new(100, 10, 90, 0)))); + assert_eq!(parse_dice("11d10k10"), Ok(("", Dice::new(11, 10, 10, 0)))); + assert_eq!(parse_dice("12d10k11"), Ok(("", Dice::new(12, 10, 11, 0)))); + assert_eq!(parse_dice("12d10k13"), Ok(("", Dice::new(12, 10, 12, 0)))); + assert_eq!(parse_dice("12d10k0"), Ok(("", Dice::new(12, 10, 12, 0)))); + assert_eq!(parse_dice("20d40d5"), Ok(("", Dice::new(20, 40, 20, 5)))); + assert_eq!(parse_dice("8d7d9"), Ok(("", Dice::new(8, 7, 8, 0)))); + assert_eq!(parse_dice("8d7d8"), Ok(("", Dice::new(8, 7, 8, 0)))); } #[test] fn element_test() { assert_eq!( parse_element(" \t\n\r\n 8d7 \n"), - Ok((" \n", Element::Dice(Dice::new(8, 7, 8)))) + Ok((" \n", Element::Dice(Dice::new(8, 7, 8, 0)))) ); assert_eq!( parse_element(" \t\n\r\n 3d20k2 \n"), - Ok((" \n", Element::Dice(Dice::new(3, 20, 2)))) + Ok((" \n", Element::Dice(Dice::new(3, 20, 2, 0)))) ); assert_eq!( parse_element(" \t\n\r\n 8 \n"), @@ -168,21 +192,21 @@ mod tests { parse_signed_element(" \t\n\r\n- 8d4 \n"), Ok(( " \n", - SignedElement::Negative(Element::Dice(Dice::new(8, 4, 8))) + SignedElement::Negative(Element::Dice(Dice::new(8, 4, 8, 0))) )) ); assert_eq!( parse_signed_element(" \t\n\r\n- 8d4k4 \n"), Ok(( " \n", - SignedElement::Negative(Element::Dice(Dice::new(8, 4, 4))) + SignedElement::Negative(Element::Dice(Dice::new(8, 4, 4, 0))) )) ); assert_eq!( parse_signed_element(" \t\n\r\n+ 8d4 \n"), Ok(( " \n", - SignedElement::Positive(Element::Dice(Dice::new(8, 4, 8))) + SignedElement::Positive(Element::Dice(Dice::new(8, 4, 8, 0))) )) ); } @@ -194,7 +218,7 @@ mod tests { Ok(( "", ElementExpression(vec![SignedElement::Positive(Element::Dice(Dice::new( - 8, 4, 8 + 8, 4, 8, 0 )))]) )) ); @@ -203,7 +227,7 @@ mod tests { Ok(( "", ElementExpression(vec![ - SignedElement::Positive(Element::Dice(Dice::new(2, 20, 1))), + SignedElement::Positive(Element::Dice(Dice::new(2, 20, 1, 0))), SignedElement::Positive(Element::Bonus(5)), ]) )) @@ -213,20 +237,20 @@ mod tests { Ok(( " \n ", ElementExpression(vec![SignedElement::Negative(Element::Dice(Dice::new( - 8, 4, 8 + 8, 4, 8, 0 )))]) )) ); assert_eq!( - parse_element_expression("\t3d4 + 7 - 5 - 6d12 + 1d1 + 53 1d5 "), + parse_element_expression("\t3d4k2 + 7 - 5 - 6d12d3 + 1d1 + 53 1d5 "), Ok(( " 1d5 ", ElementExpression(vec![ - SignedElement::Positive(Element::Dice(Dice::new(3, 4, 3))), + SignedElement::Positive(Element::Dice(Dice::new(3, 4, 2, 0))), SignedElement::Positive(Element::Bonus(7)), SignedElement::Negative(Element::Bonus(5)), - SignedElement::Negative(Element::Dice(Dice::new(6, 12, 6))), - SignedElement::Positive(Element::Dice(Dice::new(1, 1, 1))), + SignedElement::Negative(Element::Dice(Dice::new(6, 12, 6, 3))), + SignedElement::Positive(Element::Dice(Dice::new(1, 1, 1, 0))), SignedElement::Positive(Element::Bonus(53)), ]) )) diff --git a/dicebot/src/basic/roll.rs b/dicebot/src/basic/roll.rs index 524d490..6dd6c39 100644 --- a/dicebot/src/basic/roll.rs +++ b/dicebot/src/basic/roll.rs @@ -19,8 +19,8 @@ pub trait Rolled { } #[derive(Debug, PartialEq, Eq, Clone)] -// array of rolls in order, and how many dice to keep -pub struct DiceRoll (pub Vec, usize); +// array of rolls in order, how many dice to keep, and how many to drop +pub struct DiceRoll (pub Vec, usize, usize); impl DiceRoll { pub fn rolls(&self) -> &[u32] { @@ -31,9 +31,13 @@ impl DiceRoll { self.1 } + pub fn drop(&self) -> usize { + self.2 + } + // only count kept dice in total pub fn total(&self) -> u32 { - self.0[..self.1].iter().sum() + self.0[self.2..self.1].iter().sum() } } @@ -48,14 +52,19 @@ impl fmt::Display for DiceRoll { write!(f, "{}", self.rolled_value())?; let rolls = self.rolls(); let keep = self.keep(); + let drop = self.drop(); let mut iter = rolls.iter().enumerate(); if let Some(first) = iter.next() { - write!(f, " ({}", first.1)?; + if drop != 0 { + write!(f, " ([{}]", first.1)?; + } else { + write!(f, " ({}", first.1)?; + } for roll in iter { - if roll.0 < keep { - write!(f, " + {}", roll.1)?; - } else { + if roll.0 >= keep || roll.0 < drop { write!(f, " + [{}]", roll.1)?; + } else { + write!(f, " + {}", roll.1)?; } } write!(f, ")")?; @@ -75,7 +84,7 @@ impl Roll for dice::Dice { // sort rolls in descending order rolls.sort_by(|a, b| b.cmp(a)); - DiceRoll(rolls,self.keep as usize) + DiceRoll(rolls,self.keep as usize, self.drop as usize) } } @@ -211,22 +220,26 @@ mod tests { use super::*; #[test] fn dice_roll_display_test() { - assert_eq!(DiceRoll(vec![1, 3, 4], 3).to_string(), "8 (1 + 3 + 4)"); - assert_eq!(DiceRoll(vec![], 0).to_string(), "0"); + assert_eq!(DiceRoll(vec![1, 3, 4], 3, 0).to_string(), "8 (1 + 3 + 4)"); + assert_eq!(DiceRoll(vec![], 0, 0).to_string(), "0"); assert_eq!( - DiceRoll(vec![4, 7, 2, 10], 4).to_string(), + DiceRoll(vec![4, 7, 2, 10], 4, 0).to_string(), "23 (4 + 7 + 2 + 10)" ); assert_eq!( - DiceRoll(vec![20, 13, 11, 10], 3).to_string(), + DiceRoll(vec![20, 13, 11, 10], 3, 0).to_string(), "44 (20 + 13 + 11 + [10])" ); + assert_eq!( + DiceRoll(vec![20, 13, 11, 10], 4, 1).to_string(), + "34 ([20] + 13 + 11 + 10)" + ); } #[test] fn element_roll_display_test() { assert_eq!( - ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3)).to_string(), + ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3, 0)).to_string(), "8 (1 + 3 + 4)" ); assert_eq!(ElementRoll::Bonus(7).to_string(), "7"); @@ -235,11 +248,11 @@ mod tests { #[test] fn signed_element_roll_display_test() { assert_eq!( - SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3))).to_string(), + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3, 0))).to_string(), "8 (1 + 3 + 4)" ); assert_eq!( - SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3))).to_string(), + SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3, 0))).to_string(), "-8 (1 + 3 + 4)" ); assert_eq!( @@ -256,14 +269,14 @@ mod tests { fn element_expression_roll_display_test() { assert_eq!( ElementExpressionRoll(vec![SignedElementRoll::Positive(ElementRoll::Dice( - DiceRoll(vec![1, 3, 4], 3) + DiceRoll(vec![1, 3, 4], 3, 0) )),]) .to_string(), "8 (1 + 3 + 4)" ); assert_eq!( ElementExpressionRoll(vec![SignedElementRoll::Negative(ElementRoll::Dice( - DiceRoll(vec![1, 3, 4], 3) + DiceRoll(vec![1, 3, 4], 3, 0) )),]) .to_string(), "-8 (1 + 3 + 4)" @@ -280,8 +293,8 @@ mod tests { ); assert_eq!( ElementExpressionRoll(vec![ - SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3))), - SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 2], 2))), + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3, 0))), + SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 2], 2, 0))), SignedElementRoll::Positive(ElementRoll::Bonus(4)), SignedElementRoll::Negative(ElementRoll::Bonus(7)), ]) @@ -290,8 +303,8 @@ mod tests { ); assert_eq!( ElementExpressionRoll(vec![ - SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3))), - SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 2], 2))), + SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3, 0))), + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 2], 2, 0))), SignedElementRoll::Negative(ElementRoll::Bonus(4)), SignedElementRoll::Positive(ElementRoll::Bonus(7)), ]) @@ -300,13 +313,23 @@ mod tests { ); assert_eq!( ElementExpressionRoll(vec![ - SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![4, 3, 1], 3))), - SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![12, 2], 1))), + SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![4, 3, 1], 3, 0))), + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![12, 2], 1, 0))), SignedElementRoll::Negative(ElementRoll::Bonus(4)), SignedElementRoll::Positive(ElementRoll::Bonus(7)), ]) .to_string(), "7 (-8 (4 + 3 + 1) + 12 (12 + [2]) - 4 + 7)" ); + assert_eq!( + ElementExpressionRoll(vec![ + SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![4, 3, 1], 3, 1))), + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![12, 2], 2, 0))), + SignedElementRoll::Negative(ElementRoll::Bonus(4)), + SignedElementRoll::Positive(ElementRoll::Bonus(7)), + ]) + .to_string(), + "13 (-4 ([4] + 3 + 1) + 14 (12 + 2) - 4 + 7)" + ); } }