I am trying to generate a random Equation and an answer to it. Everything works fine, except the answers are being calculated wrong ( without consideration of operator precedence ). I've been trying to fix it for some time now, and i have a general idea of how to approach it, but i have no idea how to actually do it, and there is little to no information on it on the web.
Here is my code:
public static Equation randomEquation() {
Random random = new Random();
int numNumbers = random.nextInt(3) + 2; // generate between 2 and 4 numbers
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < numNumbers; i++) {
int number = random.nextInt(101); // generate random number between 0 and 100
numbers.add(number);
}
StringBuilder equationBuilder = new StringBuilder();
int answer = numbers.get(0);
equationBuilder.append(numbers.get(0));
for (int i = 1; i < numNumbers; i++) {
int operator = random.nextInt(4); // generate random operator: 0 for addition, 1 for subtraction, 2 for multiplication, 3 for division
int number;
if (operator == 2) {
// Limit multiplication operation to never generate a result more than 10
do {
number = random.nextInt(11);
} while (number == 0);
} else {
number = random.nextInt(101); // generate random number between 0 and 100
}
switch (operator) {
case 0 -> {
equationBuilder.append(" + ");
answer += number;
}
case 1 -> {
equationBuilder.append(" - ");
answer -= number;
}
case 2 -> {
equationBuilder.append(" * ");
answer *= number;
}
case 3 -> {
equationBuilder.append(" / ");
if (number == 0 || answer % number != 0) {
// If the second number is 0 or the division results in a non-integer answer, regenerate the equation
return randomEquation();
} else {
answer /= number;
}
}
}
equationBuilder.append(number);
}
String equation = equationBuilder.toString();
return new Equation(equation, answer);
}
So there is clearly a problem with how the answer is calculated in here:
switch (operator) {
case 0 -> {
equationBuilder.append(" + ");
answer += number;
}
case 1 -> {
equationBuilder.append(" - ");
answer -= number;
}
case 2 -> {
equationBuilder.append(" * ");
answer *= number;
}
case 3 -> {
equationBuilder.append(" / ");
if (number == 0 || answer % number != 0) {
// If the second number is 0 or the division results in a non-integer answer, regenerate the equation
return randomEquation();
} else {
answer /= number;
}
}
}
I am simply appending the answer with every number, which does not make sense when multiplying and dividing.
So my question is, how do i introduce the concept of operator precedence into my code?
Since the operator is generated randomly, you'll have to evaluate the equationBuilder string, after its generation.
In this example I utilized the Pattern and Matcher classes to isolate the operation indices, using a regular expression pattern.
static int answer(String string) {
Pattern pattern = Pattern.compile("(-?\\d+) \\* (-?\\d+)");
Matcher matcher;
int operandA, operandB, result;
while ((matcher = pattern.matcher(string)).find()) {
operandA = Integer.parseInt(matcher.group(1));
operandB = Integer.parseInt(matcher.group(2));
result = operandA * operandB;
string = string.replace(matcher.group(), String.valueOf(result));
}
pattern = Pattern.compile("(-?\\d+) / (-?\\d+)");
while ((matcher = pattern.matcher(string)).find()) {
operandA = Integer.parseInt(matcher.group(1));
operandB = Integer.parseInt(matcher.group(2));
result = operandA / operandB;
string = string.replace(matcher.group(), String.valueOf(result));
}
pattern = Pattern.compile("(-?\\d+) ([-+]) (-?\\d+)");
while ((matcher = pattern.matcher(string)).find()) {
operandA = Integer.parseInt(matcher.group(1));
operandB = Integer.parseInt(matcher.group(3));
result = switch (matcher.group(2)) {
case "+" -> operandA + operandB;
default -> operandA - operandB;
};
string = string.replace(matcher.group(), String.valueOf(result));
}
return Integer.parseInt(string);
}
And, I modified your operator assignment code-block, with the following.
switch (operator) {
case 0 -> equationBuilder.append(" + ");
case 1 -> equationBuilder.append(" - ");
case 2 -> equationBuilder.append(" * ");
case 3 -> {
equationBuilder.append(" / ");
if (number == 0 || answer % number != 0) {
// If the second number is 0 or the division results in a non-integer answer, regenerate the equation
return randomEquation();
}
}
}
And, subsequently, returned this as the value.
return new Equation(equation, answer(equation));
Here are a few examples.
6 * 5 - 34 - 59 = -63
8 * 1 = 8
5 * 7 = 35
71 - 22 = 49
43 - 52 = -9
Additionally, I added some println calls to the while-loops, to display the procedures.
15 * 5
15 * 5 = 75
100 + 47 - 40 + 67
100 + 47 = 147
147 - 40 = 107
107 + 67 = 174
87 / 1 * 4
1 * 4 = 4
87 / 4 = 21
22 * 6 - 37
22 * 6 = 132
132 - 37 = 95
17 - 44 + 45 * 6
45 * 6 = 270
17 - 44 = -27
-27 + 270 = 243
Furthermore, you could improve this code by utilizing a double, and adjusting the regular expression to match real numbers.
Or even, utilize the BigDecimal class to evaluate numbers outside of the 64-bit range.