Code cleanup
This commit is contained in:
parent
6a1f5b6035
commit
56a37a738f
381
src/lib.rs
381
src/lib.rs
@ -1,77 +1,102 @@
|
||||
use anyhow::Result;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::Rng;
|
||||
use regex::Regex;
|
||||
use thiserror::Error;
|
||||
use anyhow::Result;
|
||||
use rand::Rng;
|
||||
use rand::seq::SliceRandom;
|
||||
|
||||
#[cfg(test)]
|
||||
mod lib_test;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum DiceFilter {
|
||||
DropLowest(usize), DropHighest(usize), KeepLowest(usize), KeepHighest(usize),
|
||||
DropLowest(usize),
|
||||
DropHighest(usize),
|
||||
KeepLowest(usize),
|
||||
KeepHighest(usize),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum Segment {
|
||||
DiceRoll{
|
||||
op: char,
|
||||
count: i32,
|
||||
size: i32,
|
||||
filter: Option<DiceFilter>,
|
||||
},
|
||||
Modifier{
|
||||
op: char,
|
||||
amount: i32,
|
||||
},
|
||||
DiceRoll {
|
||||
op: char,
|
||||
count: i32,
|
||||
size: i32,
|
||||
filter: Option<DiceFilter>,
|
||||
},
|
||||
Modifier {
|
||||
op: char,
|
||||
amount: i32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
enum SegmentError {
|
||||
#[error("No dice size was given")]
|
||||
SizeIsMissing,
|
||||
#[error("Invalid filter operator: {op}")]
|
||||
InvalidFilterOperator{op: String},
|
||||
#[error("Incomplete filter")]
|
||||
IncompleteFilter,
|
||||
#[error("No dice size was given")]
|
||||
SizeIsMissing,
|
||||
#[error("Invalid filter operator: {op}")]
|
||||
InvalidFilterOperator { op: String },
|
||||
#[error("Incomplete filter")]
|
||||
IncompleteFilter,
|
||||
}
|
||||
|
||||
fn construct_dice_filter(op: &str, amount: usize) -> Result<DiceFilter> {
|
||||
match op.to_lowercase().as_str() {
|
||||
"d" | "dl" => Ok(DiceFilter::DropLowest(amount)),
|
||||
"dh" => Ok(DiceFilter::DropHighest(amount)),
|
||||
"k" | "kh" => Ok(DiceFilter::KeepHighest(amount)),
|
||||
"kl" => Ok(DiceFilter::KeepLowest(amount)),
|
||||
_ => Err(SegmentError::InvalidFilterOperator{op: op.to_owned()})?,
|
||||
}
|
||||
match op.to_lowercase().as_str() {
|
||||
"d" | "dl" => Ok(DiceFilter::DropLowest(amount)),
|
||||
"dh" => Ok(DiceFilter::DropHighest(amount)),
|
||||
"k" | "kh" => Ok(DiceFilter::KeepHighest(amount)),
|
||||
"kl" => Ok(DiceFilter::KeepLowest(amount)),
|
||||
_ => Err(SegmentError::InvalidFilterOperator { op: op.to_owned() }.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_dice_segment(cap: regex::Captures<'_>) -> Result<Segment> {
|
||||
let op = cap.name("op").and_then(|i| i.as_str().chars().next()).unwrap_or('+');
|
||||
let modifier = cap.name("mod").map(|i| i.as_str().parse()).transpose()?;
|
||||
let op = cap
|
||||
.name("op")
|
||||
.and_then(|i| i.as_str().chars().next())
|
||||
.unwrap_or('+');
|
||||
let modifier = cap.name("mod").map(|i| i.as_str().parse()).transpose()?;
|
||||
|
||||
if let Some(amount) = modifier {
|
||||
return Ok(Segment::Modifier{ op, amount });
|
||||
}
|
||||
if let Some(amount) = modifier {
|
||||
return Ok(Segment::Modifier { op, amount });
|
||||
}
|
||||
|
||||
let count:i32 = cap.name("count").map(|i| i.as_str().parse()).transpose()?.unwrap_or(1);
|
||||
let size:i32 = cap.name("size").map(|i| i.as_str().parse()).transpose()?.ok_or(SegmentError::SizeIsMissing)?;
|
||||
let filter_amount = cap.name("filter").map(|i| i.as_str().parse::<usize>()).transpose()?;
|
||||
let filter_op = cap.name("filter_op").map(|op| op.as_str());
|
||||
/*
|
||||
dbg!(filter_amount);
|
||||
dbg!(filter_op);
|
||||
*/
|
||||
if filter_amount.is_some() != filter_op.is_some() {
|
||||
Err(SegmentError::IncompleteFilter)?;
|
||||
}
|
||||
let filter = filter_amount.and_then(|amount| (filter_op.map(|op| construct_dice_filter(op, amount)))).transpose()?;
|
||||
let count: i32 = cap
|
||||
.name("count")
|
||||
.map(|i| i.as_str().parse())
|
||||
.transpose()?
|
||||
.unwrap_or(1);
|
||||
let size: i32 = cap
|
||||
.name("size")
|
||||
.map(|i| i.as_str().parse())
|
||||
.transpose()?
|
||||
.ok_or(SegmentError::SizeIsMissing)?;
|
||||
let filter_amount = cap
|
||||
.name("filter")
|
||||
.map(|i| i.as_str().parse::<usize>())
|
||||
.transpose()?;
|
||||
let filter_op = cap.name("filter_op").map(|op| op.as_str());
|
||||
/*
|
||||
dbg!(filter_amount);
|
||||
dbg!(filter_op);
|
||||
*/
|
||||
if filter_amount.is_some() != filter_op.is_some() {
|
||||
return Err(SegmentError::IncompleteFilter.into());
|
||||
}
|
||||
let filter = filter_amount
|
||||
.and_then(|amount| (filter_op.map(|op| construct_dice_filter(op, amount))))
|
||||
.transpose()?;
|
||||
|
||||
Ok(Segment::DiceRoll{op, count, size, filter})
|
||||
Ok(Segment::DiceRoll {
|
||||
op,
|
||||
count,
|
||||
size,
|
||||
filter,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_dice_segments(cmd: &str) -> Result<Vec<Segment>> {
|
||||
let regex = Regex::new(r#"(?x)
|
||||
let regex = Regex::new(
|
||||
r#"(?x)
|
||||
(?P<op>[+\-/*])?
|
||||
\s*
|
||||
(?:
|
||||
@ -85,154 +110,188 @@ fn parse_dice_segments(cmd: &str) -> Result<Vec<Segment>> {
|
||||
(?P<mod>\d+)
|
||||
)
|
||||
)
|
||||
"#).expect("Failed to compile regex");
|
||||
"#,
|
||||
)
|
||||
.expect("Failed to compile regex");
|
||||
|
||||
regex.captures_iter(cmd).map(construct_dice_segment).collect()
|
||||
regex
|
||||
.captures_iter(cmd)
|
||||
.map(construct_dice_segment)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct RollWithModifier {
|
||||
diceroll: Option<Segment>,
|
||||
modifiers: Vec<Segment>
|
||||
diceroll: Option<Segment>,
|
||||
modifiers: Vec<Segment>,
|
||||
}
|
||||
|
||||
fn group_modifiers_to_dicerolls(segments: &[Segment]) -> Vec<RollWithModifier> {
|
||||
let mut results = Vec::new();
|
||||
let mut results = Vec::new();
|
||||
|
||||
let mut current_diceroll : Option<Segment> = None;
|
||||
let mut current_modifiers = Vec::new();
|
||||
let mut current_diceroll: Option<Segment> = None;
|
||||
let mut current_modifiers = Vec::new();
|
||||
|
||||
for segment in segments {
|
||||
if let Segment::DiceRoll {..} = segment {
|
||||
if current_diceroll.is_some() || current_modifiers.len() > 0 {
|
||||
for segment in segments {
|
||||
if let Segment::DiceRoll { .. } = segment {
|
||||
if current_diceroll.is_some() || !current_modifiers.is_empty() {
|
||||
let with_modifier = RollWithModifier {
|
||||
diceroll: current_diceroll.clone(),
|
||||
modifiers: current_modifiers.clone(),
|
||||
};
|
||||
|
||||
current_modifiers.clear();
|
||||
|
||||
results.push(with_modifier);
|
||||
}
|
||||
|
||||
current_diceroll = Some(segment.clone());
|
||||
} else if let Segment::Modifier { .. } = segment {
|
||||
current_modifiers.push(segment.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if current_diceroll.is_some() || !current_modifiers.is_empty() {
|
||||
let with_modifier = RollWithModifier {
|
||||
diceroll: current_diceroll.clone(),
|
||||
modifiers: current_modifiers.clone(),
|
||||
diceroll: current_diceroll,
|
||||
modifiers: current_modifiers.clone(),
|
||||
};
|
||||
|
||||
current_modifiers.clear();
|
||||
|
||||
results.push(with_modifier);
|
||||
}
|
||||
|
||||
current_diceroll = Some(segment.clone());
|
||||
}
|
||||
else if let Segment::Modifier { .. } = segment {
|
||||
current_modifiers.push(segment.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if current_diceroll.is_some() || current_modifiers.len() > 0 {
|
||||
let with_modifier = RollWithModifier {
|
||||
diceroll: current_diceroll.clone(),
|
||||
modifiers: current_modifiers.clone(),
|
||||
};
|
||||
|
||||
current_modifiers.clear();
|
||||
|
||||
results.push(with_modifier);
|
||||
}
|
||||
|
||||
results
|
||||
results
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct Roll {
|
||||
operator: char,
|
||||
results: Vec<i32>,
|
||||
total: i32,
|
||||
operator: char,
|
||||
results: Vec<i32>,
|
||||
total: i32,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq, Clone)]
|
||||
enum RollError {
|
||||
#[error("Roll failed. Empty RollWithModifier")]
|
||||
EmptyRollWithModifier,
|
||||
#[error("Roll failed. Invalid filter operator '{0}'")]
|
||||
InvalidFilterOperator(char),
|
||||
#[error("Roll failed. Invalid segment operator '{0}'")]
|
||||
InvalidOperator(char),
|
||||
#[error("Roll failed. Empty RollWithModifier")]
|
||||
EmptyRollWithModifier,
|
||||
#[error("Roll failed. Invalid filter operator '{0}'")]
|
||||
InvalidFilterOperator(char),
|
||||
#[error("Roll failed. Invalid segment operator '{0}'")]
|
||||
InvalidOperator(char),
|
||||
}
|
||||
|
||||
fn roll_dice_segments<R: Rng>(rwms: &[RollWithModifier], mut rng: R) -> Result<Vec<Roll>, RollError> {
|
||||
rwms.iter()
|
||||
.map(|rwm| {
|
||||
let mut results = Vec::new();
|
||||
let mut total = 0;
|
||||
fn roll_dice_segments<R: Rng>(
|
||||
rwms: &[RollWithModifier],
|
||||
mut rng: R,
|
||||
) -> Result<Vec<Roll>, RollError> {
|
||||
rwms.iter()
|
||||
.map(|rwm| {
|
||||
let mut results = Vec::new();
|
||||
let mut total = 0;
|
||||
|
||||
// store first operator
|
||||
let operator = if let Some(Segment::DiceRoll{op, ..}) = rwm.diceroll {
|
||||
op
|
||||
} else if let Some(Segment::Modifier{op, ..}) = rwm.modifiers.iter().next() {
|
||||
*op
|
||||
} else {
|
||||
return Err(RollError::EmptyRollWithModifier);
|
||||
};
|
||||
// store first operator
|
||||
let operator = if let Some(Segment::DiceRoll { op, .. }) = rwm.diceroll {
|
||||
op
|
||||
} else if let Some(Segment::Modifier { op, .. }) = rwm.modifiers.iter().next() {
|
||||
*op
|
||||
} else {
|
||||
return Err(RollError::EmptyRollWithModifier);
|
||||
};
|
||||
|
||||
if let Some(Segment::DiceRoll{count, size, filter, ..}) = &rwm.diceroll {
|
||||
results = (0..*count).map(|_| {
|
||||
rng.gen_range(1, size + 1)
|
||||
}).collect();
|
||||
if let Some(Segment::DiceRoll {
|
||||
count,
|
||||
size,
|
||||
filter,
|
||||
..
|
||||
}) = &rwm.diceroll
|
||||
{
|
||||
results = (0..*count).map(|_| rng.gen_range(1, size + 1)).collect();
|
||||
|
||||
results.sort();
|
||||
if let Some(filter) = filter {
|
||||
match filter {
|
||||
DiceFilter::DropLowest(n) => {
|
||||
results = results.into_iter().skip(*n).collect();
|
||||
},
|
||||
DiceFilter::DropHighest(n) => {
|
||||
let len = results.len();
|
||||
results = results.into_iter().take(len - n).collect();
|
||||
},
|
||||
DiceFilter::KeepLowest(n) => {
|
||||
results = results.into_iter().take(*n).collect();
|
||||
},
|
||||
DiceFilter::KeepHighest(n) => {
|
||||
let len = results.len();
|
||||
results = results.into_iter().skip(len - n).collect();
|
||||
},
|
||||
}
|
||||
}
|
||||
results.shuffle(&mut rng);
|
||||
results.sort();
|
||||
if let Some(filter) = filter {
|
||||
match filter {
|
||||
DiceFilter::DropLowest(n) => {
|
||||
results = results.into_iter().skip(*n).collect();
|
||||
}
|
||||
DiceFilter::DropHighest(n) => {
|
||||
let len = results.len();
|
||||
results = results.into_iter().take(len - n).collect();
|
||||
}
|
||||
DiceFilter::KeepLowest(n) => {
|
||||
results = results.into_iter().take(*n).collect();
|
||||
}
|
||||
DiceFilter::KeepHighest(n) => {
|
||||
let len = results.len();
|
||||
results = results.into_iter().skip(len - n).collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
results.shuffle(&mut rng);
|
||||
|
||||
total = results.iter().sum();
|
||||
}
|
||||
total = results.iter().sum();
|
||||
}
|
||||
|
||||
for segment in &rwm.modifiers {
|
||||
if let Segment::Modifier{op,amount} = segment {
|
||||
match op {
|
||||
'+' => { total += amount; },
|
||||
'-' => { total -= amount; },
|
||||
'/' => { total /= amount; },
|
||||
'*' => { total *= amount; },
|
||||
_ => {
|
||||
return Err(RollError::InvalidFilterOperator(*op));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
for segment in &rwm.modifiers {
|
||||
if let Segment::Modifier { op, amount } = segment {
|
||||
match op {
|
||||
'+' => {
|
||||
total += amount;
|
||||
}
|
||||
'-' => {
|
||||
total -= amount;
|
||||
}
|
||||
'/' => {
|
||||
total /= amount;
|
||||
}
|
||||
'*' => {
|
||||
total *= amount;
|
||||
}
|
||||
_ => {
|
||||
return Err(RollError::InvalidFilterOperator(*op));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Roll{operator, results, total})
|
||||
Ok(Roll {
|
||||
operator,
|
||||
results,
|
||||
total,
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct RollSet {
|
||||
total: i32,
|
||||
rolls: Vec<(Roll, Vec<Segment>)>,
|
||||
}
|
||||
|
||||
fn roll_dice<R: Rng>(s: &str, mut rng: R) -> Result<RollSet> {
|
||||
let segments = parse_dice_segments(s)?;
|
||||
|
||||
let groups = group_modifiers_to_dicerolls(&segments);
|
||||
|
||||
let rolls = roll_dice_segments(&groups, &mut rng)?;
|
||||
|
||||
let total = rolls.iter().try_fold(0, |acc, roll| match roll.operator {
|
||||
'+' => Ok(acc + roll.total),
|
||||
'-' => Ok(acc - roll.total),
|
||||
'*' => Ok(acc * roll.total),
|
||||
'/' => Ok(acc / roll.total),
|
||||
_ => Err(RollError::InvalidOperator(roll.operator)),
|
||||
})?;
|
||||
|
||||
let rolls_with_modifiers = rolls
|
||||
.into_iter()
|
||||
.zip(groups.into_iter().map(|grp| grp.modifiers))
|
||||
.collect();
|
||||
|
||||
Ok(RollSet {
|
||||
total,
|
||||
rolls: rolls_with_modifiers,
|
||||
})
|
||||
.collect::<Result<Vec<_>,_>>()
|
||||
}
|
||||
|
||||
fn roll_dice<R : Rng>(s: &str, mut rng: R) -> Result<(i32, Vec<(Roll, Vec<Segment>)>)> {
|
||||
let segments = parse_dice_segments(s)?;
|
||||
|
||||
let groups = group_modifiers_to_dicerolls(&segments);
|
||||
|
||||
let rolls = roll_dice_segments(&groups, &mut rng)?;
|
||||
|
||||
let total = rolls.iter().try_fold(0, |acc,roll| match roll.operator {
|
||||
'+' => Ok(acc + roll.total),
|
||||
'-' => Ok(acc - roll.total),
|
||||
'*' => Ok(acc * roll.total),
|
||||
'/' => Ok(acc / roll.total),
|
||||
_ => Err(RollError::InvalidOperator(roll.operator))
|
||||
})?;
|
||||
|
||||
let rolls_with_modifiers = rolls.into_iter()
|
||||
.zip(groups.into_iter().map(|grp| grp.modifiers)).collect();
|
||||
|
||||
Ok((total, rolls_with_modifiers))
|
||||
}
|
||||
|
||||
302
src/lib_test.rs
302
src/lib_test.rs
@ -1,145 +1,249 @@
|
||||
use rand::{rngs::StdRng, SeedableRng};
|
||||
use super::*;
|
||||
use rand::{rngs::StdRng, SeedableRng};
|
||||
|
||||
#[test]
|
||||
fn test_parse_dice_segments() {
|
||||
let mut results = parse_dice_segments("2d6").expect("Failed to unpack results");
|
||||
let mut results = parse_dice_segments("2d6").expect("Failed to unpack results");
|
||||
|
||||
assert_eq!(results, vec![ Segment::DiceRoll{ op: '+', count: 2, size: 6, filter: None } ]);
|
||||
assert_eq!(
|
||||
results,
|
||||
vec![Segment::DiceRoll {
|
||||
op: '+',
|
||||
count: 2,
|
||||
size: 6,
|
||||
filter: None
|
||||
}]
|
||||
);
|
||||
|
||||
results = parse_dice_segments("2d6 + 1d20").expect("Failed to unpack results");
|
||||
results = parse_dice_segments("2d6 + 1d20").expect("Failed to unpack results");
|
||||
|
||||
assert_eq!(results, vec![
|
||||
Segment::DiceRoll{ op: '+', count: 2, size: 6, filter: None },
|
||||
Segment::DiceRoll{ op: '+', count: 1, size: 20, filter: None },
|
||||
]);
|
||||
assert_eq!(
|
||||
results,
|
||||
vec![
|
||||
Segment::DiceRoll {
|
||||
op: '+',
|
||||
count: 2,
|
||||
size: 6,
|
||||
filter: None
|
||||
},
|
||||
Segment::DiceRoll {
|
||||
op: '+',
|
||||
count: 1,
|
||||
size: 20,
|
||||
filter: None
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
results = parse_dice_segments("4d6d1").expect("Failed to unpack results");
|
||||
results = parse_dice_segments("4d6d1").expect("Failed to unpack results");
|
||||
|
||||
assert_eq!(results, vec![
|
||||
Segment::DiceRoll{ op: '+', count: 4, size: 6, filter: Some(DiceFilter::DropLowest(1)) },
|
||||
]);
|
||||
assert_eq!(
|
||||
results,
|
||||
vec![Segment::DiceRoll {
|
||||
op: '+',
|
||||
count: 4,
|
||||
size: 6,
|
||||
filter: Some(DiceFilter::DropLowest(1))
|
||||
},]
|
||||
);
|
||||
|
||||
results = parse_dice_segments("3d8+8").expect("Failed to unpack results");
|
||||
results = parse_dice_segments("3d8+8").expect("Failed to unpack results");
|
||||
|
||||
assert_eq!(results, vec![
|
||||
Segment::DiceRoll{ op: '+', count: 3, size: 8, filter: None },
|
||||
Segment::Modifier{ op: '+', amount: 8 },
|
||||
]);
|
||||
assert_eq!(
|
||||
results,
|
||||
vec![
|
||||
Segment::DiceRoll {
|
||||
op: '+',
|
||||
count: 3,
|
||||
size: 8,
|
||||
filter: None
|
||||
},
|
||||
Segment::Modifier { op: '+', amount: 8 },
|
||||
]
|
||||
);
|
||||
|
||||
results = parse_dice_segments("3d8kl3 - 2d1dh1 / 2").expect("Failed to unpack results");
|
||||
results = parse_dice_segments("3d8kl3 - 2d1dh1 / 2").expect("Failed to unpack results");
|
||||
|
||||
assert_eq!(results, vec![
|
||||
Segment::DiceRoll{ op: '+', count: 3, size: 8, filter: Some(DiceFilter::KeepLowest(3)) },
|
||||
Segment::DiceRoll{ op: '-', count: 2, size: 1, filter: Some(DiceFilter::DropHighest(1)) },
|
||||
Segment::Modifier{ op: '/', amount: 2 },
|
||||
]);
|
||||
assert_eq!(
|
||||
results,
|
||||
vec![
|
||||
Segment::DiceRoll {
|
||||
op: '+',
|
||||
count: 3,
|
||||
size: 8,
|
||||
filter: Some(DiceFilter::KeepLowest(3))
|
||||
},
|
||||
Segment::DiceRoll {
|
||||
op: '-',
|
||||
count: 2,
|
||||
size: 1,
|
||||
filter: Some(DiceFilter::DropHighest(1))
|
||||
},
|
||||
Segment::Modifier { op: '/', amount: 2 },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_group_modifiers_to_dicerolls() {
|
||||
let segments = parse_dice_segments("2d20 / 2 + 2 + 4d6 * 2 * 2").expect("Failed to unpack results");
|
||||
let segments =
|
||||
parse_dice_segments("2d20 / 2 + 2 + 4d6 * 2 * 2").expect("Failed to unpack results");
|
||||
|
||||
let results = group_modifiers_to_dicerolls(&segments);
|
||||
let results = group_modifiers_to_dicerolls(&segments);
|
||||
|
||||
assert_eq!(results, vec![
|
||||
RollWithModifier{
|
||||
diceroll: Some(Segment::DiceRoll{ op: '+', count: 2, size: 20, filter: None }),
|
||||
modifiers: vec![
|
||||
Segment::Modifier{ op: '/', amount: 2 },
|
||||
Segment::Modifier{ op: '+', amount: 2 },
|
||||
]
|
||||
},
|
||||
RollWithModifier{
|
||||
diceroll: Some(Segment::DiceRoll{ op: '+', count: 4, size: 6, filter: None }),
|
||||
modifiers: vec![
|
||||
Segment::Modifier{ op: '*', amount: 2 },
|
||||
Segment::Modifier{ op: '*', amount: 2 },
|
||||
]
|
||||
},
|
||||
]);
|
||||
assert_eq!(
|
||||
results,
|
||||
vec![
|
||||
RollWithModifier {
|
||||
diceroll: Some(Segment::DiceRoll {
|
||||
op: '+',
|
||||
count: 2,
|
||||
size: 20,
|
||||
filter: None
|
||||
}),
|
||||
modifiers: vec![
|
||||
Segment::Modifier { op: '/', amount: 2 },
|
||||
Segment::Modifier { op: '+', amount: 2 },
|
||||
]
|
||||
},
|
||||
RollWithModifier {
|
||||
diceroll: Some(Segment::DiceRoll {
|
||||
op: '+',
|
||||
count: 4,
|
||||
size: 6,
|
||||
filter: None
|
||||
}),
|
||||
modifiers: vec![
|
||||
Segment::Modifier { op: '*', amount: 2 },
|
||||
Segment::Modifier { op: '*', amount: 2 },
|
||||
]
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_group_modifiers_to_dicerolls_no_diceroll() {
|
||||
let segments = parse_dice_segments("* 2 * 2").expect("Failed to unpack results");
|
||||
let segments = parse_dice_segments("* 2 * 2").expect("Failed to unpack results");
|
||||
|
||||
let results = group_modifiers_to_dicerolls(&segments);
|
||||
let results = group_modifiers_to_dicerolls(&segments);
|
||||
|
||||
assert_eq!(results, vec![
|
||||
RollWithModifier{
|
||||
diceroll: None,
|
||||
modifiers: vec![
|
||||
Segment::Modifier{ op: '*', amount: 2 },
|
||||
Segment::Modifier{ op: '*', amount: 2 },
|
||||
]
|
||||
},
|
||||
]);
|
||||
assert_eq!(
|
||||
results,
|
||||
vec![RollWithModifier {
|
||||
diceroll: None,
|
||||
modifiers: vec![
|
||||
Segment::Modifier { op: '*', amount: 2 },
|
||||
Segment::Modifier { op: '*', amount: 2 },
|
||||
]
|
||||
},]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_roll_dice_segments() {
|
||||
let mut rng = StdRng::seed_from_u64(2);
|
||||
let mut rng = StdRng::seed_from_u64(2);
|
||||
|
||||
let segments = parse_dice_segments("4d6").expect("Failed to unpack results");
|
||||
let rolls_with_modifiers = group_modifiers_to_dicerolls(&segments);
|
||||
let results = roll_dice_segments(&rolls_with_modifiers, &mut rng);
|
||||
let segments = parse_dice_segments("4d6").expect("Failed to unpack results");
|
||||
let rolls_with_modifiers = group_modifiers_to_dicerolls(&segments);
|
||||
let results = roll_dice_segments(&rolls_with_modifiers, &mut rng);
|
||||
|
||||
assert_eq!(results, Ok(vec![Roll{operator: '+', results: vec![4, 5, 5, 1], total: 15}]));
|
||||
assert_eq!(
|
||||
results,
|
||||
Ok(vec![Roll {
|
||||
operator: '+',
|
||||
results: vec![4, 5, 5, 1],
|
||||
total: 15
|
||||
}])
|
||||
);
|
||||
|
||||
let segments = parse_dice_segments("4d6d1 + 2").expect("Failed to unpack results");
|
||||
let rolls_with_modifiers = group_modifiers_to_dicerolls(&segments);
|
||||
let results = roll_dice_segments(&rolls_with_modifiers, &mut rng);
|
||||
let segments = parse_dice_segments("4d6d1 + 2").expect("Failed to unpack results");
|
||||
let rolls_with_modifiers = group_modifiers_to_dicerolls(&segments);
|
||||
let results = roll_dice_segments(&rolls_with_modifiers, &mut rng);
|
||||
|
||||
assert_eq!(results, Ok(vec![Roll{operator: '+', results: vec![1, 1, 5], total: 9}]));
|
||||
assert_eq!(
|
||||
results,
|
||||
Ok(vec![Roll {
|
||||
operator: '+',
|
||||
results: vec![1, 1, 5],
|
||||
total: 9
|
||||
}])
|
||||
);
|
||||
|
||||
let segments = parse_dice_segments("+ 2 + 2d12kh1 / 2").expect("Failed to unpack results");
|
||||
let rolls_with_modifiers = group_modifiers_to_dicerolls(&segments);
|
||||
let results = roll_dice_segments(&rolls_with_modifiers, &mut rng);
|
||||
let segments = parse_dice_segments("+ 2 + 2d12kh1 / 2").expect("Failed to unpack results");
|
||||
let rolls_with_modifiers = group_modifiers_to_dicerolls(&segments);
|
||||
let results = roll_dice_segments(&rolls_with_modifiers, &mut rng);
|
||||
|
||||
assert_eq!(results, Ok(vec![
|
||||
Roll{operator: '+', results: vec![], total: 2},
|
||||
Roll{operator: '+', results: vec![12], total: 6},
|
||||
]));
|
||||
assert_eq!(
|
||||
results,
|
||||
Ok(vec![
|
||||
Roll {
|
||||
operator: '+',
|
||||
results: vec![],
|
||||
total: 2
|
||||
},
|
||||
Roll {
|
||||
operator: '+',
|
||||
results: vec![12],
|
||||
total: 6
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
let segments = parse_dice_segments("1d6 * 1d6 + 2 / 2").expect("Failed to unpack results");
|
||||
let rolls_with_modifiers = group_modifiers_to_dicerolls(&segments);
|
||||
let results = roll_dice_segments(&rolls_with_modifiers, &mut rng);
|
||||
let segments = parse_dice_segments("1d6 * 1d6 + 2 / 2").expect("Failed to unpack results");
|
||||
let rolls_with_modifiers = group_modifiers_to_dicerolls(&segments);
|
||||
let results = roll_dice_segments(&rolls_with_modifiers, &mut rng);
|
||||
|
||||
assert_eq!(results, Ok(vec![
|
||||
Roll{operator: '+', results: vec![1], total: 1},
|
||||
Roll{operator: '*', results: vec![6], total: 4},
|
||||
]));
|
||||
assert_eq!(
|
||||
results,
|
||||
Ok(vec![
|
||||
Roll {
|
||||
operator: '+',
|
||||
results: vec![1],
|
||||
total: 1
|
||||
},
|
||||
Roll {
|
||||
operator: '*',
|
||||
results: vec![6],
|
||||
total: 4
|
||||
},
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_roll_dice() {
|
||||
let input = "4d6d2 + 2 - 1d4";
|
||||
|
||||
let input = "4d6d2 + 2 - 1d4";
|
||||
let mut rng = StdRng::seed_from_u64(2);
|
||||
|
||||
let mut rng = StdRng::seed_from_u64(2);
|
||||
let result = roll_dice(input, &mut rng);
|
||||
|
||||
let result = roll_dice(input, &mut rng);
|
||||
|
||||
let expected = (8i32, vec![
|
||||
(Roll {
|
||||
operator: '+',
|
||||
results: vec![5, 5],
|
||||
total: 12
|
||||
},
|
||||
vec![Segment::Modifier { op: '+', amount: 2 }]),
|
||||
|
||||
(Roll {
|
||||
operator: '-',
|
||||
results: vec![4],
|
||||
total: 4
|
||||
},
|
||||
vec![])]);
|
||||
|
||||
if let Ok(result) = result {
|
||||
assert_eq!(result, expected);
|
||||
} else {
|
||||
panic!("Expected Ok, was: {:?}", result);
|
||||
}
|
||||
let expected = RollSet {
|
||||
total: 8i32,
|
||||
rolls: vec![
|
||||
(
|
||||
Roll {
|
||||
operator: '+',
|
||||
results: vec![5, 5],
|
||||
total: 12,
|
||||
},
|
||||
vec![Segment::Modifier { op: '+', amount: 2 }],
|
||||
),
|
||||
(
|
||||
Roll {
|
||||
operator: '-',
|
||||
results: vec![4],
|
||||
total: 4,
|
||||
},
|
||||
vec![],
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
if let Ok(result) = result {
|
||||
assert_eq!(result, expected);
|
||||
} else {
|
||||
panic!("Expected Ok, was: {:?}", result);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user