From 2654887d8c91dc0a26b1b4418968977f1b87cb1f Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Mon, 6 Sep 2021 17:23:01 -0400 Subject: [PATCH 01/12] Initial commit to add keep to dice struct and preserve parser test cases --- dicebot/src/basic/dice.rs | 11 ++++++++--- dicebot/src/basic/parser.rs | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/dicebot/src/basic/dice.rs b/dicebot/src/basic/dice.rs index 24b8bbf..695fefa 100644 --- a/dicebot/src/basic/dice.rs +++ b/dicebot/src/basic/dice.rs @@ -12,17 +12,22 @@ use std::ops::{Deref, DerefMut}; pub struct Dice { pub(crate) count: u32, pub(crate) sides: u32, + pub(crate) keep: u32, } impl fmt::Display for Dice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}d{}", self.count, self.sides) + if self.keep == self. count { + write!(f, "{}d{}", self.count, self.sides) + } else { + write!(f, "{}d{}k{}", self.count, self.sides, self.keep) + } } } impl Dice { - pub fn new(count: u32, sides: u32) -> Dice { - Dice { count, sides } + pub fn new(count: u32, sides: u32, keep: u32) -> Dice { + Dice { count, sides, keep } } } diff --git a/dicebot/src/basic/parser.rs b/dicebot/src/basic/parser.rs index c16bf23..abb51b4 100644 --- a/dicebot/src/basic/parser.rs +++ b/dicebot/src/basic/parser.rs @@ -36,7 +36,7 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> { let (input, (count, _, sides)) = tuple((digit1, tag("d"), digit1))(input)?; Ok(( input, - Dice::new(count.parse().unwrap(), sides.parse().unwrap()), + Dice::new(count.parse().unwrap(), sides.parse().unwrap(), count.parse().unwrap()), )) } @@ -108,16 +108,16 @@ mod tests { use super::*; #[test] fn dice_test() { - assert_eq!(parse_dice("2d4"), Ok(("", Dice::new(2, 4)))); - assert_eq!(parse_dice("20d40"), Ok(("", Dice::new(20, 40)))); - assert_eq!(parse_dice("8d7"), Ok(("", Dice::new(8, 7)))); + 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)))); } #[test] fn element_test() { assert_eq!( parse_element(" \t\n\r\n 8d7 \n"), - Ok((" \n", Element::Dice(Dice::new(8, 7)))) + Ok((" \n", Element::Dice(Dice::new(8, 7, 8)))) ); assert_eq!( parse_element(" \t\n\r\n 8 \n"), @@ -139,14 +139,14 @@ mod tests { parse_signed_element(" \t\n\r\n- 8d4 \n"), Ok(( " \n", - SignedElement::Negative(Element::Dice(Dice::new(8, 4))) + SignedElement::Negative(Element::Dice(Dice::new(8, 4, 8))) )) ); assert_eq!( parse_signed_element(" \t\n\r\n+ 8d4 \n"), Ok(( " \n", - SignedElement::Positive(Element::Dice(Dice::new(8, 4))) + SignedElement::Positive(Element::Dice(Dice::new(8, 4, 8))) )) ); } @@ -158,7 +158,7 @@ mod tests { Ok(( "", ElementExpression(vec![SignedElement::Positive(Element::Dice(Dice::new( - 8, 4 + 8, 4, 8 )))]) )) ); @@ -167,7 +167,7 @@ mod tests { Ok(( " \n ", ElementExpression(vec![SignedElement::Negative(Element::Dice(Dice::new( - 8, 4 + 8, 4, 8 )))]) )) ); @@ -176,11 +176,11 @@ mod tests { Ok(( " 1d5 ", ElementExpression(vec![ - SignedElement::Positive(Element::Dice(Dice::new(3, 4))), + SignedElement::Positive(Element::Dice(Dice::new(3, 4, 3))), SignedElement::Positive(Element::Bonus(7)), SignedElement::Negative(Element::Bonus(5)), - SignedElement::Negative(Element::Dice(Dice::new(6, 12))), - SignedElement::Positive(Element::Dice(Dice::new(1, 1))), + SignedElement::Negative(Element::Dice(Dice::new(6, 12, 6))), + SignedElement::Positive(Element::Dice(Dice::new(1, 1, 1))), SignedElement::Positive(Element::Bonus(53)), ]) )) -- 2.40.1 From 1860eaf378cd1b78cf3d35731719a5fe4f34b543 Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Mon, 6 Sep 2021 21:39:51 -0400 Subject: [PATCH 02/12] Adding parsing for keeping highest dice --- dicebot/src/basic/parser.rs | 39 +++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/dicebot/src/basic/parser.rs b/dicebot/src/basic/parser.rs index abb51b4..f6f2863 100644 --- a/dicebot/src/basic/parser.rs +++ b/dicebot/src/basic/parser.rs @@ -33,10 +33,22 @@ enum Sign { // Parse a dice expression. Does not eat whitespace fn parse_dice(input: &str) -> IResult<&str, Dice> { - let (input, (count, _, sides)) = tuple((digit1, tag("d"), digit1))(input)?; + // 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) { + // if ok, keep expression is present + Ok(r) => { + input = r.0; + keep = r.1.1; + } + // otherwise absent and keep all dice + Err(_) => keep = count, + }; Ok(( input, - Dice::new(count.parse().unwrap(), sides.parse().unwrap(), count.parse().unwrap()), + Dice::new(count.parse().unwrap(), sides.parse().unwrap(), keep.parse().unwrap()), )) } @@ -111,6 +123,8 @@ mod tests { 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)))); } #[test] @@ -119,6 +133,10 @@ mod tests { parse_element(" \t\n\r\n 8d7 \n"), Ok((" \n", Element::Dice(Dice::new(8, 7, 8)))) ); + assert_eq!( + parse_element(" \t\n\r\n 3d20k2 \n"), + Ok((" \n", Element::Dice(Dice::new(3, 20, 2)))) + ); assert_eq!( parse_element(" \t\n\r\n 8 \n"), Ok((" \n", Element::Bonus(8))) @@ -142,6 +160,13 @@ mod tests { SignedElement::Negative(Element::Dice(Dice::new(8, 4, 8))) )) ); + assert_eq!( + parse_signed_element(" \t\n\r\n- 8d4k4 \n"), + Ok(( + " \n", + SignedElement::Negative(Element::Dice(Dice::new(8, 4, 4))) + )) + ); assert_eq!( parse_signed_element(" \t\n\r\n+ 8d4 \n"), Ok(( @@ -162,6 +187,16 @@ mod tests { )))]) )) ); + assert_eq!( + parse_element_expression("\t2d20k1 + 5"), + Ok(( + "", + ElementExpression(vec![ + SignedElement::Positive(Element::Dice(Dice::new(2, 20, 1))), + SignedElement::Positive(Element::Bonus(5)), + ]) + )) + ); assert_eq!( parse_element_expression(" - 8d4 \n "), Ok(( -- 2.40.1 From 15163ac11de2f1edd98105cb76d95022c229ff55 Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Tue, 7 Sep 2021 20:43:08 -0400 Subject: [PATCH 03/12] Adding calculations for keep, and adding validation on keep input --- dicebot/src/basic/parser.rs | 7 ++++++- dicebot/src/basic/roll.rs | 27 ++++++++++++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/dicebot/src/basic/parser.rs b/dicebot/src/basic/parser.rs index f6f2863..7b11c6f 100644 --- a/dicebot/src/basic/parser.rs +++ b/dicebot/src/basic/parser.rs @@ -41,7 +41,12 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> { // if ok, keep expression is present Ok(r) => { input = r.0; - keep = r.1.1; + // don't allow keep greater than number of dice, and don't allow keep zero + if r.1.1 <= count && r.1.1 != "0" { + keep = r.1.1; + } else { + keep = count; + } } // otherwise absent and keep all dice Err(_) => keep = count, diff --git a/dicebot/src/basic/roll.rs b/dicebot/src/basic/roll.rs index 5614f73..f888eef 100644 --- a/dicebot/src/basic/roll.rs +++ b/dicebot/src/basic/roll.rs @@ -19,15 +19,21 @@ pub trait Rolled { } #[derive(Debug, PartialEq, Eq, Clone)] -pub struct DiceRoll(pub Vec); +// array of rolls in order, and how many dice to keep +pub struct DiceRoll (pub Vec, usize); impl DiceRoll { pub fn rolls(&self) -> &[u32] { &self.0 } + pub fn keep(&self) -> usize { + self.1 + } + + // only count kept dice in total pub fn total(&self) -> u32 { - self.0.iter().sum() + self.0[..=(self.1-1)].iter().sum() } } @@ -41,11 +47,16 @@ 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(); + let keep = self.keep(); + let mut iter = rolls.iter().enumerate(); if let Some(first) = iter.next() { - write!(f, " ({}", first)?; + write!(f, " ({}", first.1)?; for roll in iter { - write!(f, " + {}", roll)?; + if roll.0 < keep { + write!(f, " + {}", roll.1)?; + } else { + write!(f, " + [{}]", roll.1)?; + } } write!(f, ")")?; } @@ -58,11 +69,13 @@ impl Roll for dice::Dice { fn roll(&self) -> DiceRoll { let mut rng = rand::thread_rng(); - let rolls: Vec<_> = (0..self.count) + let mut rolls: Vec<_> = (0..self.count) .map(|_| rng.gen_range(1..=self.sides)) .collect(); + // sort rolls in descending order + rolls.sort_by(|a, b| b.cmp(a)); - DiceRoll(rolls) + DiceRoll(rolls,self.keep as usize) } } -- 2.40.1 From dc242182f4e492b132fe2554f0eb74a055a62017 Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Tue, 7 Sep 2021 23:59:49 -0400 Subject: [PATCH 04/12] Fix string comparison in keep/count check, and add test cases --- dicebot/src/basic/parser.rs | 20 ++++++++++++------- dicebot/src/basic/roll.rs | 40 +++++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/dicebot/src/basic/parser.rs b/dicebot/src/basic/parser.rs index 7b11c6f..28d3967 100644 --- a/dicebot/src/basic/parser.rs +++ b/dicebot/src/basic/parser.rs @@ -41,19 +41,21 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> { // if ok, keep expression is present Ok(r) => { input = r.0; - // don't allow keep greater than number of dice, and don't allow keep zero - if r.1.1 <= count && r.1.1 != "0" { - keep = r.1.1; - } else { - keep = count; - } + keep = r.1.1; } // otherwise absent and keep all dice Err(_) => keep = count, }; + // 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; + } + Ok(( input, - Dice::new(count.parse().unwrap(), sides.parse().unwrap(), keep.parse().unwrap()), + Dice::new(count, sides.parse().unwrap(), keep), )) } @@ -130,6 +132,10 @@ mod tests { 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)))); } #[test] diff --git a/dicebot/src/basic/roll.rs b/dicebot/src/basic/roll.rs index f888eef..524d490 100644 --- a/dicebot/src/basic/roll.rs +++ b/dicebot/src/basic/roll.rs @@ -33,7 +33,7 @@ impl DiceRoll { // only count kept dice in total pub fn total(&self) -> u32 { - self.0[..=(self.1-1)].iter().sum() + self.0[..self.1].iter().sum() } } @@ -211,18 +211,22 @@ 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![1, 3, 4], 3).to_string(), "8 (1 + 3 + 4)"); + assert_eq!(DiceRoll(vec![], 0).to_string(), "0"); assert_eq!( - DiceRoll(vec![4, 7, 2, 10]).to_string(), + DiceRoll(vec![4, 7, 2, 10], 4).to_string(), "23 (4 + 7 + 2 + 10)" ); + assert_eq!( + DiceRoll(vec![20, 13, 11, 10], 3).to_string(), + "44 (20 + 13 + 11 + [10])" + ); } #[test] fn element_roll_display_test() { assert_eq!( - ElementRoll::Dice(DiceRoll(vec![1, 3, 4])).to_string(), + ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3)).to_string(), "8 (1 + 3 + 4)" ); assert_eq!(ElementRoll::Bonus(7).to_string(), "7"); @@ -231,11 +235,11 @@ mod tests { #[test] fn signed_element_roll_display_test() { assert_eq!( - SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 3, 4]))).to_string(), + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3))).to_string(), "8 (1 + 3 + 4)" ); assert_eq!( - SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 3, 4]))).to_string(), + SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3))).to_string(), "-8 (1 + 3 + 4)" ); assert_eq!( @@ -252,14 +256,14 @@ mod tests { fn element_expression_roll_display_test() { assert_eq!( ElementExpressionRoll(vec![SignedElementRoll::Positive(ElementRoll::Dice( - DiceRoll(vec![1, 3, 4]) + DiceRoll(vec![1, 3, 4], 3) )),]) .to_string(), "8 (1 + 3 + 4)" ); assert_eq!( ElementExpressionRoll(vec![SignedElementRoll::Negative(ElementRoll::Dice( - DiceRoll(vec![1, 3, 4]) + DiceRoll(vec![1, 3, 4], 3) )),]) .to_string(), "-8 (1 + 3 + 4)" @@ -276,8 +280,8 @@ mod tests { ); assert_eq!( ElementExpressionRoll(vec![ - SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 3, 4]))), - SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 2]))), + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3))), + SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 2], 2))), SignedElementRoll::Positive(ElementRoll::Bonus(4)), SignedElementRoll::Negative(ElementRoll::Bonus(7)), ]) @@ -286,13 +290,23 @@ mod tests { ); assert_eq!( ElementExpressionRoll(vec![ - SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 3, 4]))), - SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 2]))), + SignedElementRoll::Negative(ElementRoll::Dice(DiceRoll(vec![1, 3, 4], 3))), + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![1, 2], 2))), SignedElementRoll::Negative(ElementRoll::Bonus(4)), SignedElementRoll::Positive(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![4, 3, 1], 3))), + SignedElementRoll::Positive(ElementRoll::Dice(DiceRoll(vec![12, 2], 1))), + SignedElementRoll::Negative(ElementRoll::Bonus(4)), + SignedElementRoll::Positive(ElementRoll::Bonus(7)), + ]) + .to_string(), + "7 (-8 (4 + 3 + 1) + 12 (12 + [2]) - 4 + 7)" + ); } } -- 2.40.1 From 069ee47364ad6547dd4f500306947e6aa59d20b3 Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Thu, 16 Sep 2021 22:55:11 -0400 Subject: [PATCH 05/12] 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)" + ); } } -- 2.40.1 From 8317f40f619f306db6d67a536377fc0c06eeb149 Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Thu, 16 Sep 2021 23:12:23 -0400 Subject: [PATCH 06/12] Updating README for keep/drop --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7fe3ccb..1575833 100644 --- a/README.md +++ b/README.md @@ -118,8 +118,16 @@ expressions. !r 3d12 - 5d2 + 3 - 7d3 + 20d20 ``` -This system does not yet have the capability to handle things like D&D -5e advantage or disadvantage. +#### Keep/Drop Dice +The bot supports either keeping the highest dice in a roll, or +dropping the highest dice in a roll. This allows the bot to handle +things like D&D 5e advantage or disadvantage. + +``` +!roll 2d20k1 +!r 2d20d1 + 5 +!r 10d10k5 + 10d10d5 - 2 +``` ### Storytelling System -- 2.40.1 From f904e3a94877bb5af3e3a342101eccb95fa96298 Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Fri, 17 Sep 2021 21:45:30 -0400 Subject: [PATCH 07/12] Updating match blocks for keep/drop --- dicebot/src/basic/parser.rs | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/dicebot/src/basic/parser.rs b/dicebot/src/basic/parser.rs index 9293f27..e8d4ad0 100644 --- a/dicebot/src/basic/parser.rs +++ b/dicebot/src/basic/parser.rs @@ -31,33 +31,25 @@ enum Sign { Minus, } -// Parse a dice expression. Does not eat whitespace +/// Parse a dice expression. Does not eat whitespace fn parse_dice(input: &str) -> IResult<&str, Dice> { // parse main dice expression - let (mut input, (count, _, sides)) = tuple((digit1, tag("d"), digit1))(input)?; + let (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) { + // check for keep expression to keep highest dice (2d20k1) + let (keep, input) = match tuple::<&str, _, (_, _), _>((tag("k"), digit1))(input) { // if ok, keep expression is present - Ok(r) => { - input = r.0; - keep = r.1.1; - } + Ok(r) => (r.1.1, r.0), // otherwise absent and keep all dice - Err(_) => keep = count, + Err(_) => (input, "") }; - // 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; - } + // check for drop expression to drop highest dice (2d20dh1) + let (drop, input) = match tuple::<&str, _, (_, _), _>((tag("dh"), digit1))(input) { + // if ok, keep expression is present + Ok(r) => (r.1.1, r.0), // otherwise absent and keep all dice - Err(_) => drop = "0", + Err(_) => (input, "") }; let count: u32 = count.parse().unwrap(); -- 2.40.1 From 1992ef4e08256e4de3c55fbcca6105079ce1a344 Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Fri, 17 Sep 2021 22:08:51 -0400 Subject: [PATCH 08/12] Updating roll doc --- dicebot/src/basic/roll.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dicebot/src/basic/roll.rs b/dicebot/src/basic/roll.rs index 6dd6c39..a104d6b 100644 --- a/dicebot/src/basic/roll.rs +++ b/dicebot/src/basic/roll.rs @@ -19,7 +19,9 @@ pub trait Rolled { } #[derive(Debug, PartialEq, Eq, Clone)] -// array of rolls in order, how many dice to keep, and how many to drop +/// array of rolls in order, how many dice to keep, and how many to drop +/// keep indicates how many of the highest dice to keep +/// drop indicates how many of the highest dice to drop pub struct DiceRoll (pub Vec, usize, usize); impl DiceRoll { -- 2.40.1 From 8b5973475faf944f6462996cec455700e5356be8 Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Fri, 17 Sep 2021 22:18:23 -0400 Subject: [PATCH 09/12] Forgot to fix tests, fixing keep/drop Err case --- dicebot/src/basic/parser.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dicebot/src/basic/parser.rs b/dicebot/src/basic/parser.rs index e8d4ad0..3702c98 100644 --- a/dicebot/src/basic/parser.rs +++ b/dicebot/src/basic/parser.rs @@ -41,7 +41,7 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> { // if ok, keep expression is present Ok(r) => (r.1.1, r.0), // otherwise absent and keep all dice - Err(_) => (input, "") + Err(_) => (count, input) }; // check for drop expression to drop highest dice (2d20dh1) @@ -49,7 +49,7 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> { // if ok, keep expression is present Ok(r) => (r.1.1, r.0), // otherwise absent and keep all dice - Err(_) => (input, "") + Err(_) => ("0", input) }; let count: u32 = count.parse().unwrap(); @@ -149,9 +149,9 @@ mod tests { 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)))); + assert_eq!(parse_dice("20d40dh5"), Ok(("", Dice::new(20, 40, 20, 5)))); + assert_eq!(parse_dice("8d7dh9"), Ok(("", Dice::new(8, 7, 8, 0)))); + assert_eq!(parse_dice("8d7dh8"), Ok(("", Dice::new(8, 7, 8, 0)))); } #[test] @@ -234,7 +234,7 @@ mod tests { )) ); assert_eq!( - parse_element_expression("\t3d4k2 + 7 - 5 - 6d12d3 + 1d1 + 53 1d5 "), + parse_element_expression("\t3d4k2 + 7 - 5 - 6d12dh3 + 1d1 + 53 1d5 "), Ok(( " 1d5 ", ElementExpression(vec![ -- 2.40.1 From 3d6210b32d5c232c28f758d5c4a64f25c61a1143 Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Fri, 17 Sep 2021 23:11:13 -0400 Subject: [PATCH 10/12] Adding enum for exclusive drop/keep --- dicebot/src/basic/dice.rs | 34 ++++++++++----- dicebot/src/basic/parser.rs | 87 ++++++++++++++++++++++--------------- dicebot/src/basic/roll.rs | 6 ++- 3 files changed, 80 insertions(+), 47 deletions(-) diff --git a/dicebot/src/basic/dice.rs b/dicebot/src/basic/dice.rs index 99e12be..25ecdc6 100644 --- a/dicebot/src/basic/dice.rs +++ b/dicebot/src/basic/dice.rs @@ -12,25 +12,39 @@ use std::ops::{Deref, DerefMut}; pub struct Dice { pub(crate) count: u32, pub(crate) sides: u32, - pub(crate) keep: u32, - pub(crate) drop: u32, + pub(crate) keep_drop: KeepOrDrop, } impl fmt::Display for Dice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - 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) + match self.keep_drop { + KeepOrDrop::Keep(keep) => { + if keep != self.count { + write!(f, "{}d{}k{}", self.count, self.sides, keep) + } else { + write!(f, "{}d{}", self.count, self.sides) + } + } + KeepOrDrop::Drop(drop) => { + if drop != 0 { + write!(f, "{}d{}dh{}", self.count, self.sides, drop) + } else { + write!(f, "{}d{}", self.count, self.sides) + } + } } } } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum KeepOrDrop { + Keep (u32), + Drop (u32), +} + impl Dice { - pub fn new(count: u32, sides: u32, keep: u32, drop: u32) -> Dice { - Dice { count, sides, keep, drop } + pub fn new(count: u32, sides: u32, keep_drop: KeepOrDrop) -> Dice { + Dice { count, sides, keep_drop } } } diff --git a/dicebot/src/basic/parser.rs b/dicebot/src/basic/parser.rs index 3702c98..9849342 100644 --- a/dicebot/src/basic/parser.rs +++ b/dicebot/src/basic/parser.rs @@ -41,7 +41,7 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> { // if ok, keep expression is present Ok(r) => (r.1.1, r.0), // otherwise absent and keep all dice - Err(_) => (count, input) + Err(_) => ("", input) }; // check for drop expression to drop highest dice (2d20dh1) @@ -49,26 +49,41 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> { // if ok, keep expression is present Ok(r) => (r.1.1, r.0), // otherwise absent and keep all dice - Err(_) => ("0", input) + Err(_) => ("", input) }; 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(); - 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; - } + let keep_drop = match keep.parse::() { + // Ok, there's a keep value, check and create Keep + Ok(i) => { + if i > count || i == 0 { + KeepOrDrop::Keep(count) + } else { + KeepOrDrop::Keep(i) + } + }, + // Err, check if drop works + Err(_) => { + match drop.parse::() { + // Ok, there's a drop value, check and create Drop + Ok(i) => { + if i >= count { + KeepOrDrop::Keep(count) + } else { + KeepOrDrop::Drop(i) + } + }, + // Err, there's neither keep nor drop + Err(_) => KeepOrDrop::Keep(count), + } + }, + }; Ok(( input, - Dice::new(count, sides.parse().unwrap(), keep, drop), + Dice::new(count, sides.parse().unwrap(), keep_drop), )) } @@ -140,29 +155,29 @@ mod tests { use super::*; #[test] fn dice_test() { - 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("20d40dh5"), Ok(("", Dice::new(20, 40, 20, 5)))); - assert_eq!(parse_dice("8d7dh9"), Ok(("", Dice::new(8, 7, 8, 0)))); - assert_eq!(parse_dice("8d7dh8"), Ok(("", Dice::new(8, 7, 8, 0)))); + assert_eq!(parse_dice("2d4"), Ok(("", Dice::new(2, 4, KeepOrDrop::Keep(2))))); + assert_eq!(parse_dice("20d40"), Ok(("", Dice::new(20, 40, KeepOrDrop::Keep(20))))); + assert_eq!(parse_dice("8d7"), Ok(("", Dice::new(8, 7, KeepOrDrop::Keep(8))))); + assert_eq!(parse_dice("2d20k1"), Ok(("", Dice::new(2, 20, KeepOrDrop::Keep(1))))); + assert_eq!(parse_dice("100d10k90"), Ok(("", Dice::new(100, 10, KeepOrDrop::Keep(90))))); + assert_eq!(parse_dice("11d10k10"), Ok(("", Dice::new(11, 10, KeepOrDrop::Keep(10))))); + assert_eq!(parse_dice("12d10k11"), Ok(("", Dice::new(12, 10, KeepOrDrop::Keep(11))))); + assert_eq!(parse_dice("12d10k13"), Ok(("", Dice::new(12, 10, KeepOrDrop::Keep(12))))); + assert_eq!(parse_dice("12d10k0"), Ok(("", Dice::new(12, 10, KeepOrDrop::Keep(12))))); + assert_eq!(parse_dice("20d40dh5"), Ok(("", Dice::new(20, 40, KeepOrDrop::Drop(5))))); + assert_eq!(parse_dice("8d7dh9"), Ok(("", Dice::new(8, 7, KeepOrDrop::Keep(8))))); + assert_eq!(parse_dice("8d7dh8"), Ok(("", Dice::new(8, 7, KeepOrDrop::Keep(8))))); } #[test] fn element_test() { assert_eq!( parse_element(" \t\n\r\n 8d7 \n"), - Ok((" \n", Element::Dice(Dice::new(8, 7, 8, 0)))) + Ok((" \n", Element::Dice(Dice::new(8, 7, KeepOrDrop::Keep(8))))) ); assert_eq!( parse_element(" \t\n\r\n 3d20k2 \n"), - Ok((" \n", Element::Dice(Dice::new(3, 20, 2, 0)))) + Ok((" \n", Element::Dice(Dice::new(3, 20, KeepOrDrop::Keep(2))))) ); assert_eq!( parse_element(" \t\n\r\n 8 \n"), @@ -184,21 +199,21 @@ mod tests { parse_signed_element(" \t\n\r\n- 8d4 \n"), Ok(( " \n", - SignedElement::Negative(Element::Dice(Dice::new(8, 4, 8, 0))) + SignedElement::Negative(Element::Dice(Dice::new(8, 4, KeepOrDrop::Keep(8)))) )) ); assert_eq!( parse_signed_element(" \t\n\r\n- 8d4k4 \n"), Ok(( " \n", - SignedElement::Negative(Element::Dice(Dice::new(8, 4, 4, 0))) + SignedElement::Negative(Element::Dice(Dice::new(8, 4, KeepOrDrop::Keep(4)))) )) ); assert_eq!( parse_signed_element(" \t\n\r\n+ 8d4 \n"), Ok(( " \n", - SignedElement::Positive(Element::Dice(Dice::new(8, 4, 8, 0))) + SignedElement::Positive(Element::Dice(Dice::new(8, 4, KeepOrDrop::Keep(8)))) )) ); } @@ -210,7 +225,7 @@ mod tests { Ok(( "", ElementExpression(vec![SignedElement::Positive(Element::Dice(Dice::new( - 8, 4, 8, 0 + 8, 4, KeepOrDrop::Keep(8) )))]) )) ); @@ -219,7 +234,7 @@ mod tests { Ok(( "", ElementExpression(vec![ - SignedElement::Positive(Element::Dice(Dice::new(2, 20, 1, 0))), + SignedElement::Positive(Element::Dice(Dice::new(2, 20, KeepOrDrop::Keep(1)))), SignedElement::Positive(Element::Bonus(5)), ]) )) @@ -229,7 +244,7 @@ mod tests { Ok(( " \n ", ElementExpression(vec![SignedElement::Negative(Element::Dice(Dice::new( - 8, 4, 8, 0 + 8, 4, KeepOrDrop::Keep(8) )))]) )) ); @@ -238,11 +253,11 @@ mod tests { Ok(( " 1d5 ", ElementExpression(vec![ - SignedElement::Positive(Element::Dice(Dice::new(3, 4, 2, 0))), + SignedElement::Positive(Element::Dice(Dice::new(3, 4, KeepOrDrop::Keep(2)))), SignedElement::Positive(Element::Bonus(7)), SignedElement::Negative(Element::Bonus(5)), - SignedElement::Negative(Element::Dice(Dice::new(6, 12, 6, 3))), - SignedElement::Positive(Element::Dice(Dice::new(1, 1, 1, 0))), + SignedElement::Negative(Element::Dice(Dice::new(6, 12, KeepOrDrop::Drop(3)))), + SignedElement::Positive(Element::Dice(Dice::new(1, 1, KeepOrDrop::Keep(1)))), SignedElement::Positive(Element::Bonus(53)), ]) )) diff --git a/dicebot/src/basic/roll.rs b/dicebot/src/basic/roll.rs index a104d6b..f0988e0 100644 --- a/dicebot/src/basic/roll.rs +++ b/dicebot/src/basic/roll.rs @@ -4,6 +4,7 @@ * project. */ use crate::basic::dice; +use crate::basic::dice::KeepOrDrop; use rand::prelude::*; use std::fmt; use std::ops::{Deref, DerefMut}; @@ -86,7 +87,10 @@ impl Roll for dice::Dice { // sort rolls in descending order rolls.sort_by(|a, b| b.cmp(a)); - DiceRoll(rolls,self.keep as usize, self.drop as usize) + match self.keep_drop { + KeepOrDrop::Keep(k) => DiceRoll(rolls,k as usize, 0), + KeepOrDrop::Drop(dh) => DiceRoll(rolls,self.count as usize, dh as usize), + } } } -- 2.40.1 From 2d9853fbf03b8b26f8df90065c949350f5f50286 Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Fri, 17 Sep 2021 23:15:55 -0400 Subject: [PATCH 11/12] Updating README for new drop command --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1575833..5882dfc 100644 --- a/README.md +++ b/README.md @@ -125,8 +125,8 @@ things like D&D 5e advantage or disadvantage. ``` !roll 2d20k1 -!r 2d20d1 + 5 -!r 10d10k5 + 10d10d5 - 2 +!r 2d20dh1 + 5 +!r 10d10k5 + 10d10dh5 - 2 ``` ### Storytelling System -- 2.40.1 From 7e7e9e534eb641c382d8b79518f27fe2540a65bc Mon Sep 17 00:00:00 2001 From: Matthew Sparks Date: Fri, 24 Sep 2021 23:03:20 -0400 Subject: [PATCH 12/12] Adding None enum to keep/drop, cleaning up matches --- dicebot/src/basic/dice.rs | 18 +++---------- dicebot/src/basic/parser.rs | 50 ++++++++++++++++--------------------- dicebot/src/basic/roll.rs | 1 + 3 files changed, 27 insertions(+), 42 deletions(-) diff --git a/dicebot/src/basic/dice.rs b/dicebot/src/basic/dice.rs index 25ecdc6..e35a744 100644 --- a/dicebot/src/basic/dice.rs +++ b/dicebot/src/basic/dice.rs @@ -18,20 +18,9 @@ pub struct Dice { impl fmt::Display for Dice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.keep_drop { - KeepOrDrop::Keep(keep) => { - if keep != self.count { - write!(f, "{}d{}k{}", self.count, self.sides, keep) - } else { - write!(f, "{}d{}", self.count, self.sides) - } - } - KeepOrDrop::Drop(drop) => { - if drop != 0 { - write!(f, "{}d{}dh{}", self.count, self.sides, drop) - } else { - write!(f, "{}d{}", self.count, self.sides) - } - } + KeepOrDrop::Keep(keep) => write!(f, "{}d{}k{}", self.count, self.sides, keep), + KeepOrDrop::Drop(drop) => write!(f, "{}d{}dh{}", self.count, self.sides, drop), + KeepOrDrop::None => write!(f, "{}d{}", self.count, self.sides), } } } @@ -40,6 +29,7 @@ impl fmt::Display for Dice { pub enum KeepOrDrop { Keep (u32), Drop (u32), + None, } impl Dice { diff --git a/dicebot/src/basic/parser.rs b/dicebot/src/basic/parser.rs index 9849342..2be8590 100644 --- a/dicebot/src/basic/parser.rs +++ b/dicebot/src/basic/parser.rs @@ -39,7 +39,7 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> { // check for keep expression to keep highest dice (2d20k1) let (keep, input) = match tuple::<&str, _, (_, _), _>((tag("k"), digit1))(input) { // if ok, keep expression is present - Ok(r) => (r.1.1, r.0), + Ok((rest, (_, keep_amount))) => (keep_amount, rest), // otherwise absent and keep all dice Err(_) => ("", input) }; @@ -47,7 +47,7 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> { // check for drop expression to drop highest dice (2d20dh1) let (drop, input) = match tuple::<&str, _, (_, _), _>((tag("dh"), digit1))(input) { // if ok, keep expression is present - Ok(r) => (r.1.1, r.0), + Ok((rest, (_, drop_amount))) => (drop_amount, rest), // otherwise absent and keep all dice Err(_) => ("", input) }; @@ -57,26 +57,20 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> { // don't allow keep greater than number of dice, and don't allow keep zero let keep_drop = match keep.parse::() { // Ok, there's a keep value, check and create Keep - Ok(i) => { - if i > count || i == 0 { - KeepOrDrop::Keep(count) - } else { - KeepOrDrop::Keep(i) - } + Ok(i) => match i { + _i if _i > count || _i == 0 => KeepOrDrop::None, + i => KeepOrDrop::Keep(i), }, // Err, check if drop works Err(_) => { match drop.parse::() { // Ok, there's a drop value, check and create Drop - Ok(i) => { - if i >= count { - KeepOrDrop::Keep(count) - } else { - KeepOrDrop::Drop(i) - } + Ok(i) => match i { + _i if i >= count => KeepOrDrop::None, + i => KeepOrDrop::Drop(i), }, // Err, there's neither keep nor drop - Err(_) => KeepOrDrop::Keep(count), + Err(_) => KeepOrDrop::None, } }, }; @@ -155,25 +149,25 @@ mod tests { use super::*; #[test] fn dice_test() { - assert_eq!(parse_dice("2d4"), Ok(("", Dice::new(2, 4, KeepOrDrop::Keep(2))))); - assert_eq!(parse_dice("20d40"), Ok(("", Dice::new(20, 40, KeepOrDrop::Keep(20))))); - assert_eq!(parse_dice("8d7"), Ok(("", Dice::new(8, 7, KeepOrDrop::Keep(8))))); + assert_eq!(parse_dice("2d4"), Ok(("", Dice::new(2, 4, KeepOrDrop::None)))); + assert_eq!(parse_dice("20d40"), Ok(("", Dice::new(20, 40, KeepOrDrop::None)))); + assert_eq!(parse_dice("8d7"), Ok(("", Dice::new(8, 7, KeepOrDrop::None)))); assert_eq!(parse_dice("2d20k1"), Ok(("", Dice::new(2, 20, KeepOrDrop::Keep(1))))); assert_eq!(parse_dice("100d10k90"), Ok(("", Dice::new(100, 10, KeepOrDrop::Keep(90))))); assert_eq!(parse_dice("11d10k10"), Ok(("", Dice::new(11, 10, KeepOrDrop::Keep(10))))); assert_eq!(parse_dice("12d10k11"), Ok(("", Dice::new(12, 10, KeepOrDrop::Keep(11))))); - assert_eq!(parse_dice("12d10k13"), Ok(("", Dice::new(12, 10, KeepOrDrop::Keep(12))))); - assert_eq!(parse_dice("12d10k0"), Ok(("", Dice::new(12, 10, KeepOrDrop::Keep(12))))); + assert_eq!(parse_dice("12d10k13"), Ok(("", Dice::new(12, 10, KeepOrDrop::None)))); + assert_eq!(parse_dice("12d10k0"), Ok(("", Dice::new(12, 10, KeepOrDrop::None)))); assert_eq!(parse_dice("20d40dh5"), Ok(("", Dice::new(20, 40, KeepOrDrop::Drop(5))))); - assert_eq!(parse_dice("8d7dh9"), Ok(("", Dice::new(8, 7, KeepOrDrop::Keep(8))))); - assert_eq!(parse_dice("8d7dh8"), Ok(("", Dice::new(8, 7, KeepOrDrop::Keep(8))))); + assert_eq!(parse_dice("8d7dh9"), Ok(("", Dice::new(8, 7, KeepOrDrop::None)))); + assert_eq!(parse_dice("8d7dh8"), Ok(("", Dice::new(8, 7, KeepOrDrop::None)))); } #[test] fn element_test() { assert_eq!( parse_element(" \t\n\r\n 8d7 \n"), - Ok((" \n", Element::Dice(Dice::new(8, 7, KeepOrDrop::Keep(8))))) + Ok((" \n", Element::Dice(Dice::new(8, 7, KeepOrDrop::None)))) ); assert_eq!( parse_element(" \t\n\r\n 3d20k2 \n"), @@ -199,7 +193,7 @@ mod tests { parse_signed_element(" \t\n\r\n- 8d4 \n"), Ok(( " \n", - SignedElement::Negative(Element::Dice(Dice::new(8, 4, KeepOrDrop::Keep(8)))) + SignedElement::Negative(Element::Dice(Dice::new(8, 4, KeepOrDrop::None))) )) ); assert_eq!( @@ -213,7 +207,7 @@ mod tests { parse_signed_element(" \t\n\r\n+ 8d4 \n"), Ok(( " \n", - SignedElement::Positive(Element::Dice(Dice::new(8, 4, KeepOrDrop::Keep(8)))) + SignedElement::Positive(Element::Dice(Dice::new(8, 4, KeepOrDrop::None))) )) ); } @@ -225,7 +219,7 @@ mod tests { Ok(( "", ElementExpression(vec![SignedElement::Positive(Element::Dice(Dice::new( - 8, 4, KeepOrDrop::Keep(8) + 8, 4, KeepOrDrop::None )))]) )) ); @@ -244,7 +238,7 @@ mod tests { Ok(( " \n ", ElementExpression(vec![SignedElement::Negative(Element::Dice(Dice::new( - 8, 4, KeepOrDrop::Keep(8) + 8, 4, KeepOrDrop::None )))]) )) ); @@ -257,7 +251,7 @@ mod tests { SignedElement::Positive(Element::Bonus(7)), SignedElement::Negative(Element::Bonus(5)), SignedElement::Negative(Element::Dice(Dice::new(6, 12, KeepOrDrop::Drop(3)))), - SignedElement::Positive(Element::Dice(Dice::new(1, 1, KeepOrDrop::Keep(1)))), + SignedElement::Positive(Element::Dice(Dice::new(1, 1, KeepOrDrop::None))), SignedElement::Positive(Element::Bonus(53)), ]) )) diff --git a/dicebot/src/basic/roll.rs b/dicebot/src/basic/roll.rs index f0988e0..42e89f2 100644 --- a/dicebot/src/basic/roll.rs +++ b/dicebot/src/basic/roll.rs @@ -90,6 +90,7 @@ impl Roll for dice::Dice { match self.keep_drop { KeepOrDrop::Keep(k) => DiceRoll(rolls,k as usize, 0), KeepOrDrop::Drop(dh) => DiceRoll(rolls,self.count as usize, dh as usize), + KeepOrDrop::None => DiceRoll(rolls,self.count as usize, 0), } } } -- 2.40.1