aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/calculator.rs
diff options
context:
space:
mode:
author简律纯 <i@jyunko.cn>2025-09-12 04:02:02 +0800
committer简律纯 <i@jyunko.cn>2025-09-12 04:02:02 +0800
commit0288d0956330d5ac8db48b752240f723e8703929 (patch)
tree0297dee9f8166af0a856dd3a1057ad5f25f14c6a /src/calculator.rs
parent5135876b5e2a6c40232414ea0b7eb875fa225cf0 (diff)
downloadOneRoll-0288d0956330d5ac8db48b752240f723e8703929.tar.gz
OneRoll-0288d0956330d5ac8db48b752240f723e8703929.zip
feat: initial basic roll features
Diffstat (limited to 'src/calculator.rs')
-rw-r--r--src/calculator.rs212
1 files changed, 212 insertions, 0 deletions
diff --git a/src/calculator.rs b/src/calculator.rs
new file mode 100644
index 0000000..9611a2b
--- /dev/null
+++ b/src/calculator.rs
@@ -0,0 +1,212 @@
+use crate::errors::DiceError;
+use crate::types::{DiceModifier, DiceResult, DiceRoll, Expression, VariableStore};
+use rand::Rng;
+
+pub struct DiceCalculator {
+ pub variables: VariableStore,
+}
+
+impl DiceCalculator {
+ pub fn new() -> Self {
+ Self {
+ variables: VariableStore::new(),
+ }
+ }
+
+ pub fn roll_dice(&mut self, dice: &DiceRoll) -> Result<Vec<Vec<i32>>, DiceError> {
+ if dice.count <= 0 || dice.sides <= 0 {
+ return Err(DiceError::InvalidExpression(
+ "骰子数量和面数必须大于0".to_string(),
+ ));
+ }
+
+ let mut rolls = Vec::new();
+
+ for _ in 0..dice.count {
+ let mut roll = rand::random::<u32>() % dice.sides as u32 + 1;
+ let mut final_rolls = vec![roll as i32];
+
+ // handle exploded throwing
+ for modifier in &dice.modifiers {
+ match modifier {
+ DiceModifier::Explode => {
+ while roll == dice.sides as u32 {
+ roll = rand::random::<u32>() % dice.sides as u32 + 1;
+ final_rolls.push(roll as i32);
+ }
+ }
+ _ => {}
+ }
+ }
+
+ // handle reroll
+ for modifier in &dice.modifiers {
+ match modifier {
+ DiceModifier::Reroll(threshold) => {
+ if final_rolls.iter().any(|&r| r <= *threshold) {
+ let new_roll = rand::random::<u32>() % dice.sides as u32 + 1;
+ final_rolls = vec![new_roll as i32];
+ }
+ }
+ DiceModifier::RerollOnce(threshold) => {
+ if let Some(pos) = final_rolls.iter().position(|&r| r <= *threshold) {
+ let new_roll = rand::random::<u32>() % dice.sides as u32 + 1;
+ final_rolls[pos] = new_roll as i32;
+ }
+ }
+ _ => {}
+ }
+ }
+
+ rolls.push(final_rolls);
+ }
+
+ // handle high/low and discard high/low
+ let mut final_rolls = rolls;
+ for modifier in &dice.modifiers {
+ match modifier {
+ DiceModifier::KeepHigh(n) => {
+ let all_values: Vec<i32> = final_rolls.iter().flatten().cloned().collect();
+ let mut sorted = all_values;
+ sorted.sort_by(|a, b| b.cmp(a));
+ final_rolls = sorted.iter().take(*n as usize).map(|&v| vec![v]).collect();
+ }
+ DiceModifier::KeepLow(n) => {
+ let all_values: Vec<i32> = final_rolls.iter().flatten().cloned().collect();
+ let mut sorted = all_values;
+ sorted.sort();
+ final_rolls = sorted.iter().take(*n as usize).map(|&v| vec![v]).collect();
+ }
+ DiceModifier::DropHigh(n) => {
+ let all_values: Vec<i32> = final_rolls.iter().flatten().cloned().collect();
+ let mut sorted = all_values;
+ sorted.sort_by(|a, b| b.cmp(a));
+ final_rolls = sorted.iter().skip(*n as usize).map(|&v| vec![v]).collect();
+ }
+ DiceModifier::DropLow(n) => {
+ let all_values: Vec<i32> = final_rolls.iter().flatten().cloned().collect();
+ let mut sorted = all_values;
+ sorted.sort();
+ final_rolls = sorted.iter().skip(*n as usize).map(|&v| vec![v]).collect();
+ }
+ _ => {}
+ }
+ }
+
+ Ok(final_rolls)
+ }
+
+ pub fn evaluate_expression(&mut self, expr: &Expression) -> Result<DiceResult, DiceError> {
+ match expr {
+ Expression::Number(n) => Ok(DiceResult {
+ expression: n.to_string(),
+ total: *n,
+ rolls: vec![],
+ details: format!("{}", n),
+ comment: None,
+ }),
+ Expression::DiceRoll(dice) => {
+ let rolls = self.roll_dice(dice)?;
+ let total: i32 = rolls.iter().flatten().sum();
+ let details = format!(
+ "{}d{}{} = {} (详情: {:?})",
+ dice.count,
+ dice.sides,
+ self.modifiers_to_string(&dice.modifiers),
+ total,
+ rolls
+ );
+ Ok(DiceResult {
+ expression: format!("{}d{}", dice.count, dice.sides),
+ total,
+ rolls,
+ details,
+ comment: None,
+ })
+ }
+ Expression::Add(left, right) => {
+ let left_result = self.evaluate_expression(left)?;
+ let right_result = self.evaluate_expression(right)?;
+ Ok(DiceResult {
+ expression: format!("({}) + ({})", left_result.expression, right_result.expression),
+ total: left_result.total + right_result.total,
+ rolls: [left_result.rolls, right_result.rolls].concat(),
+ details: format!("{} + {} = {}", left_result.total, right_result.total, left_result.total + right_result.total),
+ comment: None,
+ })
+ }
+ Expression::Subtract(left, right) => {
+ let left_result = self.evaluate_expression(left)?;
+ let right_result = self.evaluate_expression(right)?;
+ Ok(DiceResult {
+ expression: format!("({}) - ({})", left_result.expression, right_result.expression),
+ total: left_result.total - right_result.total,
+ rolls: [left_result.rolls, right_result.rolls].concat(),
+ details: format!("{} - {} = {}", left_result.total, right_result.total, left_result.total - right_result.total),
+ comment: None,
+ })
+ }
+ Expression::Multiply(left, right) => {
+ let left_result = self.evaluate_expression(left)?;
+ let right_result = self.evaluate_expression(right)?;
+ Ok(DiceResult {
+ expression: format!("({}) * ({})", left_result.expression, right_result.expression),
+ total: left_result.total * right_result.total,
+ rolls: [left_result.rolls, right_result.rolls].concat(),
+ details: format!("{} * {} = {}", left_result.total, right_result.total, left_result.total * right_result.total),
+ comment: None,
+ })
+ }
+ Expression::Divide(left, right) => {
+ let left_result = self.evaluate_expression(left)?;
+ let right_result = self.evaluate_expression(right)?;
+ if right_result.total == 0 {
+ return Err(DiceError::CalculationError("除零错误".to_string()));
+ }
+ Ok(DiceResult {
+ expression: format!("({}) / ({})", left_result.expression, right_result.expression),
+ total: left_result.total / right_result.total,
+ rolls: [left_result.rolls, right_result.rolls].concat(),
+ details: format!("{} / {} = {}", left_result.total, right_result.total, left_result.total / right_result.total),
+ comment: None,
+ })
+ }
+ Expression::Power(left, right) => {
+ let left_result = self.evaluate_expression(left)?;
+ let right_result = self.evaluate_expression(right)?;
+ let result = left_result.total.pow(right_result.total as u32);
+ Ok(DiceResult {
+ expression: format!("({}) ^ ({})", left_result.expression, right_result.expression),
+ total: result,
+ rolls: [left_result.rolls, right_result.rolls].concat(),
+ details: format!("{} ^ {} = {}", left_result.total, right_result.total, result),
+ comment: None,
+ })
+ }
+ Expression::Paren(expr) => self.evaluate_expression(expr),
+ Expression::WithComment(expr, comment) => {
+ let mut result = self.evaluate_expression(expr)?;
+ result.comment = comment.clone();
+ Ok(result)
+ }
+ }
+ }
+
+
+ pub fn modifiers_to_string(&self, modifiers: &[DiceModifier]) -> String {
+ let mut result = String::new();
+ for modifier in modifiers {
+ match modifier {
+ DiceModifier::Explode => result.push('!'),
+ DiceModifier::Reroll(n) => result.push_str(&format!("r{}", n)),
+ DiceModifier::RerollOnce(n) => result.push_str(&format!("ro{}", n)),
+ DiceModifier::KeepHigh(n) => result.push_str(&format!("kh{}", n)),
+ DiceModifier::KeepLow(n) => result.push_str(&format!("kl{}", n)),
+ DiceModifier::DropHigh(n) => result.push_str(&format!("dh{}", n)),
+ DiceModifier::DropLow(n) => result.push_str(&format!("dl{}", n)),
+ }
+ }
+ result
+ }
+
+}