Keep/Drop Function #92
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)),
|
||||
])
|
||||
))
|
||||
|
|
|
@ -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<u32>, usize);
|
||||
// array of rolls in order, how many dice to keep, and how many to drop
|
||||
pub struct DiceRoll (pub Vec<u32>, 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)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue