1

I wrote a couple of functions that take a formatted string, defines the number of dice and the size of the dice, throw them, and adds their values. Eg: 3d8 would mean to throw 3 dices of 8 sides each and add their values. The values are always positive, so every time I run this function for 3d8 I could get values between 3 and 24.

    public int calcDice(String diceFormula){
        String[] divided = diceFormula.split("d");
        int cant = Integer.parseInt(divided[0]);
        int dice = Integer.parseInt(divided[1]);
        int result = 0;
        for (int i = 0; i < cant; i++) {
            result += throwDice(dice);
        }
        return result;
    }

    private int throwDice(int diceSize) {
        diceSize = diceSize < 0 ? dice * -1 : diceSize;
        Random r = new Random();
        return r.nextInt((diceSize - 1) + 1) + 1;
    }

What I require now, is to be able to make mathematical functions using these values, so I could input a mathematical function that will be calculated. I would need to respect the resolution order Eg. ((3d8)+1) x (2d4) x 3

One of the ideas was to take the string and process first the values, then replace the javascript evaluator to figure out the result, but I'm not sure how can I "pick" the values. (A regex maybe?)

Gino Marin
  • 41
  • 7
  • A possible solution is to substute in the string the `throwDice` parts, and then use the answer here: https://stackoverflow.com/questions/3422673/how-to-evaluate-a-math-expression-given-in-string-form – Eduardo Pascual Aseff Feb 09 '20 at 05:46
  • 1
    Thank you for your comment. I thought of this as well, but the problem is that inner operations had to be calculated taking away the decimals. So in this mathematical world, 10/3 would be a total of 3, not 3.333 – Gino Marin Feb 09 '20 at 23:33

1 Answers1

0

What I did to solve this was to implement a ShuntingYard function that was able to parse mathematical expressions

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class ShuttingYard {

    private final Map<String, Integer> operators = new HashMap<>();

    public ShuttingYard(){
        operators.put("-", 0);
        operators.put("+", 0);
        operators.put("/", 1);
        operators.put("*", 1);
        operators.put("^", 2);
    }
    public double doTheShuntingYard(String expression)throws IllegalArgumentException, NumberFormatException, ArithmeticException{
        if(expression == null || expression.trim().length() == 0)
            throw new IllegalArgumentException("Empty expression or null");
        expression = expression.replaceAll("\\s+","");
        expression = expression.replace("(-", "(0-");
        if (expression.startsWith("-")){
            expression = "0" + expression;
        }
        Pattern pattern = Pattern.compile("((([0-9]*[.])?[0-9]+)|([\\+\\-\\*\\/\\(\\)\\^]))");
        Matcher matcher = pattern.matcher(expression);

        int counter = 0;
        List<String> tokens = new ArrayList<>();
        while(matcher.find()){
            if(matcher.start() != counter){
                throw new IllegalArgumentException("Invalid Expression:" + expression + ". Error between " + counter+ " end " + matcher.start());
            }
            tokens.add(matcher.group().trim());
            counter += tokens.get(tokens.size() - 1 ).length();
        }
        if(counter != expression.length()){
            throw new IllegalArgumentException("Invalid end of expression");
        }

        Stack<String> stack = new Stack<>();
        List<String> output = new ArrayList<>();

        for(String token : tokens){
            if(operators.containsKey(token)){
                while(!stack.empty() &&
                        operators.containsKey(stack.peek())&&
                        ((operators.get(token) <= operators.get(stack.peek()) && !token.equals("^"))||
                                (operators.get(token) < operators.get(stack.peek()) && token.equals("^")))){
                    output.add(stack.pop());
                }
                stack.push(token);

            }
            else if(token.equals("(")){
                stack.push(token);
            }
            else if(token.equals(")")){
                while(!stack.empty()){
                    if(!stack.peek().equals("(")){
                        output.add(stack.pop());
                    }
                    else{
                        break;
                    }
                }
                if(!stack.empty()){
                    stack.pop();
                }
            }
            else{
                output.add(token);
            }
        }

        while(!stack.empty()){
            output.add(stack.pop());
        }

        Stack<Double> doubles = new Stack<>();
        for(String token : output){
            if(!operators.containsKey(token) && token.matches("([0-9]*[.])?[0-9]+")){
                try{
                    doubles.push(Double.parseDouble(token));
                }
                catch(NumberFormatException n){
                    throw n;
                }
            }
            else{
                if(doubles.size() > 1){
                    double op1 = doubles.pop();
                    double op2 = doubles.pop();
                    switch (token) {
                        case "+":
                            doubles.push(op2 + op1);
                            break;
                        case "-":
                            doubles.push(op2 - op1);
                            break;
                        case "*":
                            doubles.push(op2 * op1);
                            break;
                        case "/":
                            if(op1 == 0){
                                throw new ArithmeticException("Division by 0");
                            }
                            doubles.push(Math.floor(op2 / op1));
                            break;
                        case "^":
                            doubles.push(Math.pow(op2, op1));
                            break;
                        default:
                            throw new IllegalArgumentException(token + " is not an operator or is not handled");
                    }
                }
            }
        }
        if(doubles.empty() || doubles.size() > 1){
            throw new IllegalArgumentException("Invalid expression, could not find a result. An operator seems to be absent");
        }
        return doubles.peek();
    }
}

Then, I would call this function after resolving the throwDice operations

public class DiceThrower{

    private ShuttingYard shuttingYard;

    public DiceThrower(){
        this.shuttingYard = new ShuttingYard();
    }

    public void throwDiceAction(View view){

        TextView result = findViewById(R.id.diceResult);
        try{
            String original = ((EditText)findViewById(R.id.formula)).getText().toString();
            Pattern pattern = Pattern.compile("([0-9]{1,999})d([0-9]{1,999})");
            Matcher matcher = pattern.matcher(original);
            while(matcher.find()){
                original = matcher.replaceFirst(Integer.toString(calcDice(matcher.group(0))));
                matcher = pattern.matcher(original);
            }
            result.setText(evaluateExpression(original).split("\\.")[0]);
        }catch(ArithmeticException e){
            result.setText("This doesn't seem to be a valid mathematical expression");
        }


    }


    public String evaluateExpression(String expression){
        expression = expression.replaceAll("\\)\\(", ")*(");
        expression = expression.replaceAll("x", "*");
        return Double.toString(this.shuttingYard.doTheShuntingYard(expression));
    }

    public int calcDice(String formula){
        String[] divided = formula.split("d");
        int cant = Integer.parseInt(divided[0]);
        int dice = Integer.parseInt(divided[1]);
        int result = 0;
        for (int i = 0; i < cant; i++) {
            result += throwDice(dice);
        }
        return result;
    }

    private int throwDice(int dice) {
        dice = dice < 0 ? dice * -1 : dice;
        Random r = new Random();
        return r.nextInt((dice - 1) + 1) + 1;
    }

}
Gino Marin
  • 41
  • 7