骰子公式求值器扩展需求:支持含骰子表达式的数学运算
当然可以用正则来提取所有骰子表达式!这其实是扩展你现有骰子计算功能最直接的思路之一,完全可行。我来一步步给你拆解实现方案:
核心思路
先通过正则匹配出表达式中所有的骰子模式(比如3d8、2d4,甚至支持省略数量的d20),把每个骰子表达式替换为实际投掷后的总和,得到纯数学表达式后,再用JS或者Java的表达式引擎求值,自动处理运算优先级。
步骤1:用正则匹配骰子模式
你需要的正则表达式可以写成(\d*)d(\d+),它能匹配两种情况:
- 带数量的骰子:比如
3d8,捕获组1是3(骰子数量),捕获组2是8(面数) - 省略数量的骰子:比如
d20,捕获组1为空字符串(默认1个骰子),捕获组2是20
如果要支持负数数量(比如-2d6),可以调整为(-?\d*)d(\d+),后续处理时判断数量的正负即可。
步骤2:替换骰子表达式为计算值
用Java的Matcher类遍历所有匹配结果,对每个骰子表达式调用你现有的投掷逻辑计算总和,然后替换掉原表达式中的骰子部分。这里要注意复用Random实例,避免频繁创建影响性能和随机性。
步骤3:求值处理后的纯数学表达式
你提到的用JS求值是个简单的方案,Java自带了ScriptEngine可以直接执行JS代码。需要注意把表达式中的×替换成JS支持的*符号,否则会报错。如果担心脚本注入风险(比如不可控的用户输入),可以用更安全的Java表达式求值库,比如Apache Commons Math的ExpressionEvaluator。
完整示例代码
import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; public class DiceCalculator { // 复用Random实例,提升性能和随机性一致性 private static final Random random = new Random(); // 匹配骰子表达式,支持可选数量、负数数量 private static final Pattern DICE_PATTERN = Pattern.compile("(-?\\d*)d(\\d+)"); public static void main(String[] args) throws Exception { String inputExpr = "((3d8)+1)×(2d4)×3"; System.out.println("原始表达式:" + inputExpr); // 第一步:统一数学符号,把×替换为JS支持的* String normalizedExpr = inputExpr.replace('×', '*'); // 第二步:替换所有骰子表达式为实际投掷总和 String mathExpr = replaceDiceWithValues(normalizedExpr); System.out.println("替换后表达式:" + mathExpr); // 第三步:求值并输出结果 int finalResult = evaluateMathExpression(mathExpr); System.out.println("最终结果:" + finalResult); } private static String replaceDiceWithValues(String expr) { Matcher matcher = DICE_PATTERN.matcher(expr); StringBuilder resultBuilder = new StringBuilder(); while (matcher.find()) { String countStr = matcher.group(1); int diceCount = countStr.isEmpty() ? 1 : Integer.parseInt(countStr); int diceSize = Math.abs(Integer.parseInt(matcher.group(2))); // 确保面数为正 int sum = 0; // 处理负数数量:比如-2d6表示投掷2个6面骰后取负数 int sign = diceCount < 0 ? -1 : 1; diceCount = Math.abs(diceCount); for (int i = 0; i < diceCount; i++) { sum += throwDice(diceSize); } sum *= sign; // 替换当前匹配的骰子表达式为计算结果 matcher.appendReplacement(resultBuilder, String.valueOf(sum)); } matcher.appendTail(resultBuilder); return resultBuilder.toString(); } private static int throwDice(int diceSize) { // 生成1到diceSize的随机整数 return random.nextInt(diceSize) + 1; } private static int evaluateMathExpression(String expr) throws Exception { ScriptEngineManager engineManager = new ScriptEngineManager(); ScriptEngine jsEngine = engineManager.getEngineByName("JavaScript"); // 因为所有运算都是整数,转成int返回 return ((Number) jsEngine.eval(expr)).intValue(); } }
关键细节说明
- 正则扩展:新增了对负数数量的支持,比如
-2d6会计算为-(2d6的总和),符合常见的骰子规则场景。 - Random复用:避免每次投掷都创建新的
Random实例,既提升性能,也保证随机性的稳定性。 - 安全提示:如果你的表达式来自不可信的用户输入,不建议直接用
ScriptEngine执行JS,因为存在脚本注入风险。这种情况下可以选择Apache Commons Math或者自己实现一个简单的整数表达式解析器,只支持加减乘除和括号运算。
内容的提问来源于stack exchange,提问作者Gino Marin
相关产品推荐
相关产品推荐

