Resolve variables in dice pools.

This commit is contained in:
projectmoon 2020-10-16 21:20:17 +00:00 committed by ProjectMoon
parent dc8a74cc35
commit af2e58351f
4 changed files with 62 additions and 15 deletions

1
Cargo.lock generated
View File

@ -299,6 +299,7 @@ dependencies = [
"rand", "rand",
"serde", "serde",
"sled", "sled",
"tempfile",
"thiserror", "thiserror",
"tokio", "tokio",
"toml", "toml",

View File

@ -46,3 +46,6 @@ features = ['derive']
[dependencies.tokio] [dependencies.tokio]
version = "0.2" version = "0.2"
features = ["rt-core", "rt-util", "macros", "time", "signal"] features = ["rt-core", "rt-util", "macros", "time", "signal"]
[dev-dependencies]
tempfile = "3.1"

View File

@ -1,3 +1,4 @@
use crate::context::Context;
use crate::error::BotError; use crate::error::BotError;
use crate::roll::{Roll, Rolled}; use crate::roll::{Roll, Rolled};
use itertools::Itertools; use itertools::Itertools;
@ -107,12 +108,14 @@ pub struct DicePool {
pub(crate) modifiers: DicePoolModifiers, pub(crate) modifiers: DicePoolModifiers,
} }
fn calculate_dice_amount(amounts: &Vec<Amount>) -> Result<i32, BotError> { fn calculate_dice_amount(pool: &DicePoolWithContext) -> Result<i32, BotError> {
let dice_amount: Result<i32, BotError> = amounts let dice_amount: Result<i32, BotError> = pool
.0
.amounts
.iter() .iter()
.map(|amount| match &amount.element { .map(|amount| match &amount.element {
Element::Number(num_dice) => Ok(*num_dice * amount.operator.mult()), Element::Number(num_dice) => Ok(*num_dice * amount.operator.mult()),
Element::Variable(variable) => handle_variable(&variable), Element::Variable(variable) => handle_variable(&pool.1, &variable),
}) })
.collect::<Result<Vec<i32>, _>>() .collect::<Result<Vec<i32>, _>>()
.map(|numbers| numbers.iter().sum()); .map(|numbers| numbers.iter().sum());
@ -240,7 +243,10 @@ impl DicePoolRoll {
} }
} }
impl Roll for DicePool { /// Attach a Context to a dice pool. Needed for database access.
pub struct DicePoolWithContext<'a>(pub &'a DicePool, pub &'a Context<'a>);
impl Roll for DicePoolWithContext<'_> {
type Output = Result<RolledDicePool, BotError>; type Output = Result<RolledDicePool, BotError>;
fn roll(&self) -> Result<RolledDicePool, BotError> { fn roll(&self) -> Result<RolledDicePool, BotError> {
@ -341,21 +347,30 @@ fn roll_die<R: DieRoller>(roller: &mut R, pool: &DicePool) -> Vec<i32> {
results results
} }
fn handle_variable(_variable: &str) -> Result<i32, BotError> { fn handle_variable(ctx: &Context, variable: &str) -> Result<i32, BotError> {
Err(BotError::VariablesNotSupported) ctx.db
.get_user_variable(&ctx.room_id, &ctx.username, variable)
.map_err(|e| e.into())
} }
///Roll the dice in a dice pool, according to behavior documented in the various rolling ///Roll the dice in a dice pool, according to behavior documented in the various rolling
///methods. ///methods.
fn roll_dice<R: DieRoller>(pool: &DicePool, roller: &mut R) -> Result<RolledDicePool, BotError> { fn roll_dice<R: DieRoller>(
let num_dice = calculate_dice_amount(&pool.amounts)?; pool: &DicePoolWithContext,
let rolls: Vec<i32> = (0..num_dice).flat_map(|_| roll_die(roller, pool)).collect(); roller: &mut R,
Ok(RolledDicePool::from(pool, num_dice, rolls)) ) -> Result<RolledDicePool, BotError> {
let num_dice = calculate_dice_amount(&pool)?;
let rolls: Vec<i32> = (0..num_dice)
.flat_map(|_| roll_die(roller, &pool.0))
.collect();
Ok(RolledDicePool::from(&pool.0, num_dice, rolls))
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::db::Database;
use tempfile::tempdir;
///Instead of being random, generate a series of numbers we have complete ///Instead of being random, generate a series of numbers we have complete
///control over. ///control over.
@ -477,9 +492,13 @@ mod tests {
#[test] #[test]
pub fn no_explode_roll_test() { pub fn no_explode_roll_test() {
let db = Database::new(&sled::open(tempdir().unwrap()).unwrap());
let ctx = Context::new(&db, "roomid", "username", "message");
let pool = DicePool::easy_pool(1, DicePoolQuality::NoExplode); let pool = DicePool::easy_pool(1, DicePoolQuality::NoExplode);
let pool_with_ctx = DicePoolWithContext(&pool, &ctx);
let mut roller = SequentialDieRoller::new(vec![10, 8]); let mut roller = SequentialDieRoller::new(vec![10, 8]);
let result = roll_dice(&pool, &mut roller); let result = roll_dice(&pool_with_ctx, &mut roller);
assert!(result.is_ok()); assert!(result.is_ok());
let roll = result.unwrap().roll; let roll = result.unwrap().roll;
@ -488,15 +507,38 @@ mod tests {
#[test] #[test]
pub fn number_of_dice_equality_test() { pub fn number_of_dice_equality_test() {
let db = Database::new(&sled::open(tempdir().unwrap()).unwrap());
let ctx = Context::new(&db, "roomid", "username", "message");
let pool = DicePool::easy_pool(5, DicePoolQuality::NoExplode); let pool = DicePool::easy_pool(5, DicePoolQuality::NoExplode);
let pool_with_ctx = DicePoolWithContext(&pool, &ctx);
let mut roller = SequentialDieRoller::new(vec![1, 2, 3, 4, 5]); let mut roller = SequentialDieRoller::new(vec![1, 2, 3, 4, 5]);
let result = roll_dice(&pool, &mut roller); let result = roll_dice(&pool_with_ctx, &mut roller);
assert!(result.is_ok()); assert!(result.is_ok());
let roll = result.unwrap(); let roll = result.unwrap();
assert_eq!(5, roll.num_dice); assert_eq!(5, roll.num_dice);
} }
#[test]
fn can_resolve_variables_test() {
let db = Database::new(&sled::open(tempdir().unwrap()).unwrap());
let ctx = Context::new(&db, "roomid", "username", "message");
db.set_user_variable(&ctx.room_id, &ctx.username, "myvariable", 10)
.expect("could not set myvariable to 10");
let amounts = vec![Amount {
operator: Operator::Plus,
element: Element::Variable("myvariable".to_owned()),
}];
let pool = DicePool::new(amounts, DicePoolModifiers::default());
let pool_with_ctx = DicePoolWithContext(&pool, &ctx);
assert_eq!(calculate_dice_amount(&pool_with_ctx).unwrap(), 10);
}
//DicePool tests //DicePool tests
#[test] #[test]
fn easy_pool_chance_die_test() { fn easy_pool_chance_die_test() {

View File

@ -1,4 +1,4 @@
use crate::cofd::dice::DicePool; use crate::cofd::dice::{DicePool, DicePoolWithContext};
use crate::context::Context; use crate::context::Context;
use crate::db::DataError; use crate::db::DataError;
use crate::dice::ElementExpression; use crate::dice::ElementExpression;
@ -63,8 +63,9 @@ impl Command for PoolRollCommand {
"roll dice pool" "roll dice pool"
} }
fn execute(&self, _ctx: &Context) -> Execution { fn execute(&self, ctx: &Context) -> Execution {
let roll_result = self.0.roll(); let pool_with_ctx = DicePoolWithContext(&self.0, ctx);
let roll_result = pool_with_ctx.roll();
let (plain, html) = match roll_result { let (plain, html) = match roll_result {
Ok(rolled_pool) => { Ok(rolled_pool) => {