package model; import java.util.Stack; /** * 小学题目生成器,确保题目结果非负 */ public class PrimaryMaker extends Student { private static final char[] OPERATORS = {'+', '-', '*', '/'}; private static final int MIN_OPERANDS = 2; private static final int MAX_OPERANDS = 5; private static final int MAX_OPERAND_VALUE = 100; private int operandCount; private int[] operands; private int parenStart; private int parenEnd; public PrimaryMaker(String name, String password, String path) { super(name, password, path); } @Override protected String makeOneQuestion() { String question; while (true) { question = generateSingleQuestion(); if (isQuestionValid(question)) { break; } } return question; } private String generateSingleQuestion() { generateRandomParameters(); StringBuilder question = new StringBuilder(); for (int j = 0; j < operandCount; j++) { appendOperandWithParentheses(question, j); if (j < operandCount - 1) { appendOperator(question, j); } } return question.toString(); } private void generateRandomParameters() { operandCount = random.nextInt(MAX_OPERANDS - MIN_OPERANDS + 1) + MIN_OPERANDS; operands = new int[operandCount]; for (int j = 0; j < operandCount; j++) { operands[j] = random.nextInt(MAX_OPERAND_VALUE) + 1; } int[] parenPos = bracketMaker(operandCount); parenStart = parenPos[0]; parenEnd = parenPos[1]; } private void appendOperandWithParentheses(StringBuilder question, int index) { if (index == parenStart) { question.append("( "); } question.append(operands[index]); if (index == parenEnd) { question.append(" )"); } } private void appendOperator(StringBuilder question, int currentIndex) { char op = OPERATORS[random.nextInt(OPERATORS.length)]; if (op == '-') { ensureSubtractionIsValid(currentIndex); } if (op == '/') { ensureDivisionIsValid(currentIndex); } question.append(" ").append(op).append(" "); } private void ensureSubtractionIsValid(int currentIndex) { if (operands[currentIndex] < operands[currentIndex + 1]) { int temp = operands[currentIndex]; operands[currentIndex] = operands[currentIndex + 1]; operands[currentIndex + 1] = temp; } } private void ensureDivisionIsValid(int currentIndex) { if (operands[currentIndex + 1] == 0) { operands[currentIndex + 1] = 1; } } private boolean isQuestionValid(String expression) { try { evaluateExpression(expression, true); return true; } catch (ArithmeticException e) { return false; } } private double evaluateExpression(String expression, boolean checkNegative) throws ArithmeticException { Stack numbers = new Stack<>(); Stack ops = new Stack<>(); for (int i = 0; i < expression.length(); i++) { char c = expression.charAt(i); if (c == ' ') { continue; } if (Character.isDigit(c)) { i = parseAndPushNumber(expression, i, numbers); } else if (c == '(') { ops.push(c); } else if (c == ')') { processClosingParenthesis(numbers, ops, checkNegative); } else if (isOperator(c)) { processOperator(c, numbers, ops, checkNegative); } } processRemainingOperators(numbers, ops, checkNegative); double finalResult = numbers.pop(); validateResult(finalResult, checkNegative); return finalResult; } private int parseAndPushNumber(String expression, int startIndex, Stack numbers) { StringBuilder sb = new StringBuilder(); int i = startIndex; while (i < expression.length() && (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.')) { sb.append(expression.charAt(i++)); } numbers.push(Double.parseDouble(sb.toString())); return i - 1; } private void processClosingParenthesis(Stack numbers, Stack ops, boolean checkNegative) { while (ops.peek() != '(') { double result = applyOp(ops.pop(), numbers.pop(), numbers.pop()); validateResult(result, checkNegative); numbers.push(result); } ops.pop(); } private void processOperator(char currentOp, Stack numbers, Stack ops, boolean checkNegative) { while (!ops.isEmpty() && hasPrecedence(currentOp, ops.peek())) { double result = applyOp(ops.pop(), numbers.pop(), numbers.pop()); validateResult(result, checkNegative); numbers.push(result); } ops.push(currentOp); } private void processRemainingOperators(Stack numbers, Stack ops, boolean checkNegative) { while (!ops.isEmpty()) { double result = applyOp(ops.pop(), numbers.pop(), numbers.pop()); validateResult(result, checkNegative); numbers.push(result); } } private void validateResult(double result, boolean checkNegative) { if (checkNegative && result < 0) { throw new ArithmeticException("Negative result"); } } private boolean isOperator(char c) { return c == '+' || c == '-' || c == '*' || c == '/'; } private boolean hasPrecedence(char op1, char op2) { if (op2 == '(' || op2 == ')') { return false; } return (op1 != '*' && op1 != '/') || (op2 != '+' && op2 != '-'); } private double applyOp(char op, double b, double a) { switch (op) { case '+': return a + b; case '-': if (a < b) { throw new ArithmeticException("Negative result in subtraction"); } return a - b; case '*': return a * b; case '/': if (b == 0) { throw new ArithmeticException("Cannot divide by zero"); } return a / b; default: return 0; } } }