aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author简律纯 <i@jyunko.cn>2025-09-12 18:19:16 +0800
committer简律纯 <i@jyunko.cn>2025-09-12 18:19:16 +0800
commit2778db81c6973078dc0e8e04c4bb711143aef84d (patch)
treea262ee069fe4155c1bf315ed549161d3ee4ee1f8
parent899ca820e34b1b62190e88da71cf734295974a19 (diff)
downloadOneRoll-2778db81c6973078dc0e8e04c4bb711143aef84d.tar.gz
OneRoll-2778db81c6973078dc0e8e04c4bb711143aef84d.zip
feat: add new reroll modifiers for enhanced dice functionality
-rw-r--r--README.md2
-rw-r--r--src/calculator.rs25
-rw-r--r--src/oneroll/grammar.pest4
-rw-r--r--src/parser.rs10
-rw-r--r--src/types.rs2
5 files changed, 42 insertions, 1 deletions
diff --git a/README.md b/README.md
index 771fb9a..8b3c409 100644
--- a/README.md
+++ b/README.md
@@ -94,6 +94,8 @@ python -m oneroll --tui
- `u` - 去重: `4d6u` (相同点数只计一次)
- `s` - 排序(不改变总和): `5d6s`
- `cV` - 计数值V出现次数(作为一个结果输出): `5d6c6`
+- `RX` - 直到重投(直至 > X,含安全上限): `d20R10`
+- `aX` - 重投并相加(若 ≤ X 则再掷并加到结果): `4d6a2`
### 复合表达式
- `6d6dl2kh3` - 丢弃最低2个,然后取最高3个
diff --git a/src/calculator.rs b/src/calculator.rs
index 5376f76..07518b0 100644
--- a/src/calculator.rs
+++ b/src/calculator.rs
@@ -45,7 +45,7 @@ impl DiceCalculator {
}
}
- // handle reroll
+ // handle reroll variants
for modifier in &dice.modifiers {
match modifier {
DiceModifier::Reroll(threshold) => {
@@ -60,6 +60,27 @@ impl DiceCalculator {
final_rolls[pos] = new_roll as i32;
}
}
+ DiceModifier::RerollUntil(threshold) => {
+ // keep rolling until > threshold (safety cap)
+ let mut current = *final_rolls.last().unwrap_or(&((roll) as i32));
+ let mut attempts = 0;
+ const MAX_ATTEMPTS: usize = 100;
+ while current <= *threshold && attempts < MAX_ATTEMPTS {
+ let new_roll = rand::random::<u32>() % dice.sides as u32 + 1;
+ current = new_roll as i32;
+ final_rolls = vec![current];
+ attempts += 1;
+ }
+ }
+ DiceModifier::RerollAndAdd(threshold) => {
+ // if <= threshold, roll again and add to the last value
+ if final_rolls.iter().any(|&r| r <= *threshold) {
+ let new_roll = rand::random::<u32>() % dice.sides as u32 + 1;
+ let mut sum = final_rolls.iter().sum::<i32>();
+ sum += new_roll as i32;
+ final_rolls = vec![sum];
+ }
+ }
_ => {}
}
}
@@ -246,6 +267,8 @@ impl DiceCalculator {
DiceModifier::ExplodeKeepHigh(n) => result.push_str(&format!("K{}", n)),
DiceModifier::Reroll(n) => result.push_str(&format!("r{}", n)),
DiceModifier::RerollOnce(n) => result.push_str(&format!("ro{}", n)),
+ DiceModifier::RerollUntil(n) => result.push_str(&format!("R{}", n)),
+ DiceModifier::RerollAndAdd(n) => result.push_str(&format!("a{}", n)),
DiceModifier::KeepAlias(n) => result.push_str(&format!("k{}", n)),
DiceModifier::KeepHigh(n) => result.push_str(&format!("kh{}", n)),
DiceModifier::KeepLow(n) => result.push_str(&format!("kl{}", n)),
diff --git a/src/oneroll/grammar.pest b/src/oneroll/grammar.pest
index 09b86f8..006be64 100644
--- a/src/oneroll/grammar.pest
+++ b/src/oneroll/grammar.pest
@@ -26,6 +26,8 @@ modifier = {
| explode_keep_high
| reroll
| reroll_once
+ | reroll_until
+ | reroll_add
| keep_alias
| keep_high
| keep_low
@@ -41,6 +43,8 @@ explode_alias = { "e" }
explode_keep_high = { "K" ~ number }
reroll = { "r" ~ number }
reroll_once = { "ro" ~ number }
+reroll_until = { "R" ~ number }
+reroll_add = { "a" ~ number }
keep_alias = { "k" ~ number }
keep_high = { "kh" ~ number }
keep_low = { "kl" ~ number }
diff --git a/src/parser.rs b/src/parser.rs
index 50231c0..20cd992 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -126,6 +126,16 @@ impl DiceParser {
.map_err(|_| DiceError::ParseError("无效的条件重投数值".to_string()))?;
Ok(DiceModifier::RerollOnce(num))
}
+ Rule::reroll_until => {
+ let num = inner.into_inner().next().unwrap().as_str().parse::<i32>()
+ .map_err(|_| DiceError::ParseError("无效的直到重投数值".to_string()))?;
+ Ok(DiceModifier::RerollUntil(num))
+ }
+ Rule::reroll_add => {
+ let num = inner.into_inner().next().unwrap().as_str().parse::<i32>()
+ .map_err(|_| DiceError::ParseError("无效的重投并相加数值".to_string()))?;
+ Ok(DiceModifier::RerollAndAdd(num))
+ }
Rule::keep_alias => {
let num = inner.into_inner().next().unwrap().as_str().parse::<i32>()
.map_err(|_| DiceError::ParseError("无效的取高数值".to_string()))?;
diff --git a/src/types.rs b/src/types.rs
index aad0080..1d5d369 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -24,6 +24,8 @@ pub enum DiceModifier {
ExplodeKeepHigh(i32), // KX == explode then keep high X
Reroll(i32), // rX
RerollOnce(i32), // roX
+ RerollUntil(i32), // RX (until > X; with cap)
+ RerollAndAdd(i32), // aX (reroll if <= X and add)
KeepAlias(i32), // kX == khX
KeepHigh(i32), // khX
KeepLow(i32), // klX