Allow simple math input in AmountSpecifyingScreen
This commit is contained in:
@@ -3,6 +3,7 @@ package com.refinedmods.refinedstorage.screen;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.refinedmods.refinedstorage.RS;
|
||||
import com.refinedmods.refinedstorage.render.RenderSettings;
|
||||
import com.refinedmods.refinedstorage.util.EquationEvaluator;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -38,6 +39,25 @@ public abstract class AmountSpecifyingScreen<T extends AbstractContainerMenu> ex
|
||||
|
||||
protected abstract int getMaxAmount();
|
||||
|
||||
protected int getMinAmount() {
|
||||
if (canAmountGoNegative()) {
|
||||
return -getMaxAmount();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected int parseAmount() {
|
||||
return (int) Math.ceil(EquationEvaluator.evaluate(amountField.getText()));
|
||||
}
|
||||
|
||||
protected int clampAmount(int amount) {
|
||||
return Math.max(getMinAmount(), Math.min(getMaxAmount(), amount));
|
||||
}
|
||||
|
||||
protected boolean isAmountInBounds(int amount) {
|
||||
return getMinAmount() <= amount && amount <= getMaxAmount();
|
||||
}
|
||||
|
||||
protected Pair<Integer, Integer> getAmountPos() {
|
||||
return Pair.of(7 + 2, 50 + 1);
|
||||
}
|
||||
@@ -137,29 +157,32 @@ public abstract class AmountSpecifyingScreen<T extends AbstractContainerMenu> ex
|
||||
}
|
||||
|
||||
private void onIncrementButtonClicked(int increment) {
|
||||
int oldAmount = 0;
|
||||
|
||||
try {
|
||||
oldAmount = Integer.parseInt(amountField.getValue());
|
||||
} catch (NumberFormatException e) {
|
||||
int oldAmount = parseAmount();
|
||||
int newAmount = oldAmount + increment;
|
||||
if (!canAmountGoNegative() && oldAmount == 1) {
|
||||
newAmount--;
|
||||
}
|
||||
amountField.setText(String.valueOf(clampAmount(newAmount)));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// NO OP
|
||||
}
|
||||
|
||||
int newAmount = increment;
|
||||
|
||||
if (!canAmountGoNegative()) {
|
||||
newAmount = Math.max(1, ((oldAmount == 1 && newAmount != 1) ? 0 : oldAmount) + newAmount);
|
||||
} else {
|
||||
newAmount = oldAmount + newAmount;
|
||||
}
|
||||
|
||||
if (newAmount > getMaxAmount()) {
|
||||
newAmount = getMaxAmount();
|
||||
}
|
||||
|
||||
amountField.setValue(String.valueOf(newAmount));
|
||||
}
|
||||
|
||||
private void onOkButtonPressed(boolean shiftDown) {
|
||||
try {
|
||||
int amount = parseAmount();
|
||||
if (isAmountInBounds(amount)) {
|
||||
onValidAmountSave(shiftDown, amount);
|
||||
close();
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// NO OP
|
||||
}
|
||||
}
|
||||
|
||||
protected void onValidAmountSave(boolean shiftDown, int amount) {}
|
||||
|
||||
@Override
|
||||
public void tick(int x, int y) {
|
||||
// NO OP
|
||||
@@ -179,9 +202,6 @@ public abstract class AmountSpecifyingScreen<T extends AbstractContainerMenu> ex
|
||||
renderString(poseStack, 7, 7, title.getString());
|
||||
}
|
||||
|
||||
protected void onOkButtonPressed(boolean shiftDown) {
|
||||
// NO OP
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double x, double y, double delta) {
|
||||
|
@@ -87,15 +87,7 @@ public class FluidAmountScreen extends AmountSpecifyingScreen<FluidAmountContain
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOkButtonPressed(boolean shiftDown) {
|
||||
try {
|
||||
int amount = Integer.parseInt(amountField.getValue());
|
||||
|
||||
RS.NETWORK_HANDLER.sendToServer(new SetFluidFilterSlotMessage(containerSlot, StackUtils.copy(stack, amount)));
|
||||
|
||||
close();
|
||||
} catch (NumberFormatException e) {
|
||||
// NO OP
|
||||
}
|
||||
protected void onValidAmountSave(boolean shiftDown, int amount) {
|
||||
RS.NETWORK_HANDLER.sendToServer(new SetFluidFilterSlotMessage(containerSlot, StackUtils.copy(stack, amount)));
|
||||
}
|
||||
}
|
||||
|
@@ -87,15 +87,7 @@ public class ItemAmountScreen extends AmountSpecifyingScreen<AmountContainerMenu
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOkButtonPressed(boolean shiftDown) {
|
||||
try {
|
||||
int amount = Integer.parseInt(amountField.getValue());
|
||||
|
||||
RS.NETWORK_HANDLER.sendToServer(new SetFilterSlotMessage(containerSlot, ItemHandlerHelper.copyStackWithSize(stack, amount)));
|
||||
|
||||
close();
|
||||
} catch (NumberFormatException e) {
|
||||
// NO OP
|
||||
}
|
||||
protected void onValidAmountSave(boolean shiftDown, int amount) {
|
||||
RS.NETWORK_HANDLER.sendToServer(new SetFilterSlotMessage(containerSlot, ItemHandlerHelper.copyStackWithSize(stack, amount)));
|
||||
}
|
||||
}
|
||||
|
@@ -67,15 +67,7 @@ public class PriorityScreen extends AmountSpecifyingScreen<AbstractContainerMenu
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOkButtonPressed(boolean noPreview) {
|
||||
try {
|
||||
int amount = Integer.parseInt(amountField.getValue());
|
||||
|
||||
BlockEntitySynchronizationManager.setParameter(priority, amount);
|
||||
|
||||
close();
|
||||
} catch (NumberFormatException e) {
|
||||
// NO OP
|
||||
}
|
||||
protected void onValidAmountSave(boolean shiftDown, int amount) {
|
||||
BlockEntitySynchronizationManager.setParameter(priority, amount);
|
||||
}
|
||||
}
|
||||
|
@@ -62,15 +62,9 @@ public class CraftingSettingsScreen extends AmountSpecifyingScreen<CraftingSetti
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOkButtonPressed(boolean shiftDown) {
|
||||
try {
|
||||
int quantity = Integer.parseInt(amountField.getValue());
|
||||
protected void onValidAmountSave(boolean shiftDown, int amount) {
|
||||
RS.NETWORK_HANDLER.sendToServer(new GridCraftingPreviewRequestMessage(stack.getId(), amount, shiftDown, stack instanceof FluidGridStack));
|
||||
|
||||
RS.NETWORK_HANDLER.sendToServer(new GridCraftingPreviewRequestMessage(stack.getId(), quantity, shiftDown, stack instanceof FluidGridStack));
|
||||
|
||||
okButton.active = false;
|
||||
} catch (NumberFormatException e) {
|
||||
// NO OP
|
||||
}
|
||||
okButton.active = false;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,107 @@
|
||||
package com.refinedmods.refinedstorage.util;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Implements evaluation for simple math expressions using the shunting yard
|
||||
* algorithm.
|
||||
*/
|
||||
public class EquationEvaluator {
|
||||
|
||||
public static double evaluate(String input) {
|
||||
StringTokenizer tokens = new StringTokenizer(input, "+-*/()", true);
|
||||
Deque<String> operators = new ArrayDeque<>();
|
||||
Deque<Double> operands = new ArrayDeque<>();
|
||||
String lastToken = null;
|
||||
while (tokens.hasMoreTokens()) {
|
||||
String rawToken = tokens.nextToken().trim();
|
||||
if (rawToken.isEmpty())
|
||||
continue;
|
||||
|
||||
boolean isUnaryMinus = rawToken.equals("-") && (lastToken == null
|
||||
|| (!lastToken.equals("u") && isOperator(lastToken)) || lastToken.equals("("));
|
||||
final String token = isUnaryMinus ? "u" : rawToken;
|
||||
if (isOperator(token)) {
|
||||
popOperators(input, operators, operands,
|
||||
top -> (!top.equals("(") && getPrecedence(token) <= getPrecedence(top)));
|
||||
operators.push(token);
|
||||
} else if (token.equals("(")) {
|
||||
operators.push(token);
|
||||
} else if (token.equals(")")) {
|
||||
popOperators(input, operators, operands, top -> !top.equals("("));
|
||||
if (!operators.pop().equals("(")) {
|
||||
throw new IllegalArgumentException("Unbalanced right parentheses in expression: " + input);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
operands.push(Double.parseDouble(token));
|
||||
} catch (NumberFormatException exception) {
|
||||
throw new IllegalArgumentException("Could not evaluate expression: " + input);
|
||||
}
|
||||
}
|
||||
lastToken = token;
|
||||
}
|
||||
popOperators(input, operators, operands, top -> true);
|
||||
return operands.pop();
|
||||
}
|
||||
|
||||
private static void popOperators(String input, Deque<String> operators, Deque<Double> operands,
|
||||
Predicate<String> predicate) {
|
||||
try {
|
||||
while (!operators.isEmpty() && predicate.test(operators.peek())) {
|
||||
String op = operators.pop();
|
||||
if (op.equals("u")) {
|
||||
operands.push(-operands.pop());
|
||||
continue;
|
||||
}
|
||||
operands.push(evaluateExpression(op, operands.pop(), operands.pop()));
|
||||
}
|
||||
} catch (NoSuchElementException exception) {
|
||||
throw new IllegalArgumentException("Missing operand in expression: " + input);
|
||||
}
|
||||
}
|
||||
|
||||
private static double evaluateExpression(String op, double b, double a) {
|
||||
switch (op) {
|
||||
case "+":
|
||||
return a + b;
|
||||
case "-":
|
||||
return a - b;
|
||||
case "*":
|
||||
return a * b;
|
||||
case "/":
|
||||
return a / b;
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected arithmetic operation " + op);
|
||||
}
|
||||
|
||||
private static boolean isOperator(String op) {
|
||||
switch (op) {
|
||||
case "+":
|
||||
case "-":
|
||||
case "*":
|
||||
case "/":
|
||||
case "u":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int getPrecedence(String op) {
|
||||
switch (op) {
|
||||
case "+":
|
||||
case "-":
|
||||
return 0;
|
||||
case "*":
|
||||
case "/":
|
||||
return 1;
|
||||
case "u":
|
||||
return 2;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package com.refinedmods.refinedstorage.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class EquationEvaluatorTest {
|
||||
|
||||
private void assertFuzzyEquals(double expected, double actual) {
|
||||
assertEquals(expected, actual, 0.001D);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstants() {
|
||||
assertFuzzyEquals(1, EquationEvaluator.evaluate("1"));
|
||||
assertFuzzyEquals(122, EquationEvaluator.evaluate("122"));
|
||||
assertFuzzyEquals(4.2, EquationEvaluator.evaluate("4.20"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNegativeConstants() {
|
||||
assertFuzzyEquals(-1, EquationEvaluator.evaluate("-1"));
|
||||
assertFuzzyEquals(-2.5, EquationEvaluator.evaluate("-2.5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBasicOperations() {
|
||||
assertFuzzyEquals(2, EquationEvaluator.evaluate("1+1"));
|
||||
assertFuzzyEquals(1, EquationEvaluator.evaluate("3 - 2"));
|
||||
assertFuzzyEquals(6, EquationEvaluator.evaluate("2*3"));
|
||||
assertFuzzyEquals(2, EquationEvaluator.evaluate("6 / 3"));
|
||||
assertFuzzyEquals(-2, EquationEvaluator.evaluate("2-4"));
|
||||
|
||||
assertFuzzyEquals(32, EquationEvaluator.evaluate("64 + -4 * 8"));
|
||||
assertFuzzyEquals(8, EquationEvaluator.evaluate("16 + -4 + -4"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidInput() {
|
||||
assertThrows(IllegalArgumentException.class, () -> EquationEvaluator.evaluate("hello"));
|
||||
assertThrows(IllegalArgumentException.class, () -> EquationEvaluator.evaluate("1 / ("));
|
||||
assertThrows(IllegalArgumentException.class, () -> EquationEvaluator.evaluate("1+-*/"));
|
||||
assertThrows(IllegalArgumentException.class, () -> EquationEvaluator.evaluate("----1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWhitespace() {
|
||||
assertFuzzyEquals(2, EquationEvaluator.evaluate(" 1 + 1"));
|
||||
assertFuzzyEquals(1, EquationEvaluator.evaluate(" 3 - 2 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testParentheses() {
|
||||
assertFuzzyEquals(42, EquationEvaluator.evaluate("10 + 8*(2 + 2)"));
|
||||
assertFuzzyEquals(20, EquationEvaluator.evaluate("5 * ((1 + 1) * 2)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOrderOfOperations() {
|
||||
assertFuzzyEquals(1, EquationEvaluator.evaluate("1 - 2 + 3 * 4 / 6"));
|
||||
assertFuzzyEquals(4.0 / 3, EquationEvaluator.evaluate("(((1 - 2) + 3) * 4) / 6"));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user