Resolve variables in dice pools.
This commit is contained in:
parent
dc8a74cc35
commit
af2e58351f
|
@ -299,6 +299,7 @@ dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"sled",
|
"sled",
|
||||||
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
Loading…
Reference in New Issue