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",
"serde",
"sled",
"tempfile",
"thiserror",
"tokio",
"toml",

View File

@ -46,3 +46,6 @@ features = ['derive']
[dependencies.tokio]
version = "0.2"
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::roll::{Roll, Rolled};
use itertools::Itertools;
@ -107,12 +108,14 @@ pub struct DicePool {
pub(crate) modifiers: DicePoolModifiers,
}
fn calculate_dice_amount(amounts: &Vec<Amount>) -> Result<i32, BotError> {
let dice_amount: Result<i32, BotError> = amounts
fn calculate_dice_amount(pool: &DicePoolWithContext) -> Result<i32, BotError> {
let dice_amount: Result<i32, BotError> = pool
.0
.amounts
.iter()
.map(|amount| match &amount.element {
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>, _>>()
.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>;
fn roll(&self) -> Result<RolledDicePool, BotError> {
@ -341,21 +347,30 @@ fn roll_die<R: DieRoller>(roller: &mut R, pool: &DicePool) -> Vec<i32> {
results
}
fn handle_variable(_variable: &str) -> Result<i32, BotError> {
Err(BotError::VariablesNotSupported)
fn handle_variable(ctx: &Context, variable: &str) -> Result<i32, BotError> {
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
///methods.
fn roll_dice<R: DieRoller>(pool: &DicePool, roller: &mut R) -> Result<RolledDicePool, BotError> {
let num_dice = calculate_dice_amount(&pool.amounts)?;
let rolls: Vec<i32> = (0..num_dice).flat_map(|_| roll_die(roller, pool)).collect();
Ok(RolledDicePool::from(pool, num_dice, rolls))
fn roll_dice<R: DieRoller>(
pool: &DicePoolWithContext,
roller: &mut R,
) -> 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)]
mod tests {
use super::*;
use crate::db::Database;
use tempfile::tempdir;
///Instead of being random, generate a series of numbers we have complete
///control over.
@ -477,9 +492,13 @@ mod tests {
#[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_with_ctx = DicePoolWithContext(&pool, &ctx);
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());
let roll = result.unwrap().roll;
@ -488,15 +507,38 @@ mod tests {
#[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_with_ctx = DicePoolWithContext(&pool, &ctx);
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());
let roll = result.unwrap();
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
#[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::db::DataError;
use crate::dice::ElementExpression;
@ -63,8 +63,9 @@ impl Command for PoolRollCommand {
"roll dice pool"
}
fn execute(&self, _ctx: &Context) -> Execution {
let roll_result = self.0.roll();
fn execute(&self, ctx: &Context) -> Execution {
let pool_with_ctx = DicePoolWithContext(&self.0, ctx);
let roll_result = pool_with_ctx.roll();
let (plain, html) = match roll_result {
Ok(rolled_pool) => {