From 6dd2bf888bd53bd990c7d55c09e2531fdb512e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=84=8F?= <1449892469@qq.com> Date: Sat, 11 Oct 2025 11:21:37 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=AD=94=E6=A1=88?= =?UTF-8?q?=E7=94=9F=E6=88=90=E4=BC=98=E5=85=88=E7=BA=A7=E7=AE=97=E6=B3=95?= =?UTF-8?q?=E5=92=8C=E5=88=9D=E4=B8=AD=E5=BC=80=E6=A0=B9=E5=8F=B7=E8=A6=81?= =?UTF-8?q?=E6=B1=82=EF=BC=88=E4=BB=BB=E6=84=8F=E6=93=8D=E4=BD=9C=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E8=80=8C=E9=9D=9E=E5=AE=8C=E5=85=A8=E5=B9=B3=E6=96=B9?= =?UTF-8?q?=E6=95=B0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/vcs.xml | 6 + .../mathlearning/model/QuestionGenerator.java | 692 +++++++++--------- 2 files changed, 367 insertions(+), 331 deletions(-) create mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..c8397c9 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/com/mathlearning/model/QuestionGenerator.java b/src/com/mathlearning/model/QuestionGenerator.java index 871427b..d45a941 100644 --- a/src/com/mathlearning/model/QuestionGenerator.java +++ b/src/com/mathlearning/model/QuestionGenerator.java @@ -11,7 +11,7 @@ public class QuestionGenerator { public List generateQuestions(String level, int count) { List questions = new ArrayList<>(); - Set questionTexts = new HashSet<>(); + Set questionTexts = new HashSet<>(); // 防止重复题目 for (int i = 0; i < count; i++) { Question question; @@ -44,443 +44,473 @@ public class QuestionGenerator { } private Question generatePrimaryQuestion(int index) { - int operandCount = random.nextInt(4) + 2; - List operands = new ArrayList<>(); - List operators = new ArrayList<>(); - - // 生成操作数和运算符,确保结果非负 - generateSafePrimaryExpression(operands, operators, operandCount); + int operandCount = random.nextInt(4) + 2; // 2-5个操作数 + int[] operands = new int[operandCount]; + int[] operations = new int[operandCount - 1]; // 操作符数组 String questionText; - int result; - int attempts = 0; + int answer; + // 生成合适的题目,确保计算过程中不出现负数 do { - boolean useParentheses = random.nextBoolean() && operandCount >= 3; - if (useParentheses) { - int parenPos = random.nextInt(operandCount - 1); - questionText = buildParenthesesExpression(operands, operators, parenPos); - result = evaluateWithParentheses(operands, operators, parenPos); - } else { - questionText = buildSimpleExpression(operands, operators); - result = evaluateSequential(operands, operators); + // 生成操作数 + for (int i = 0; i < operandCount; i++) { + operands[i] = random.nextInt(100) + 1; // 1-100 } - attempts++; - if (result < 0) { - // 重新生成安全的表达式 - generateSafePrimaryExpression(operands, operators, operandCount); + // 生成操作符 (0: +, 1: -, 2: *, 3: /) + for (int i = 0; i < operations.length; i++) { + operations[i] = random.nextInt(4); } - } while (result < 0 && attempts < 10); - // 如果还是负数,强制使用加法 - if (result < 0) { - for (int i = 0; i < operators.size(); i++) { - operators.set(i, '+'); - } - questionText = buildSimpleExpression(operands, operators); - result = evaluateSequential(operands, operators); - } + // 构建题目并计算答案 + questionText = buildPrimaryQuestionText(operands, operations); + answer = calculatePrimaryAnswer(operands, operations); + + } while (answer < 0 || !isValidPrimaryCalculation(operands, operations)); - return generateIntOptions(questionText + " = ?", result, "小学"); + return generateOptions(questionText, answer, "小学"); } - private void generateSafePrimaryExpression(List operands, List operators, int operandCount) { - operands.clear(); - operators.clear(); + private Question generateMiddleSchoolQuestion(int index) { + int operandCount = random.nextInt(5) + 1; // 1-5个操作数 + int[] operands = new int[operandCount]; + int[] operations = new int[Math.max(0, operandCount - 1)]; // 操作符数组 + boolean[] hasSpecialOp = new boolean[operandCount]; // 标记哪些操作数有平方或开根号 + int[] specialOpTypes = new int[operandCount]; // 0:平方, 1:开根号 - 新增:记录具体操作类型 - // 首先生成所有操作数 - for (int i = 0; i < operandCount; i++) { - operands.add(random.nextInt(100) + 1); - } + String questionText; + double answer; - // 然后生成安全的运算符序列 - int currentValue = operands.get(0); - for (int i = 1; i < operandCount; i++) { - char op; - if (currentValue < operands.get(i)) { - // 如果当前值小于下一个操作数,避免减法导致负数 - if (random.nextBoolean()) { - op = '+'; - currentValue += operands.get(i); - } else { - op = (random.nextBoolean() && currentValue > 0) ? '*' : '+'; - if (op == '*') currentValue *= operands.get(i); - else currentValue += operands.get(i); - } - } else { - // 可以安全使用减法 - op = getRandomOperation("+-*/"); - switch (op) { - case '+': currentValue += operands.get(i); break; - case '-': currentValue -= operands.get(i); break; - case '*': currentValue *= operands.get(i); break; - case '/': - if (operands.get(i) != 0) currentValue /= operands.get(i); - else currentValue += operands.get(i); - break; - } + // 确保至少有一个平方或开根号运算符 + boolean hasSpecialOperation = false; + for (int i = 0; i < operandCount; i++) { + hasSpecialOp[i] = random.nextDouble() < 0.6; // 60%的概率有特殊运算 + if (hasSpecialOp[i]) { + hasSpecialOperation = true; + specialOpTypes[i] = random.nextInt(2); // 随机选择平方或开根号,并记录下来 } - operators.add(op); } - } - private Question generateMiddleSchoolQuestion(int index) { - int type = random.nextInt(4); - MathExpression expression = new MathExpression(); - - switch (type) { - case 0: - expression = createSquareExpression(); - break; - case 1: - expression = createSqrtExpression(); - break; - case 2: - expression = createSquareProductExpression(); - break; - case 3: - expression = createSquareDivisionExpression(); - break; + // 如果没有特殊运算,强制至少一个 + if (!hasSpecialOperation && operandCount > 0) { + int idx = random.nextInt(operandCount); + hasSpecialOp[idx] = true; + specialOpTypes[idx] = random.nextInt(2); } - // 确保初中题目结果非负 - if (expression.result < 0) { - expression = createSquareExpression(); // 重新生成一个肯定为正的题目 - } + // 生成合适的题目 + do { + // 生成操作数 + for (int i = 0; i < operandCount; i++) { + operands[i] = random.nextInt(100) + 1; // 1-100 + } - return generateIntOptions(expression.questionText + " = ?", expression.result, "初中"); - } + // 生成操作符 (0: +, 1: -, 2: *, 3: /) + for (int i = 0; i < operations.length; i++) { + operations[i] = random.nextInt(4); + } - private MathExpression createSquareExpression() { - int a = random.nextInt(10) + 1; - int b = random.nextInt(100) + 1; - int c = random.nextInt(100) + 1; - // 只使用加法确保结果为正 - char op = '+'; + // 构建题目并计算答案 + questionText = buildMiddleSchoolQuestionText(operands, operations, hasSpecialOp, specialOpTypes); + answer = calculateMiddleSchoolAnswer(operands, operations, hasSpecialOp, specialOpTypes); - String questionText = a + "² " + op + " " + b + " × " + c; - int result = a * a + b * c; + } while (Double.isNaN(answer) || Double.isInfinite(answer) || Math.abs(answer) > 10000); - return new MathExpression(questionText, result); + return generateOptions(questionText, answer, "初中"); } - private MathExpression createSqrtExpression() { - int sqrtBase = findPerfectSquare(100); - int sqrtVal = (int) Math.sqrt(sqrtBase); - int d = random.nextInt(100) + 1; - // 只使用加法确保结果为正 - char op = '+'; + private Question generateHighSchoolQuestion(int index) { + int operandCount = random.nextInt(5) + 1; // 1-5个操作数 + int[] operands = new int[operandCount]; + int[] operations = new int[Math.max(0, operandCount - 1)]; // 操作符数组 + boolean[] hasTrigOp = new boolean[operandCount]; // 标记哪些操作数有三角函数 + int[] trigFunctions = new int[operandCount]; // 0:sin, 1:cos, 2:tan - String questionText = "√" + sqrtBase + " " + op + " " + d; - int result = sqrtVal + d; + String questionText; + double answer; - return new MathExpression(questionText, result); - } + // 确保至少有一个三角函数运算符 + boolean hasTrigOperation = false; + for (int i = 0; i < operandCount; i++) { + hasTrigOp[i] = random.nextDouble() < 0.6; // 60%的概率有三角函数 + if (hasTrigOp[i]) { + hasTrigOperation = true; + trigFunctions[i] = random.nextInt(3); // 随机选择三角函数 + } + } - private MathExpression createSquareProductExpression() { - int e = random.nextInt(100) + 1; - int f = random.nextInt(10) + 1; - int g = random.nextInt(100) + 1; - // 只使用加法确保结果为正 - char op = '+'; + // 如果没有三角函数,强制至少一个 + if (!hasTrigOperation && operandCount > 0) { + int idx = random.nextInt(operandCount); + hasTrigOp[idx] = true; + trigFunctions[idx] = random.nextInt(3); + } - String questionText = e + " × " + f + "² " + op + " " + g; - int result = e * f * f + g; + // 生成合适的题目 + do { + // 生成操作数 (角度值,通常用特殊角度) + for (int i = 0; i < operandCount; i++) { + // 使用常见角度:0, 30, 45, 60, 90等 + int[] commonAngles = {0, 30, 45, 60, 90, 120, 135, 150, 180}; + operands[i] = commonAngles[random.nextInt(commonAngles.length)]; + } - return new MathExpression(questionText, result); - } + // 生成操作符 (0: +, 1: -, 2: *, 3: /) + for (int i = 0; i < operations.length; i++) { + operations[i] = random.nextInt(4); + } - private MathExpression createSquareDivisionExpression() { - int h = random.nextInt(50) + 1; - int i = random.nextInt(50) + 1; - int sum = h + i; - int square = sum * sum; - int j = findDivisor(square); + // 构建题目并计算答案 + questionText = buildHighSchoolQuestionText(operands, operations, hasTrigOp, trigFunctions); + answer = calculateHighSchoolAnswer(operands, operations, hasTrigOp, trigFunctions); - String questionText = "(" + h + " + " + i + ")² ÷ " + j; - int result = square / j; + } while (Double.isNaN(answer) || Double.isInfinite(answer) || Math.abs(answer) > 10000); - return new MathExpression(questionText, result); + return generateOptions(questionText, (int)Math.round(answer), "高中"); } - private Question generateHighSchoolQuestion(int index) { - int type = random.nextInt(4); - TrigExpression expression = new TrigExpression(); + // 小学题目相关方法 + private String buildPrimaryQuestionText(int[] operands, int[] operations) { + StringBuilder sb = new StringBuilder(); - switch (type) { - case 0: - expression = createSinExpression(); - break; - case 1: - expression = createCosExpression(); - break; - case 2: - expression = createTanExpression(); - break; - case 3: - expression = createSinCosExpression(); - break; + for (int i = 0; i < operands.length; i++) { + if (i > 0) { + switch (operations[i - 1]) { + case 0: sb.append(" + "); break; + case 1: sb.append(" - "); break; + case 2: sb.append(" × "); break; + case 3: sb.append(" ÷ "); break; + } + } + sb.append(operands[i]); } + sb.append(" = ?"); - return generateDoubleOptions(expression.questionText + " = ?", expression.result, "高中"); + return sb.toString(); } - private TrigExpression createSinExpression() { - String[] angles = {"0°", "30°", "45°", "60°", "90°"}; - double[] sinValues = {0, 0.5, 0.71, 0.87, 1.0}; + private int calculatePrimaryAnswer(int[] operands, int[] operations) { + // 先处理乘除法,再处理加减法 + List numbers = new ArrayList<>(); + List ops = new ArrayList<>(); - int idx = random.nextInt(angles.length); - int b = random.nextInt(100) + 1; - // 只使用加法确保结果为正 - char op = '+'; + for (int operand : operands) { + numbers.add(operand); + } + for (int operation : operations) { + ops.add(operation); + } - String questionText = "sin(" + angles[idx] + ") " + op + " " + b; - double result = sinValues[idx] + b; + // 处理乘除法 + for (int i = 0; i < ops.size(); i++) { + int op = ops.get(i); + if (op == 2 || op == 3) { + int left = numbers.get(i); + int right = numbers.get(i + 1); + int result; - return new TrigExpression(questionText, result); - } + if (op == 2) { + result = left * right; + } else { + if (right == 0 || left % right != 0) return -1; + result = left / right; + } - private TrigExpression createCosExpression() { - String[] angles = {"0°", "30°", "45°", "60°"}; - double[] cosValues = {1.0, 0.87, 0.71, 0.5}; + numbers.set(i, result); + numbers.remove(i + 1); + ops.remove(i); + i--; + } + } - int idx = random.nextInt(angles.length); - int a = random.nextInt(100) + 1; + // 处理加减法 + int result = numbers.get(0); + for (int i = 0; i < ops.size(); i++) { + int op = ops.get(i); + int nextNum = numbers.get(i + 1); - String questionText = a + " × cos(" + angles[idx] + ")"; - double result = a * cosValues[idx]; + if (op == 0) { + result += nextNum; + } else { + if (result < nextNum) return -1; + result -= nextNum; + } + } - return new TrigExpression(questionText, result); + return result; } - private TrigExpression createTanExpression() { - String[] angles = {"0°", "30°", "45°", "60°"}; - double[] tanValues = {0, 0.58, 1.0, 1.73}; + private boolean isValidPrimaryCalculation(int[] operands, int[] operations) { + try { + int current = operands[0]; + + for (int i = 0; i < operations.length; i++) { + int next = operands[i + 1]; + int op = operations[i]; + + if (op == 1) { + if (current < next) return false; + current = current - next; + } else if (op == 0) { + current = current + next; + } else if (op == 2) { + current = current * next; + } else if (op == 3) { + if (next == 0 || current % next != 0) return false; + current = current / next; + } + } + return current >= 0; + } catch (Exception e) { + return false; + } + } - int idx = random.nextInt(angles.length); - int c = random.nextInt(20) + 1; + // 初中题目相关方法 + private String buildMiddleSchoolQuestionText(int[] operands, int[] operations, boolean[] hasSpecialOp, int[] specialOpTypes) { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < operands.length; i++) { + if (i > 0) { + switch (operations[i - 1]) { + case 0: sb.append(" + "); break; + case 1: sb.append(" - "); break; + case 2: sb.append(" × "); break; + case 3: sb.append(" ÷ "); break; + } + } - String questionText = "tan(" + angles[idx] + ") ÷ " + c; - double result = tanValues[idx] / c; + if (hasSpecialOp[i]) { + if (specialOpTypes[i] == 0) { + // 平方 + sb.append(operands[i]).append("²"); + } else { + // 开根号 + sb.append("√").append(operands[i]); + } + } else { + sb.append(operands[i]); + } + } + sb.append(" = ?"); - return new TrigExpression(questionText, result); + return sb.toString(); } - private TrigExpression createSinCosExpression() { - String[] angles = {"0°", "30°", "45°", "60°"}; - double[] sinValues = {0, 0.5, 0.71, 0.87}; - double[] cosValues = {1.0, 0.87, 0.71, 0.5}; + private double calculateMiddleSchoolAnswer(int[] operands, int[] operations, boolean[] hasSpecialOp, int[] specialOpTypes) { + // 先处理特殊运算(平方和开根号) + double[] processedValues = new double[operands.length]; + for (int i = 0; i < operands.length; i++) { + if (hasSpecialOp[i]) { + if (specialOpTypes[i] == 0) { + // 平方 + processedValues[i] = operands[i] * operands[i]; + } else { + // 开根号 + processedValues[i] = Math.sqrt(operands[i]); + } + } else { + processedValues[i] = operands[i]; + } + } - int idx1 = random.nextInt(angles.length); - int idx2 = random.nextInt(angles.length); - int d = random.nextInt(100) + 1; - // 只使用加法确保结果为正 - char op = '+'; + // 然后按照小学的计算逻辑处理 + List numbers = new ArrayList<>(); + List ops = new ArrayList<>(); - String questionText = "sin(" + angles[idx1] + ") × cos(" + angles[idx2] + ") " + op + " " + d; - double product = sinValues[idx1] * cosValues[idx2]; - double result = product + d; + for (double value : processedValues) { + numbers.add(value); + } + for (int operation : operations) { + ops.add(operation); + } - return new TrigExpression(questionText, result); - } + // 处理乘除法 + for (int i = 0; i < ops.size(); i++) { + int op = ops.get(i); + if (op == 2 || op == 3) { + double left = numbers.get(i); + double right = numbers.get(i + 1); + double result; - private char getRandomOperation(String operations) { - return operations.charAt(random.nextInt(operations.length())); - } + if (op == 2) { + result = left * right; + } else { + if (right == 0) return Double.NaN; + result = left / right; + } - private String buildSimpleExpression(List operands, List operators) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < operands.size(); i++) { - sb.append(operands.get(i)); - if (i < operands.size() - 1) { - sb.append(" ").append(operators.get(i)).append(" "); + numbers.set(i, result); + numbers.remove(i + 1); + ops.remove(i); + i--; } } - return sb.toString(); - } - private String buildParenthesesExpression(List operands, List operators, int parenPos) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < operands.size(); i++) { - if (i == parenPos) sb.append("("); - sb.append(operands.get(i)); - if (i == parenPos + 1) sb.append(")"); - if (i < operands.size() - 1) { - sb.append(" ").append(operators.get(i)).append(" "); + // 处理加减法 + double result = numbers.get(0); + for (int i = 0; i < ops.size(); i++) { + int op = ops.get(i); + double nextNum = numbers.get(i + 1); + + if (op == 0) { + result += nextNum; + } else { + result -= nextNum; } } - return sb.toString(); - } - private int evaluateSequential(List operands, List operators) { - int result = operands.get(0); - for (int i = 0; i < operators.size(); i++) { - result = calculate(result, operands.get(i + 1), operators.get(i)); - } return result; } - private int evaluateWithParentheses(List operands, List operators, int parenPos) { - int parenResult = calculate(operands.get(parenPos), operands.get(parenPos + 1), operators.get(parenPos)); - List newOperands = new ArrayList<>(); - List newOperators = new ArrayList<>(); + // 高中题目相关方法 + private String buildHighSchoolQuestionText(int[] operands, int[] operations, boolean[] hasTrigOp, int[] trigFunctions) { + StringBuilder sb = new StringBuilder(); - for (int i = 0; i < operands.size(); i++) { - if (i == parenPos) { - newOperands.add(parenResult); - } else if (i != parenPos + 1) { - newOperands.add(operands.get(i)); + for (int i = 0; i < operands.length; i++) { + if (i > 0) { + switch (operations[i - 1]) { + case 0: sb.append(" + "); break; + case 1: sb.append(" - "); break; + case 2: sb.append(" × "); break; + case 3: sb.append(" ÷ "); break; + } } - } - for (int i = 0; i < operators.size(); i++) { - if (i != parenPos) { - newOperators.add(operators.get(i)); + if (hasTrigOp[i]) { + switch (trigFunctions[i]) { + case 0: sb.append("sin(").append(operands[i]).append("°)"); break; + case 1: sb.append("cos(").append(operands[i]).append("°)"); break; + case 2: sb.append("tan(").append(operands[i]).append("°)"); break; + } + } else { + sb.append(operands[i]); } } + sb.append(" = ?"); - return evaluateSequential(newOperands, newOperators); + return sb.toString(); } - private int calculate(int a, int b, char op) { - switch (op) { - case '+': return a + b; - case '-': return Math.max(a - b, 0); - case '*': return a * b; - case '/': return (b == 0) ? a : a / b; - default: return a + b; + private double calculateHighSchoolAnswer(int[] operands, int[] operations, boolean[] hasTrigOp, int[] trigFunctions) { + // 先处理三角函数运算 + double[] processedValues = new double[operands.length]; + for (int i = 0; i < operands.length; i++) { + if (hasTrigOp[i]) { + double radians = Math.toRadians(operands[i]); + switch (trigFunctions[i]) { + case 0: processedValues[i] = Math.sin(radians); break; + case 1: processedValues[i] = Math.cos(radians); break; + case 2: processedValues[i] = Math.tan(radians); break; + } + } else { + processedValues[i] = operands[i]; + } } - } - private int findPerfectSquare(int max) { - List squares = new ArrayList<>(); - for (int i = 1; i * i <= max; i++) { - squares.add(i * i); - } - return squares.get(random.nextInt(squares.size())); - } + // 然后按照常规计算逻辑处理 + List numbers = new ArrayList<>(); + List ops = new ArrayList<>(); - private int findDivisor(int number) { - List divisors = new ArrayList<>(); - for (int i = 2; i <= Math.min(20, number); i++) { - if (number % i == 0) { - divisors.add(i); - } + for (double value : processedValues) { + numbers.add(value); + } + for (int operation : operations) { + ops.add(operation); } - return divisors.isEmpty() ? 1 : divisors.get(random.nextInt(divisors.size())); - } - - private Question generateIntOptions(String questionText, int answer, String level) { - String[] options = new String[4]; - Set usedValues = new HashSet<>(); - String correctAnswer = String.valueOf(answer); - options[0] = correctAnswer; - usedValues.add(answer); + // 处理乘除法 + for (int i = 0; i < ops.size(); i++) { + int op = ops.get(i); + if (op == 2 || op == 3) { + double left = numbers.get(i); + double right = numbers.get(i + 1); + double result; - for (int i = 1; i < 4; i++) { - int wrongAnswer; - int attempts = 0; - do { - int range = Math.max(3, Math.abs(answer) / 5 + 1); - int offset = random.nextInt(range * 2 + 1) - range; - wrongAnswer = answer + offset; - wrongAnswer = Math.max(wrongAnswer, 0); // 确保错误答案也不为负 - attempts++; - if (attempts > 20) { - wrongAnswer = answer + (i + 1) * 10 + 5; - break; + if (op == 2) { + result = left * right; + } else { + if (right == 0) return Double.NaN; + result = left / right; } - } while (wrongAnswer == answer || usedValues.contains(wrongAnswer)); - options[i] = String.valueOf(wrongAnswer); - usedValues.add(wrongAnswer); + numbers.set(i, result); + numbers.remove(i + 1); + ops.remove(i); + i--; + } } - shuffleArray(options); - int correctIndex = findCorrectIndex(options, correctAnswer); + // 处理加减法 + double result = numbers.get(0); + for (int i = 0; i < ops.size(); i++) { + int op = ops.get(i); + double nextNum = numbers.get(i + 1); - return new Question(questionText, options, correctIndex, level); + if (op == 0) { + result += nextNum; + } else { + result -= nextNum; + } + } + + return result; } - private Question generateDoubleOptions(String questionText, double answer, String level) { + // 生成选项的通用方法 + private Question generateOptions(String questionText, double answer, String level) { String[] options = new String[4]; - Set usedValues = new HashSet<>(); + Set usedValues = new HashSet<>(); - String correctAnswer = formatDouble(answer); - options[0] = correctAnswer; - usedValues.add(correctAnswer); + // 添加正确答案,保留1位小数 + options[0] = String.format("%.1f", answer); + usedValues.add(Math.round(answer * 10) / 10.0); // 保留1位小数进行比较 + // 生成错误答案 for (int i = 1; i < 4; i++) { - String wrongAnswer; + double wrongAnswer; int attempts = 0; do { - double offset = (random.nextDouble() - 0.5) * Math.max(1, Math.abs(answer) * 0.3); - double wrongValue = answer + offset; - wrongAnswer = formatDouble(Math.max(wrongValue, 0)); // 确保错误答案不为负 + // 根据答案大小生成合适的错误答案 + double range = Math.max(2, Math.abs(answer) / 4 + 1); + double offset = (random.nextDouble() * range * 2) - range; + wrongAnswer = answer + offset; + wrongAnswer = Math.round(wrongAnswer * 10) / 10.0; // 保留1位小数 attempts++; - if (attempts > 20) { - wrongAnswer = formatDouble(answer + (i + 1) * 2.5); - break; - } - } while (usedValues.contains(wrongAnswer) || wrongAnswer.equals(correctAnswer)); + } while (usedValues.contains(wrongAnswer) && attempts < 20); - options[i] = wrongAnswer; + if (usedValues.contains(wrongAnswer)) { + wrongAnswer = answer + (i * 0.5) + 0.3; + wrongAnswer = Math.round(wrongAnswer * 10) / 10.0; + } + + options[i] = String.format("%.1f", wrongAnswer); usedValues.add(wrongAnswer); } + // 打乱选项顺序 shuffleArray(options); - int correctIndex = findCorrectIndex(options, correctAnswer); - - return new Question(questionText, options, correctIndex, level); - } - private String formatDouble(double value) { - if (Double.isInfinite(value)) return "∞"; - if (Double.isNaN(value)) return "无解"; - if (Math.abs(value) < 0.001) return "0"; - return String.format("%.2f", value).replaceAll("0*$", "").replaceAll("\\.$", ""); - } - - private int findCorrectIndex(String[] options, String correctAnswer) { + // 找到正确答案的新位置 + int correctIndex = 0; + String correctAnswerStr = String.format("%.1f", answer); for (int i = 0; i < options.length; i++) { - if (options[i].equals(correctAnswer)) return i; + if (options[i].equals(correctAnswerStr)) { + correctIndex = i; + break; + } } - return 0; + + return new Question(questionText, options, correctIndex, level); } private void shuffleArray(String[] array) { for (int i = array.length - 1; i > 0; i--) { - int index = random.nextInt(i + 1); - String temp = array[index]; - array[index] = array[i]; - array[i] = temp; - } - } - - // 辅助类来存储题目和答案 - private static class MathExpression { - String questionText; - int result; - - MathExpression() {} - - MathExpression(String questionText, int result) { - this.questionText = questionText; - this.result = result; - } - } - - private static class TrigExpression { - String questionText; - double result; - - TrigExpression() {} - - TrigExpression(String questionText, double result) { - this.questionText = questionText; - this.result = result; + int j = random.nextInt(i + 1); + String temp = array[i]; + array[i] = array[j]; + array[j] = temp; } } } \ No newline at end of file -- 2.34.1 From 0a08b966c4f62cae549cb221547ab4ce69c9ed3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=84=8F?= <1449892469@qq.com> Date: Sat, 11 Oct 2025 13:24:45 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E5=B0=8F=E5=AD=A6=E9=A2=98=E7=9B=AE?= =?UTF-8?q?=E5=8A=A0=E4=B8=8A=E6=8B=AC=E5=8F=B7=EF=BC=8C=E9=AB=98=E4=B8=AD?= =?UTF-8?q?=E4=B8=89=E8=A7=92=E5=87=BD=E6=95=B0=E5=85=88=E7=B2=BE=E7=A1=AE?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E5=86=8D=E4=BF=9D=E7=95=992=E4=BD=8D?= =?UTF-8?q?=E5=B0=8F=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mathlearning/model/QuestionGenerator.java | 349 ++++++++++++++---- 1 file changed, 267 insertions(+), 82 deletions(-) diff --git a/src/com/mathlearning/model/QuestionGenerator.java b/src/com/mathlearning/model/QuestionGenerator.java index d45a941..2f8dda4 100644 --- a/src/com/mathlearning/model/QuestionGenerator.java +++ b/src/com/mathlearning/model/QuestionGenerator.java @@ -46,12 +46,11 @@ public class QuestionGenerator { private Question generatePrimaryQuestion(int index) { int operandCount = random.nextInt(4) + 2; // 2-5个操作数 int[] operands = new int[operandCount]; - int[] operations = new int[operandCount - 1]; // 操作符数组 + int[] operations = new int[operandCount - 1]; String questionText; - int answer; + double answer; - // 生成合适的题目,确保计算过程中不出现负数 do { // 生成操作数 for (int i = 0; i < operandCount; i++) { @@ -63,11 +62,27 @@ public class QuestionGenerator { operations[i] = random.nextInt(4); } - // 构建题目并计算答案 - questionText = buildPrimaryQuestionText(operands, operations); - answer = calculatePrimaryAnswer(operands, operations); + // 改进的括号逻辑:在可能改变计算顺序的地方加括号 + boolean useParentheses = random.nextDouble() < 0.3 && operandCount >= 3; + int parenStart = -1; + int parenEnd = -1; + + if (useParentheses) { + // 寻找适合加括号的位置(加减法周围) + parenStart = findSuitableParenthesesPosition(operations); + if (parenStart != -1) { + // 括号包含2-3个操作数 + parenEnd = parenStart + 1; + if (parenEnd < operands.length - 1 && random.nextBoolean()) { + parenEnd++; + } + } + } - } while (answer < 0 || !isValidPrimaryCalculation(operands, operations)); + questionText = buildPrimaryQuestionText(operands, operations, parenStart, parenEnd); + answer = calculatePrimaryAnswerWithParentheses(operands, operations, parenStart, parenEnd); + + } while (answer < 0 || Double.isNaN(answer) || Double.isInfinite(answer)); return generateOptions(questionText, answer, "小学"); } @@ -77,7 +92,7 @@ public class QuestionGenerator { int[] operands = new int[operandCount]; int[] operations = new int[Math.max(0, operandCount - 1)]; // 操作符数组 boolean[] hasSpecialOp = new boolean[operandCount]; // 标记哪些操作数有平方或开根号 - int[] specialOpTypes = new int[operandCount]; // 0:平方, 1:开根号 - 新增:记录具体操作类型 + int[] specialOpTypes = new int[operandCount]; // 0:平方, 1:开根号 String questionText; double answer; @@ -88,7 +103,7 @@ public class QuestionGenerator { hasSpecialOp[i] = random.nextDouble() < 0.6; // 60%的概率有特殊运算 if (hasSpecialOp[i]) { hasSpecialOperation = true; - specialOpTypes[i] = random.nextInt(2); // 随机选择平方或开根号,并记录下来 + specialOpTypes[i] = random.nextInt(2); // 随机选择平方或开根号 } } @@ -149,11 +164,16 @@ public class QuestionGenerator { // 生成合适的题目 do { - // 生成操作数 (角度值,通常用特殊角度) + // 生成操作数 for (int i = 0; i < operandCount; i++) { - // 使用常见角度:0, 30, 45, 60, 90等 - int[] commonAngles = {0, 30, 45, 60, 90, 120, 135, 150, 180}; - operands[i] = commonAngles[random.nextInt(commonAngles.length)]; + if (hasTrigOp[i]) { + // 三角函数使用特殊角度值 + int[] specialAngles = {0, 15, 30, 45, 60, 75, 90}; + operands[i] = specialAngles[random.nextInt(specialAngles.length)]; + } else { + // 普通操作数使用1-100的随机数 + operands[i] = random.nextInt(100) + 1; + } } // 生成操作符 (0: +, 1: -, 2: *, 3: /) @@ -167,31 +187,69 @@ public class QuestionGenerator { } while (Double.isNaN(answer) || Double.isInfinite(answer) || Math.abs(answer) > 10000); - return generateOptions(questionText, (int)Math.round(answer), "高中"); + return generateOptions(questionText, answer, "高中"); } // 小学题目相关方法 - private String buildPrimaryQuestionText(int[] operands, int[] operations) { + private int findSuitableParenthesesPosition(int[] operations) { + // 寻找加减法操作符的位置,在这些地方加括号才有意义 + List suitablePositions = new ArrayList<>(); + + for (int i = 0; i < operations.length; i++) { + // 如果是加减法,且不是第一个操作符 + if ((operations[i] == 0 || operations[i] == 1) && i > 0) { + suitablePositions.add(i); + } + } + + if (suitablePositions.isEmpty()) { + // 如果没有找到合适的加减法位置,随机选择一个位置 + return operations.length > 1 ? random.nextInt(operations.length - 1) : -1; + } + + // 从合适的位置中随机选择一个 + return suitablePositions.get(random.nextInt(suitablePositions.size())); + } + + private String buildPrimaryQuestionText(int[] operands, int[] operations, int parenStart, int parenEnd) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < operands.length; i++) { - if (i > 0) { - switch (operations[i - 1]) { + // 在操作数前添加左括号 + if (i == parenStart) { + sb.append("("); + } + + // 添加操作数 + sb.append(operands[i]); + + // 在操作数后添加右括号(但要在操作符之前) + if (i == parenEnd) { + sb.append(")"); + } + + // 添加操作符(如果不是最后一个操作数) + if (i < operations.length) { + switch (operations[i]) { case 0: sb.append(" + "); break; case 1: sb.append(" - "); break; case 2: sb.append(" × "); break; case 3: sb.append(" ÷ "); break; } } - sb.append(operands[i]); } sb.append(" = ?"); return sb.toString(); } - private int calculatePrimaryAnswer(int[] operands, int[] operations) { - // 先处理乘除法,再处理加减法 + private double calculatePrimaryAnswerWithParentheses(int[] operands, int[] operations, int parenStart, int parenEnd) { + // 如果没有括号,使用原来的计算方法 + if (parenStart == -1 || parenEnd == -1) { + return calculatePrimaryAnswer(operands, operations); + } + + // 复制操作数和操作符 List numbers = new ArrayList<>(); List ops = new ArrayList<>(); @@ -202,33 +260,91 @@ public class QuestionGenerator { ops.add(operation); } - // 处理乘除法 + // 计算括号内的表达式 + double parenthesesResult = calculateParenthesesContent(numbers, ops, parenStart, parenEnd); + + // 验证括号计算结果是否有效 + if (parenthesesResult < 0 || Double.isNaN(parenthesesResult) || Double.isInfinite(parenthesesResult)) { + return -1; + } + + // 用括号计算结果替换括号内的所有元素 + replaceWithParenthesesResult(numbers, ops, parenStart, parenEnd, parenthesesResult); + + // 计算剩余表达式 + return calculateWithoutParentheses(numbers, ops); + } + + private double calculateParenthesesContent(List numbers, List ops, int start, int end) { + // 提取括号内的数字和操作符 + List parenNumbers = new ArrayList<>(); + List parenOps = new ArrayList<>(); + + for (int i = start; i <= end; i++) { + parenNumbers.add(numbers.get(i)); + } + for (int i = start; i < end; i++) { + parenOps.add(ops.get(i)); + } + + // 计算括号内的表达式(精确计算) + return calculateWithoutParentheses(parenNumbers, parenOps); + } + + private void replaceWithParenthesesResult(List numbers, List ops, + int start, int end, double result) { + // 计算需要移除的元素数量 + int numCountToRemove = end - start + 1; + int opCountToRemove = end - start; + + // 移除括号范围内的数字 + for (int i = 0; i < numCountToRemove; i++) { + numbers.remove(start); + } + + // 移除括号范围内的操作符 + for (int i = 0; i < opCountToRemove; i++) { + ops.remove(start); + } + + // 插入括号计算结果 + numbers.add(start, (int) Math.round(result)); + } + + private double calculateWithoutParentheses(List numbers, List ops) { + // 转换为double列表进行计算(精确计算) + List doubleNumbers = new ArrayList<>(); + for (Integer num : numbers) { + doubleNumbers.add(num.doubleValue()); + } + + // 先处理乘除法(精确计算) for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); if (op == 2 || op == 3) { - int left = numbers.get(i); - int right = numbers.get(i + 1); - int result; + double left = doubleNumbers.get(i); + double right = doubleNumbers.get(i + 1); + double result; if (op == 2) { result = left * right; } else { - if (right == 0 || left % right != 0) return -1; + if (right == 0) return Double.NaN; result = left / right; } - numbers.set(i, result); - numbers.remove(i + 1); + doubleNumbers.set(i, result); + doubleNumbers.remove(i + 1); ops.remove(i); i--; } } - // 处理加减法 - int result = numbers.get(0); + // 处理加减法(精确计算) + double result = doubleNumbers.get(0); for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); - int nextNum = numbers.get(i + 1); + double nextNum = doubleNumbers.get(i + 1); if (op == 0) { result += nextNum; @@ -238,33 +354,60 @@ public class QuestionGenerator { } } - return result; + // 只在最后结果保留2位小数 + return Math.round(result * 100) / 100.0; } - private boolean isValidPrimaryCalculation(int[] operands, int[] operations) { - try { - int current = operands[0]; + private double calculatePrimaryAnswer(int[] operands, int[] operations) { + // 转换为double列表(精确计算) + List numbers = new ArrayList<>(); + List ops = new ArrayList<>(); - for (int i = 0; i < operations.length; i++) { - int next = operands[i + 1]; - int op = operations[i]; - - if (op == 1) { - if (current < next) return false; - current = current - next; - } else if (op == 0) { - current = current + next; - } else if (op == 2) { - current = current * next; - } else if (op == 3) { - if (next == 0 || current % next != 0) return false; - current = current / next; + for (int operand : operands) { + numbers.add((double) operand); + } + for (int operation : operations) { + ops.add(operation); + } + + // 处理乘除法(精确计算) + for (int i = 0; i < ops.size(); i++) { + int op = ops.get(i); + if (op == 2 || op == 3) { + double left = numbers.get(i); + double right = numbers.get(i + 1); + double result; + + if (op == 2) { + result = left * right; + } else { + if (right == 0) return Double.NaN; + result = left / right; } + + numbers.set(i, result); + numbers.remove(i + 1); + ops.remove(i); + i--; } - return current >= 0; - } catch (Exception e) { - return false; } + + // 处理加减法(精确计算) + double result = numbers.get(0); + for (int i = 0; i < ops.size(); i++) { + int op = ops.get(i); + double nextNum = numbers.get(i + 1); + + if (op == 0) { + result += nextNum; + } else { + if (result < nextNum) return -1; + result -= nextNum; + } + } + + // 只在最后结果保留2位小数 + return Math.round(result * 100) / 100.0; } // 初中题目相关方法 @@ -299,7 +442,7 @@ public class QuestionGenerator { } private double calculateMiddleSchoolAnswer(int[] operands, int[] operations, boolean[] hasSpecialOp, int[] specialOpTypes) { - // 先处理特殊运算(平方和开根号) + // 先处理特殊运算(平方和开根号)- 精确计算 double[] processedValues = new double[operands.length]; for (int i = 0; i < operands.length; i++) { if (hasSpecialOp[i]) { @@ -315,7 +458,7 @@ public class QuestionGenerator { } } - // 然后按照小学的计算逻辑处理 + // 然后按照小学的计算逻辑处理(精确计算) List numbers = new ArrayList<>(); List ops = new ArrayList<>(); @@ -326,7 +469,7 @@ public class QuestionGenerator { ops.add(operation); } - // 处理乘除法 + // 处理乘除法(精确计算) for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); if (op == 2 || op == 3) { @@ -348,7 +491,7 @@ public class QuestionGenerator { } } - // 处理加减法 + // 处理加减法(精确计算) double result = numbers.get(0); for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); @@ -361,7 +504,8 @@ public class QuestionGenerator { } } - return result; + // 只在最后结果保留2位小数 + return Math.round(result * 100) / 100.0; } // 高中题目相关方法 @@ -394,22 +538,34 @@ public class QuestionGenerator { } private double calculateHighSchoolAnswer(int[] operands, int[] operations, boolean[] hasTrigOp, int[] trigFunctions) { - // 先处理三角函数运算 + // 先处理三角函数运算(精确计算) double[] processedValues = new double[operands.length]; for (int i = 0; i < operands.length; i++) { if (hasTrigOp[i]) { double radians = Math.toRadians(operands[i]); switch (trigFunctions[i]) { - case 0: processedValues[i] = Math.sin(radians); break; - case 1: processedValues[i] = Math.cos(radians); break; - case 2: processedValues[i] = Math.tan(radians); break; + case 0: + processedValues[i] = Math.sin(radians); + break; + case 1: + processedValues[i] = Math.cos(radians); + break; + case 2: + // 避免tan(90°)等无效值 + if (operands[i] % 180 == 90 && operands[i] % 360 != 270) { + return Double.NaN; + } + processedValues[i] = Math.tan(radians); + break; + default: + processedValues[i] = 0; } } else { processedValues[i] = operands[i]; } } - // 然后按照常规计算逻辑处理 + // 然后按照常规计算逻辑处理(精确计算) List numbers = new ArrayList<>(); List ops = new ArrayList<>(); @@ -420,7 +576,7 @@ public class QuestionGenerator { ops.add(operation); } - // 处理乘除法 + // 处理乘除法(精确计算) for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); if (op == 2 || op == 3) { @@ -442,7 +598,7 @@ public class QuestionGenerator { } } - // 处理加减法 + // 处理加减法(精确计算) double result = numbers.get(0); for (int i = 0; i < ops.size(); i++) { int op = ops.get(i); @@ -455,37 +611,59 @@ public class QuestionGenerator { } } - return result; + // 只在最后结果保留2位小数 + return Math.round(result * 100) / 100.0; } // 生成选项的通用方法 private Question generateOptions(String questionText, double answer, String level) { String[] options = new String[4]; - Set usedValues = new HashSet<>(); + Set usedValues = new HashSet<>(); - // 添加正确答案,保留1位小数 - options[0] = String.format("%.1f", answer); - usedValues.add(Math.round(answer * 10) / 10.0); // 保留1位小数进行比较 + // 检查答案是否为整数 + boolean isIntegerAnswer = (answer == (int) answer); + + // 格式化正确答案 + String correctAnswer = formatAnswer(answer); + options[0] = correctAnswer; + usedValues.add(correctAnswer); // 生成错误答案 for (int i = 1; i < 4; i++) { - double wrongAnswer; + String wrongAnswer; int attempts = 0; do { - // 根据答案大小生成合适的错误答案 - double range = Math.max(2, Math.abs(answer) / 4 + 1); - double offset = (random.nextDouble() * range * 2) - range; - wrongAnswer = answer + offset; - wrongAnswer = Math.round(wrongAnswer * 10) / 10.0; // 保留1位小数 + // 基于正确答案生成错误答案 + double wrongValue; + double offset = (random.nextDouble() * 5) + 1; // 1-6的偏移量 + + if (isIntegerAnswer) { + // 如果答案是整数,生成整数错误答案 + int intAnswer = (int) answer; + int intOffset = random.nextInt(10) + 1; // 1-10的整数偏移 + if (random.nextBoolean()) { + wrongValue = intAnswer + intOffset; + } else { + wrongValue = Math.max(1, intAnswer - intOffset); + } + // 确保是整数 + wrongValue = (int) wrongValue; + } else { + // 如果答案是小数,生成小数错误答案 + if (random.nextBoolean()) { + wrongValue = answer + offset; + } else { + wrongValue = answer - offset; + } + // 保留2位小数 + wrongValue = Math.round(wrongValue * 100) / 100.0; + } + + wrongAnswer = formatAnswer(wrongValue); attempts++; } while (usedValues.contains(wrongAnswer) && attempts < 20); - if (usedValues.contains(wrongAnswer)) { - wrongAnswer = answer + (i * 0.5) + 0.3; - wrongAnswer = Math.round(wrongAnswer * 10) / 10.0; - } - - options[i] = String.format("%.1f", wrongAnswer); + options[i] = wrongAnswer; usedValues.add(wrongAnswer); } @@ -494,9 +672,8 @@ public class QuestionGenerator { // 找到正确答案的新位置 int correctIndex = 0; - String correctAnswerStr = String.format("%.1f", answer); for (int i = 0; i < options.length; i++) { - if (options[i].equals(correctAnswerStr)) { + if (options[i].equals(correctAnswer)) { correctIndex = i; break; } @@ -505,6 +682,14 @@ public class QuestionGenerator { return new Question(questionText, options, correctIndex, level); } + private String formatAnswer(double answer) { + if (answer == (int) answer) { + return String.valueOf((int) answer); // 整数不显示小数 + } else { + return String.format("%.2f", answer); // 小数保留2位 + } + } + private void shuffleArray(String[] array) { for (int i = array.length - 1; i > 0; i--) { int j = random.nextInt(i + 1); -- 2.34.1 From 763eaa0e23b184e56bf624b5235d05b5db3e4d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=84=8F?= <1449892469@qq.com> Date: Sat, 11 Oct 2025 22:05:21 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=92=8C=E6=8A=BD=E8=B1=A1=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/misc.xml | 2 +- .../controller/QuestionController.java | 121 ++- .../model/AbstractQuestionGenerator.java | 41 + .../model/BaseQuestionGenerator.java | 181 +++++ .../model/ConcreteQuestionGenerator.java | 21 + .../model/HighSchoolQuestionGenerator.java | 112 +++ .../model/MiddleSchoolQuestionGenerator.java | 91 +++ .../model/PrimaryQuestionGenerator.java | 124 ++++ src/com/mathlearning/model/Question.java | 29 +- .../mathlearning/model/QuestionGenerator.java | 698 +----------------- src/com/mathlearning/view/QuizFrame.java | 126 ++-- src/com/mathlearning/view/ScoreFrame.java | 39 +- 12 files changed, 752 insertions(+), 833 deletions(-) create mode 100644 src/com/mathlearning/model/AbstractQuestionGenerator.java create mode 100644 src/com/mathlearning/model/BaseQuestionGenerator.java create mode 100644 src/com/mathlearning/model/ConcreteQuestionGenerator.java create mode 100644 src/com/mathlearning/model/HighSchoolQuestionGenerator.java create mode 100644 src/com/mathlearning/model/MiddleSchoolQuestionGenerator.java create mode 100644 src/com/mathlearning/model/PrimaryQuestionGenerator.java diff --git a/.idea/misc.xml b/.idea/misc.xml index 12ee456..ee1d9a3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/src/com/mathlearning/controller/QuestionController.java b/src/com/mathlearning/controller/QuestionController.java index e793a9f..228fdad 100644 --- a/src/com/mathlearning/controller/QuestionController.java +++ b/src/com/mathlearning/controller/QuestionController.java @@ -1,8 +1,8 @@ package com.mathlearning.controller; import com.mathlearning.model.Question; +import com.mathlearning.model.ConcreteQuestionGenerator; import com.mathlearning.model.QuestionGenerator; - import java.util.List; public class QuestionController { @@ -10,68 +10,131 @@ public class QuestionController { private List currentQuestions; private int currentQuestionIndex; private int score; - private int[] userAnswers; public QuestionController() { - this.questionGenerator = new QuestionGenerator(); + this.questionGenerator = new ConcreteQuestionGenerator(); + this.currentQuestionIndex = 0; + this.score = 0; + } + + // 原有的生成问题方法 + public List generateQuestions(String level, int count) { + return questionGenerator.generateQuestions(level, count); } + // 原有的显示问题方法 + public void displayQuestions(String level, int count) { + List questions = generateQuestions(level, count); + System.out.println(level + "题目:"); + for (int i = 0; i < questions.size(); i++) { + Question q = questions.get(i); + System.out.println((i + 1) + ". " + q.getQuestionText()); + String[] options = q.getOptions(); + for (int j = 0; j < options.length; j++) { + System.out.println(" " + (char)('A' + j) + ". " + options[j]); + } + System.out.println(" 正确答案: " + (char)('A' + q.getCorrectIndex())); + System.out.println(); + } + } + + // 新增加的测验管理方法 + + // 开始新测验 public void startNewQuiz(String level, int questionCount) { this.currentQuestions = questionGenerator.generateQuestions(level, questionCount); this.currentQuestionIndex = 0; this.score = 0; - this.userAnswers = new int[questionCount]; - // Initialize with -1 (no answer) - for (int i = 0; i < userAnswers.length; i++) { - userAnswers[i] = -1; - } } - /* + // 获取当前问题列表 - 新增的方法 + public List getCurrentQuestions() { + return currentQuestions; + } + + // 获取当前问题 public Question getCurrentQuestion() { - if (currentQuestions == null || currentQuestionIndex >= currentQuestions.size()) { + if (currentQuestions == null || currentQuestions.isEmpty()) { return null; } return currentQuestions.get(currentQuestionIndex); } - public void submitAnswer(int answerIndex) { - if (currentQuestionIndex < userAnswers.length) { - userAnswers[currentQuestionIndex] = answerIndex; + // 获取当前问题索引 + public int getCurrentQuestionIndex() { + return currentQuestionIndex; + } + + // 获取问题总数 + public int getTotalQuestions() { + return currentQuestions != null ? currentQuestions.size() : 0; + } + + // 提交答案并检查是否正确 + public boolean submitAnswer(int selectedOption) { + if (currentQuestions == null || currentQuestionIndex >= currentQuestions.size()) { + return false; } - if (answerIndex != -1 && currentQuestions.get(currentQuestionIndex).isCorrect(answerIndex)) { + Question currentQuestion = currentQuestions.get(currentQuestionIndex); + boolean isCorrect = (selectedOption == currentQuestion.getCorrectIndex()); + + if (isCorrect) { score++; } - currentQuestionIndex++; + return isCorrect; } - public boolean hasNextQuestion() { - return currentQuestions != null && currentQuestionIndex < currentQuestions.size(); + // 移动到下一个问题 + public boolean nextQuestion() { + if (currentQuestions == null || currentQuestionIndex >= currentQuestions.size() - 1) { + return false; + } + currentQuestionIndex++; + return true; } - public int getCurrentQuestionNumber() { - return currentQuestionIndex + 1; - } - */ - public int getTotalQuestions() { - return currentQuestions != null ? currentQuestions.size() : 0; + // 移动到上一个问题 + public boolean previousQuestion() { + if (currentQuestions == null || currentQuestionIndex <= 0) { + return false; + } + currentQuestionIndex--; + return true; } + // 获取当前分数 public int getScore() { return score; } - public double getPercentage() { - return getTotalQuestions() > 0 ? (double) score / getTotalQuestions() * 100 : 0; + // 检查是否还有更多问题 + public boolean hasMoreQuestions() { + return currentQuestions != null && currentQuestionIndex < currentQuestions.size() - 1; } - public int[] getUserAnswers() { - return userAnswers; + // 检查测验是否完成 + public boolean isQuizCompleted() { + return currentQuestions != null && currentQuestionIndex >= currentQuestions.size() - 1; } - public List getCurrentQuestions() { + // 获取所有问题(用于显示结果等) + public List getAllQuestions() { return currentQuestions; } -} + + // 重置测验状态 + public void resetQuiz() { + this.currentQuestions = null; + this.currentQuestionIndex = 0; + this.score = 0; + } + + // 直接设置当前问题索引(如果需要) + public void setCurrentQuestionIndex(int index) { + if (currentQuestions != null && index >= 0 && index < currentQuestions.size()) { + this.currentQuestionIndex = index; + } + } +} \ No newline at end of file diff --git a/src/com/mathlearning/model/AbstractQuestionGenerator.java b/src/com/mathlearning/model/AbstractQuestionGenerator.java new file mode 100644 index 0000000..1b9513f --- /dev/null +++ b/src/com/mathlearning/model/AbstractQuestionGenerator.java @@ -0,0 +1,41 @@ +package com.mathlearning.model; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public abstract class AbstractQuestionGenerator implements QuestionGenerator { + + @Override + public List generateQuestions(String level, int count) { + List questions = new ArrayList<>(); + Set questionTexts = new HashSet<>(); + + for (int i = 0; i < count; i++) { + Question question = generateUniqueQuestion(level, i, questionTexts); + if (question != null) { + questions.add(question); + } + } + + return questions; + } + + private Question generateUniqueQuestion(String level, int index, Set questionTexts) { + Question question; + int attempt = 0; + do { + question = generateQuestion(level, index); + attempt++; + } while (question != null && questionTexts.contains(question.getQuestionText()) && attempt < 10); + + if (question != null) { + questionTexts.add(question.getQuestionText()); + } + + return question; + } + + protected abstract Question generateQuestion(String level, int index); +} \ No newline at end of file diff --git a/src/com/mathlearning/model/BaseQuestionGenerator.java b/src/com/mathlearning/model/BaseQuestionGenerator.java new file mode 100644 index 0000000..88cec22 --- /dev/null +++ b/src/com/mathlearning/model/BaseQuestionGenerator.java @@ -0,0 +1,181 @@ +package com.mathlearning.model; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +public abstract class BaseQuestionGenerator { + protected Random random = new Random(); + + public abstract Question generateQuestion(int index); + + protected void generateOperands(int[] operands, int min, int max) { + for (int i = 0; i < operands.length; i++) { + operands[i] = random.nextInt(max - min + 1) + min; + } + } + + protected void generateOperations(int[] operations, int operationTypes) { + for (int i = 0; i < operations.length; i++) { + operations[i] = random.nextInt(operationTypes); + } + } + + protected void appendOperator(StringBuilder sb, int operation) { + switch (operation) { + case 0: sb.append(" + "); break; + case 1: sb.append(" - "); break; + case 2: sb.append(" × "); break; + case 3: sb.append(" ÷ "); break; + } + } + + protected boolean isValidAnswer(double answer) { + return !(answer < 0 || Double.isNaN(answer) || Double.isInfinite(answer)); + } + + protected double calculateExpression(double[] values, int[] operations) { + List numbers = new ArrayList<>(); + List ops = new ArrayList<>(); + + for (double value : values) numbers.add(value); + for (int operation : operations) ops.add(operation); + + processMultiplicationAndDivision(numbers, ops); + return processAdditionAndSubtraction(numbers, ops); + } + + protected double calculateWithoutParentheses(int[] operands, int[] operations) { + double[] doubleOperands = new double[operands.length]; + for (int i = 0; i < operands.length; i++) { + doubleOperands[i] = operands[i]; + } + return calculateExpression(doubleOperands, operations); + } + + protected double calculateWithoutParentheses(List numbers, List ops) { + double[] doubleNumbers = new double[numbers.size()]; + for (int i = 0; i < numbers.size(); i++) { + doubleNumbers[i] = numbers.get(i); + } + + int[] intOps = new int[ops.size()]; + for (int i = 0; i < ops.size(); i++) { + intOps[i] = ops.get(i); + } + + return calculateExpression(doubleNumbers, intOps); + } + + private void processMultiplicationAndDivision(List numbers, List ops) { + for (int i = 0; i < ops.size(); i++) { + int op = ops.get(i); + if (op == 2 || op == 3) { + double left = numbers.get(i); + double right = numbers.get(i + 1); + double result = performOperation(left, right, op); + + numbers.set(i, result); + numbers.remove(i + 1); + ops.remove(i); + i--; + } + } + } + + private double processAdditionAndSubtraction(List numbers, List ops) { + double result = numbers.get(0); + + for (int i = 0; i < ops.size(); i++) { + int op = ops.get(i); + double nextNum = numbers.get(i + 1); + result = performOperation(result, nextNum, op); + } + + return Math.round(result * 100) / 100.0; + } + + private double performOperation(double left, double right, int operation) { + switch (operation) { + case 0: return left + right; + case 1: return left - right; + case 2: return left * right; + case 3: + if (right == 0) return Double.NaN; + return left / right; + default: return left; + } + } + + protected Question generateOptions(String questionText, double answer, String level) { + String[] options = new String[4]; + Set usedValues = new HashSet<>(); + + boolean isIntegerAnswer = (answer == (int) answer); + String correctAnswer = formatAnswer(answer); + + options[0] = correctAnswer; + usedValues.add(correctAnswer); + + generateWrongOptions(options, usedValues, answer, isIntegerAnswer); + shuffleArray(options); + + int correctIndex = findCorrectIndex(options, correctAnswer); + return new Question(questionText, options, correctIndex, level); + } + + private void generateWrongOptions(String[] options, Set usedValues, + double answer, boolean isIntegerAnswer) { + for (int i = 1; i < 4; i++) { + String wrongAnswer; + int attempts = 0; + + do { + double wrongValue = generateWrongValue(answer, isIntegerAnswer); + wrongAnswer = formatAnswer(wrongValue); + attempts++; + } while (usedValues.contains(wrongAnswer) && attempts < 20); + + options[i] = wrongAnswer; + usedValues.add(wrongAnswer); + } + } + + private double generateWrongValue(double answer, boolean isIntegerAnswer) { + double offset = (random.nextDouble() * 5) + 1; + + if (isIntegerAnswer) { + int intAnswer = (int) answer; + int intOffset = random.nextInt(10) + 1; + double wrongValue = random.nextBoolean() ? + intAnswer + intOffset : Math.max(1, intAnswer - intOffset); + return (int) wrongValue; + } else { + double wrongValue = random.nextBoolean() ? answer + offset : answer - offset; + return Math.round(wrongValue * 100) / 100.0; + } + } + + private String formatAnswer(double answer) { + return (answer == (int) answer) ? + String.valueOf((int) answer) : String.format("%.2f", answer); + } + + private void shuffleArray(String[] array) { + for (int i = array.length - 1; i > 0; i--) { + int j = random.nextInt(i + 1); + String temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + } + + private int findCorrectIndex(String[] options, String correctAnswer) { + for (int i = 0; i < options.length; i++) { + if (options[i].equals(correctAnswer)) return i; + } + return 0; + } +} \ No newline at end of file diff --git a/src/com/mathlearning/model/ConcreteQuestionGenerator.java b/src/com/mathlearning/model/ConcreteQuestionGenerator.java new file mode 100644 index 0000000..446d689 --- /dev/null +++ b/src/com/mathlearning/model/ConcreteQuestionGenerator.java @@ -0,0 +1,21 @@ +package com.mathlearning.model; + +public class ConcreteQuestionGenerator extends AbstractQuestionGenerator { + private PrimaryQuestionGenerator primaryGenerator = new PrimaryQuestionGenerator(); + private MiddleSchoolQuestionGenerator middleSchoolGenerator = new MiddleSchoolQuestionGenerator(); + private HighSchoolQuestionGenerator highSchoolGenerator = new HighSchoolQuestionGenerator(); + + @Override + protected Question generateQuestion(String level, int index) { + switch (level) { + case "小学": + return primaryGenerator.generateQuestion(index); + case "初中": + return middleSchoolGenerator.generateQuestion(index); + case "高中": + return highSchoolGenerator.generateQuestion(index); + default: + return primaryGenerator.generateQuestion(index); + } + } +} \ No newline at end of file diff --git a/src/com/mathlearning/model/HighSchoolQuestionGenerator.java b/src/com/mathlearning/model/HighSchoolQuestionGenerator.java new file mode 100644 index 0000000..bde9d3c --- /dev/null +++ b/src/com/mathlearning/model/HighSchoolQuestionGenerator.java @@ -0,0 +1,112 @@ +package com.mathlearning.model; + +public class HighSchoolQuestionGenerator extends BaseQuestionGenerator { + private static final int[] SPECIAL_ANGLES = {0, 15, 30, 45, 60, 75, 90}; + + @Override + public Question generateQuestion(int index) { + int operandCount = random.nextInt(5) + 1; + int[] operands = new int[operandCount]; + int[] operations = new int[Math.max(0, operandCount - 1)]; + boolean[] hasTrigOp = new boolean[operandCount]; + int[] trigFunctions = new int[operandCount]; + + ensureTrigOperation(hasTrigOp, trigFunctions, operandCount); + + String questionText; + double answer; + + do { + generateOperandsWithTrig(operands, hasTrigOp); + generateOperations(operations, 4); + + questionText = buildQuestionText(operands, operations, hasTrigOp, trigFunctions); + answer = calculateAnswer(operands, operations, hasTrigOp, trigFunctions); + + } while (!isValidAnswer(answer) || Math.abs(answer) > 10000); + + return generateOptions(questionText, answer, "高中"); + } + + private void ensureTrigOperation(boolean[] hasTrigOp, int[] trigFunctions, int operandCount) { + boolean hasTrigOperation = false; + + for (int i = 0; i < operandCount; i++) { + hasTrigOp[i] = random.nextDouble() < 0.6; + if (hasTrigOp[i]) hasTrigOperation = true; + } + + if (!hasTrigOperation && operandCount > 0) { + int idx = random.nextInt(operandCount); + hasTrigOp[idx] = true; + trigFunctions[idx] = random.nextInt(3); + } + } + + private void generateOperandsWithTrig(int[] operands, boolean[] hasTrigOp) { + for (int i = 0; i < operands.length; i++) { + operands[i] = hasTrigOp[i] ? + SPECIAL_ANGLES[random.nextInt(SPECIAL_ANGLES.length)] : + random.nextInt(100) + 1; + } + } + + private String buildQuestionText(int[] operands, int[] operations, + boolean[] hasTrigOp, int[] trigFunctions) { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < operands.length; i++) { + if (i > 0) appendOperator(sb, operations[i - 1]); + + if (hasTrigOp[i]) { + appendTrigFunction(sb, operands[i], trigFunctions[i]); + } else { + sb.append(operands[i]); + } + } + + sb.append(" = ?"); + return sb.toString(); + } + + private void appendTrigFunction(StringBuilder sb, int operand, int trigFunction) { + switch (trigFunction) { + case 0: sb.append("sin(").append(operand).append("°)"); break; + case 1: sb.append("cos(").append(operand).append("°)"); break; + case 2: sb.append("tan(").append(operand).append("°)"); break; + } + } + + private double calculateAnswer(int[] operands, int[] operations, + boolean[] hasTrigOp, int[] trigFunctions) { + double[] processedValues = processTrigOperations(operands, hasTrigOp, trigFunctions); + return calculateExpression(processedValues, operations); + } + + private double[] processTrigOperations(int[] operands, boolean[] hasTrigOp, int[] trigFunctions) { + double[] processedValues = new double[operands.length]; + + for (int i = 0; i < operands.length; i++) { + if (hasTrigOp[i]) { + processedValues[i] = calculateTrigFunction(operands[i], trigFunctions[i]); + } else { + processedValues[i] = operands[i]; + } + } + + return processedValues; + } + + private double calculateTrigFunction(int angle, int trigFunction) { + double radians = Math.toRadians(angle); + + switch (trigFunction) { + case 0: return Math.sin(radians); + case 1: return Math.cos(radians); + case 2: + if (angle % 180 == 90 && angle % 360 != 270) return Double.NaN; + return Math.tan(radians); + default: return 0; + } + } +} \ No newline at end of file diff --git a/src/com/mathlearning/model/MiddleSchoolQuestionGenerator.java b/src/com/mathlearning/model/MiddleSchoolQuestionGenerator.java new file mode 100644 index 0000000..c1b1537 --- /dev/null +++ b/src/com/mathlearning/model/MiddleSchoolQuestionGenerator.java @@ -0,0 +1,91 @@ +package com.mathlearning.model; + +public class MiddleSchoolQuestionGenerator extends BaseQuestionGenerator { + + @Override + public Question generateQuestion(int index) { + int operandCount = random.nextInt(5) + 1; + int[] operands = new int[operandCount]; + int[] operations = new int[Math.max(0, operandCount - 1)]; + boolean[] hasSpecialOp = new boolean[operandCount]; + int[] specialOpTypes = new int[operandCount]; + + ensureSpecialOperation(hasSpecialOp, specialOpTypes, operandCount); + + String questionText; + double answer; + + do { + generateOperands(operands, 1, 100); + generateOperations(operations, 4); + + questionText = buildQuestionText(operands, operations, hasSpecialOp, specialOpTypes); + answer = calculateAnswer(operands, operations, hasSpecialOp, specialOpTypes); + + } while (!isValidAnswer(answer) || Math.abs(answer) > 10000); + + return generateOptions(questionText, answer, "初中"); + } + + private void ensureSpecialOperation(boolean[] hasSpecialOp, int[] specialOpTypes, int operandCount) { + boolean hasSpecialOperation = false; + + for (int i = 0; i < operandCount; i++) { + hasSpecialOp[i] = random.nextDouble() < 0.6; + if (hasSpecialOp[i]) hasSpecialOperation = true; + } + + if (!hasSpecialOperation && operandCount > 0) { + int idx = random.nextInt(operandCount); + hasSpecialOp[idx] = true; + specialOpTypes[idx] = random.nextInt(2); + } + } + + private String buildQuestionText(int[] operands, int[] operations, + boolean[] hasSpecialOp, int[] specialOpTypes) { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < operands.length; i++) { + if (i > 0) appendOperator(sb, operations[i - 1]); + + if (hasSpecialOp[i]) { + appendSpecialOperation(sb, operands[i], specialOpTypes[i]); + } else { + sb.append(operands[i]); + } + } + + sb.append(" = ?"); + return sb.toString(); + } + + private void appendSpecialOperation(StringBuilder sb, int operand, int opType) { + if (opType == 0) { + sb.append(operand).append("²"); + } else { + sb.append("√").append(operand); + } + } + + private double calculateAnswer(int[] operands, int[] operations, + boolean[] hasSpecialOp, int[] specialOpTypes) { + double[] processedValues = processSpecialOperations(operands, hasSpecialOp, specialOpTypes); + return calculateExpression(processedValues, operations); + } + + private double[] processSpecialOperations(int[] operands, boolean[] hasSpecialOp, int[] specialOpTypes) { + double[] processedValues = new double[operands.length]; + + for (int i = 0; i < operands.length; i++) { + if (hasSpecialOp[i]) { + processedValues[i] = (specialOpTypes[i] == 0) ? + operands[i] * operands[i] : Math.sqrt(operands[i]); + } else { + processedValues[i] = operands[i]; + } + } + + return processedValues; + } +} \ No newline at end of file diff --git a/src/com/mathlearning/model/PrimaryQuestionGenerator.java b/src/com/mathlearning/model/PrimaryQuestionGenerator.java new file mode 100644 index 0000000..cdcaaad --- /dev/null +++ b/src/com/mathlearning/model/PrimaryQuestionGenerator.java @@ -0,0 +1,124 @@ +package com.mathlearning.model; + +import java.util.ArrayList; +import java.util.List; + +public class PrimaryQuestionGenerator extends BaseQuestionGenerator { + + @Override + public Question generateQuestion(int index) { + int operandCount = random.nextInt(4) + 2; + int[] operands = new int[operandCount]; + int[] operations = new int[operandCount - 1]; + + String questionText; + double answer; + + do { + generateOperands(operands, 1, 100); + generateOperations(operations, 4); + + boolean useParentheses = random.nextDouble() < 0.3 && operandCount >= 3; + int[] parenthesesPositions = findParenthesesPositions(operations, useParentheses); + + questionText = buildQuestionText(operands, operations, + parenthesesPositions[0], parenthesesPositions[1]); + answer = calculateAnswer(operands, operations, + parenthesesPositions[0], parenthesesPositions[1]); + + } while (!isValidAnswer(answer)); + + return generateOptions(questionText, answer, "小学"); + } + + private int[] findParenthesesPositions(int[] operations, boolean useParentheses) { + int parenStart = -1; + int parenEnd = -1; + + if (useParentheses) { + parenStart = findSuitableParenthesesPosition(operations); + if (parenStart != -1) { + parenEnd = parenStart + 1; + if (parenEnd < operations.length && random.nextBoolean()) { + parenEnd++; + } + } + } + + return new int[]{parenStart, parenEnd}; + } + + private int findSuitableParenthesesPosition(int[] operations) { + List suitablePositions = new ArrayList<>(); + + for (int i = 0; i < operations.length; i++) { + if ((operations[i] == 0 || operations[i] == 1) && i > 0) { + suitablePositions.add(i); + } + } + + if (suitablePositions.isEmpty()) { + return operations.length > 1 ? random.nextInt(operations.length - 1) : -1; + } + + return suitablePositions.get(random.nextInt(suitablePositions.size())); + } + + private String buildQuestionText(int[] operands, int[] operations, int parenStart, int parenEnd) { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < operands.length; i++) { + if (i == parenStart) sb.append("("); + sb.append(operands[i]); + if (i == parenEnd) sb.append(")"); + if (i < operations.length) appendOperator(sb, operations[i]); + } + + sb.append(" = ?"); + return sb.toString(); + } + + private double calculateAnswer(int[] operands, int[] operations, int parenStart, int parenEnd) { + if (parenStart == -1 || parenEnd == -1) { + return calculateWithoutParentheses(operands, operations); + } + + return calculateWithParentheses(operands, operations, parenStart, parenEnd); + } + + private double calculateWithParentheses(int[] operands, int[] operations, int parenStart, int parenEnd) { + List numbers = new ArrayList<>(); + List ops = new ArrayList<>(); + + for (int operand : operands) numbers.add(operand); + for (int operation : operations) ops.add(operation); + + double parenthesesResult = calculateParenthesesContent(numbers, ops, parenStart, parenEnd); + + if (!isValidAnswer(parenthesesResult)) return -1; + + replaceWithParenthesesResult(numbers, ops, parenStart, parenEnd, parenthesesResult); + return calculateWithoutParentheses(numbers, ops); + } + + private double calculateParenthesesContent(List numbers, List ops, int start, int end) { + List parenNumbers = new ArrayList<>(); + List parenOps = new ArrayList<>(); + + for (int i = start; i <= end; i++) parenNumbers.add(numbers.get(i)); + for (int i = start; i < end; i++) parenOps.add(ops.get(i)); + + return calculateWithoutParentheses(parenNumbers, parenOps); + } + + private void replaceWithParenthesesResult(List numbers, List ops, + int start, int end, double result) { + int numCountToRemove = end - start + 1; + int opCountToRemove = end - start; + + for (int i = 0; i < numCountToRemove; i++) numbers.remove(start); + for (int i = 0; i < opCountToRemove; i++) ops.remove(start); + + numbers.add(start, (int) Math.round(result)); + } +} \ No newline at end of file diff --git a/src/com/mathlearning/model/Question.java b/src/com/mathlearning/model/Question.java index 3094ba2..592904a 100644 --- a/src/com/mathlearning/model/Question.java +++ b/src/com/mathlearning/model/Question.java @@ -3,34 +3,45 @@ package com.mathlearning.model; public class Question { private String questionText; private String[] options; - private int correctAnswerIndex; + private int correctIndex; private String level; - public Question(String questionText, String[] options, int correctAnswerIndex, String level) { + public Question(String questionText, String[] options, int correctIndex, String level) { this.questionText = questionText; this.options = options; - this.correctAnswerIndex = correctAnswerIndex; + this.correctIndex = correctIndex; this.level = level; } - // Getters public String getQuestionText() { return questionText; } + public void setQuestionText(String questionText) { + this.questionText = questionText; + } + public String[] getOptions() { return options; } - public int getCorrectAnswerIndex() { - return correctAnswerIndex; + public void setOptions(String[] options) { + this.options = options; + } + + public int getCorrectIndex() { + return correctIndex; + } + + public void setCorrectIndex(int correctIndex) { + this.correctIndex = correctIndex; } public String getLevel() { return level; } - public boolean isCorrect(int selectedIndex) { - return selectedIndex == correctAnswerIndex; + public void setLevel(String level) { + this.level = level; } -} +} \ No newline at end of file diff --git a/src/com/mathlearning/model/QuestionGenerator.java b/src/com/mathlearning/model/QuestionGenerator.java index 2f8dda4..2b43c4f 100644 --- a/src/com/mathlearning/model/QuestionGenerator.java +++ b/src/com/mathlearning/model/QuestionGenerator.java @@ -1,701 +1,7 @@ package com.mathlearning.model; -import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Random; -import java.util.Set; -public class QuestionGenerator { - private Random random = new Random(); - - public List generateQuestions(String level, int count) { - List questions = new ArrayList<>(); - Set questionTexts = new HashSet<>(); // 防止重复题目 - - for (int i = 0; i < count; i++) { - Question question; - int attempt = 0; - do { - question = generateQuestion(level, i); - attempt++; - } while (questionTexts.contains(question.getQuestionText()) && attempt < 10); - - if (question != null) { - questionTexts.add(question.getQuestionText()); - questions.add(question); - } - } - - return questions; - } - - private Question generateQuestion(String level, int index) { - switch (level) { - case "小学": - return generatePrimaryQuestion(index); - case "初中": - return generateMiddleSchoolQuestion(index); - case "高中": - return generateHighSchoolQuestion(index); - default: - return generatePrimaryQuestion(index); - } - } - - private Question generatePrimaryQuestion(int index) { - int operandCount = random.nextInt(4) + 2; // 2-5个操作数 - int[] operands = new int[operandCount]; - int[] operations = new int[operandCount - 1]; - - String questionText; - double answer; - - do { - // 生成操作数 - for (int i = 0; i < operandCount; i++) { - operands[i] = random.nextInt(100) + 1; // 1-100 - } - - // 生成操作符 (0: +, 1: -, 2: *, 3: /) - for (int i = 0; i < operations.length; i++) { - operations[i] = random.nextInt(4); - } - - // 改进的括号逻辑:在可能改变计算顺序的地方加括号 - boolean useParentheses = random.nextDouble() < 0.3 && operandCount >= 3; - int parenStart = -1; - int parenEnd = -1; - - if (useParentheses) { - // 寻找适合加括号的位置(加减法周围) - parenStart = findSuitableParenthesesPosition(operations); - if (parenStart != -1) { - // 括号包含2-3个操作数 - parenEnd = parenStart + 1; - if (parenEnd < operands.length - 1 && random.nextBoolean()) { - parenEnd++; - } - } - } - - questionText = buildPrimaryQuestionText(operands, operations, parenStart, parenEnd); - answer = calculatePrimaryAnswerWithParentheses(operands, operations, parenStart, parenEnd); - - } while (answer < 0 || Double.isNaN(answer) || Double.isInfinite(answer)); - - return generateOptions(questionText, answer, "小学"); - } - - private Question generateMiddleSchoolQuestion(int index) { - int operandCount = random.nextInt(5) + 1; // 1-5个操作数 - int[] operands = new int[operandCount]; - int[] operations = new int[Math.max(0, operandCount - 1)]; // 操作符数组 - boolean[] hasSpecialOp = new boolean[operandCount]; // 标记哪些操作数有平方或开根号 - int[] specialOpTypes = new int[operandCount]; // 0:平方, 1:开根号 - - String questionText; - double answer; - - // 确保至少有一个平方或开根号运算符 - boolean hasSpecialOperation = false; - for (int i = 0; i < operandCount; i++) { - hasSpecialOp[i] = random.nextDouble() < 0.6; // 60%的概率有特殊运算 - if (hasSpecialOp[i]) { - hasSpecialOperation = true; - specialOpTypes[i] = random.nextInt(2); // 随机选择平方或开根号 - } - } - - // 如果没有特殊运算,强制至少一个 - if (!hasSpecialOperation && operandCount > 0) { - int idx = random.nextInt(operandCount); - hasSpecialOp[idx] = true; - specialOpTypes[idx] = random.nextInt(2); - } - - // 生成合适的题目 - do { - // 生成操作数 - for (int i = 0; i < operandCount; i++) { - operands[i] = random.nextInt(100) + 1; // 1-100 - } - - // 生成操作符 (0: +, 1: -, 2: *, 3: /) - for (int i = 0; i < operations.length; i++) { - operations[i] = random.nextInt(4); - } - - // 构建题目并计算答案 - questionText = buildMiddleSchoolQuestionText(operands, operations, hasSpecialOp, specialOpTypes); - answer = calculateMiddleSchoolAnswer(operands, operations, hasSpecialOp, specialOpTypes); - - } while (Double.isNaN(answer) || Double.isInfinite(answer) || Math.abs(answer) > 10000); - - return generateOptions(questionText, answer, "初中"); - } - - private Question generateHighSchoolQuestion(int index) { - int operandCount = random.nextInt(5) + 1; // 1-5个操作数 - int[] operands = new int[operandCount]; - int[] operations = new int[Math.max(0, operandCount - 1)]; // 操作符数组 - boolean[] hasTrigOp = new boolean[operandCount]; // 标记哪些操作数有三角函数 - int[] trigFunctions = new int[operandCount]; // 0:sin, 1:cos, 2:tan - - String questionText; - double answer; - - // 确保至少有一个三角函数运算符 - boolean hasTrigOperation = false; - for (int i = 0; i < operandCount; i++) { - hasTrigOp[i] = random.nextDouble() < 0.6; // 60%的概率有三角函数 - if (hasTrigOp[i]) { - hasTrigOperation = true; - trigFunctions[i] = random.nextInt(3); // 随机选择三角函数 - } - } - - // 如果没有三角函数,强制至少一个 - if (!hasTrigOperation && operandCount > 0) { - int idx = random.nextInt(operandCount); - hasTrigOp[idx] = true; - trigFunctions[idx] = random.nextInt(3); - } - - // 生成合适的题目 - do { - // 生成操作数 - for (int i = 0; i < operandCount; i++) { - if (hasTrigOp[i]) { - // 三角函数使用特殊角度值 - int[] specialAngles = {0, 15, 30, 45, 60, 75, 90}; - operands[i] = specialAngles[random.nextInt(specialAngles.length)]; - } else { - // 普通操作数使用1-100的随机数 - operands[i] = random.nextInt(100) + 1; - } - } - - // 生成操作符 (0: +, 1: -, 2: *, 3: /) - for (int i = 0; i < operations.length; i++) { - operations[i] = random.nextInt(4); - } - - // 构建题目并计算答案 - questionText = buildHighSchoolQuestionText(operands, operations, hasTrigOp, trigFunctions); - answer = calculateHighSchoolAnswer(operands, operations, hasTrigOp, trigFunctions); - - } while (Double.isNaN(answer) || Double.isInfinite(answer) || Math.abs(answer) > 10000); - - return generateOptions(questionText, answer, "高中"); - } - - // 小学题目相关方法 - private int findSuitableParenthesesPosition(int[] operations) { - // 寻找加减法操作符的位置,在这些地方加括号才有意义 - List suitablePositions = new ArrayList<>(); - - for (int i = 0; i < operations.length; i++) { - // 如果是加减法,且不是第一个操作符 - if ((operations[i] == 0 || operations[i] == 1) && i > 0) { - suitablePositions.add(i); - } - } - - if (suitablePositions.isEmpty()) { - // 如果没有找到合适的加减法位置,随机选择一个位置 - return operations.length > 1 ? random.nextInt(operations.length - 1) : -1; - } - - // 从合适的位置中随机选择一个 - return suitablePositions.get(random.nextInt(suitablePositions.size())); - } - - private String buildPrimaryQuestionText(int[] operands, int[] operations, int parenStart, int parenEnd) { - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < operands.length; i++) { - // 在操作数前添加左括号 - if (i == parenStart) { - sb.append("("); - } - - // 添加操作数 - sb.append(operands[i]); - - // 在操作数后添加右括号(但要在操作符之前) - if (i == parenEnd) { - sb.append(")"); - } - - // 添加操作符(如果不是最后一个操作数) - if (i < operations.length) { - switch (operations[i]) { - case 0: sb.append(" + "); break; - case 1: sb.append(" - "); break; - case 2: sb.append(" × "); break; - case 3: sb.append(" ÷ "); break; - } - } - } - sb.append(" = ?"); - - return sb.toString(); - } - - private double calculatePrimaryAnswerWithParentheses(int[] operands, int[] operations, int parenStart, int parenEnd) { - // 如果没有括号,使用原来的计算方法 - if (parenStart == -1 || parenEnd == -1) { - return calculatePrimaryAnswer(operands, operations); - } - - // 复制操作数和操作符 - List numbers = new ArrayList<>(); - List ops = new ArrayList<>(); - - for (int operand : operands) { - numbers.add(operand); - } - for (int operation : operations) { - ops.add(operation); - } - - // 计算括号内的表达式 - double parenthesesResult = calculateParenthesesContent(numbers, ops, parenStart, parenEnd); - - // 验证括号计算结果是否有效 - if (parenthesesResult < 0 || Double.isNaN(parenthesesResult) || Double.isInfinite(parenthesesResult)) { - return -1; - } - - // 用括号计算结果替换括号内的所有元素 - replaceWithParenthesesResult(numbers, ops, parenStart, parenEnd, parenthesesResult); - - // 计算剩余表达式 - return calculateWithoutParentheses(numbers, ops); - } - - private double calculateParenthesesContent(List numbers, List ops, int start, int end) { - // 提取括号内的数字和操作符 - List parenNumbers = new ArrayList<>(); - List parenOps = new ArrayList<>(); - - for (int i = start; i <= end; i++) { - parenNumbers.add(numbers.get(i)); - } - for (int i = start; i < end; i++) { - parenOps.add(ops.get(i)); - } - - // 计算括号内的表达式(精确计算) - return calculateWithoutParentheses(parenNumbers, parenOps); - } - - private void replaceWithParenthesesResult(List numbers, List ops, - int start, int end, double result) { - // 计算需要移除的元素数量 - int numCountToRemove = end - start + 1; - int opCountToRemove = end - start; - - // 移除括号范围内的数字 - for (int i = 0; i < numCountToRemove; i++) { - numbers.remove(start); - } - - // 移除括号范围内的操作符 - for (int i = 0; i < opCountToRemove; i++) { - ops.remove(start); - } - - // 插入括号计算结果 - numbers.add(start, (int) Math.round(result)); - } - - private double calculateWithoutParentheses(List numbers, List ops) { - // 转换为double列表进行计算(精确计算) - List doubleNumbers = new ArrayList<>(); - for (Integer num : numbers) { - doubleNumbers.add(num.doubleValue()); - } - - // 先处理乘除法(精确计算) - for (int i = 0; i < ops.size(); i++) { - int op = ops.get(i); - if (op == 2 || op == 3) { - double left = doubleNumbers.get(i); - double right = doubleNumbers.get(i + 1); - double result; - - if (op == 2) { - result = left * right; - } else { - if (right == 0) return Double.NaN; - result = left / right; - } - - doubleNumbers.set(i, result); - doubleNumbers.remove(i + 1); - ops.remove(i); - i--; - } - } - - // 处理加减法(精确计算) - double result = doubleNumbers.get(0); - for (int i = 0; i < ops.size(); i++) { - int op = ops.get(i); - double nextNum = doubleNumbers.get(i + 1); - - if (op == 0) { - result += nextNum; - } else { - if (result < nextNum) return -1; - result -= nextNum; - } - } - - // 只在最后结果保留2位小数 - return Math.round(result * 100) / 100.0; - } - - private double calculatePrimaryAnswer(int[] operands, int[] operations) { - // 转换为double列表(精确计算) - List numbers = new ArrayList<>(); - List ops = new ArrayList<>(); - - for (int operand : operands) { - numbers.add((double) operand); - } - for (int operation : operations) { - ops.add(operation); - } - - // 处理乘除法(精确计算) - for (int i = 0; i < ops.size(); i++) { - int op = ops.get(i); - if (op == 2 || op == 3) { - double left = numbers.get(i); - double right = numbers.get(i + 1); - double result; - - if (op == 2) { - result = left * right; - } else { - if (right == 0) return Double.NaN; - result = left / right; - } - - numbers.set(i, result); - numbers.remove(i + 1); - ops.remove(i); - i--; - } - } - - // 处理加减法(精确计算) - double result = numbers.get(0); - for (int i = 0; i < ops.size(); i++) { - int op = ops.get(i); - double nextNum = numbers.get(i + 1); - - if (op == 0) { - result += nextNum; - } else { - if (result < nextNum) return -1; - result -= nextNum; - } - } - - // 只在最后结果保留2位小数 - return Math.round(result * 100) / 100.0; - } - - // 初中题目相关方法 - private String buildMiddleSchoolQuestionText(int[] operands, int[] operations, boolean[] hasSpecialOp, int[] specialOpTypes) { - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < operands.length; i++) { - if (i > 0) { - switch (operations[i - 1]) { - case 0: sb.append(" + "); break; - case 1: sb.append(" - "); break; - case 2: sb.append(" × "); break; - case 3: sb.append(" ÷ "); break; - } - } - - if (hasSpecialOp[i]) { - if (specialOpTypes[i] == 0) { - // 平方 - sb.append(operands[i]).append("²"); - } else { - // 开根号 - sb.append("√").append(operands[i]); - } - } else { - sb.append(operands[i]); - } - } - sb.append(" = ?"); - - return sb.toString(); - } - - private double calculateMiddleSchoolAnswer(int[] operands, int[] operations, boolean[] hasSpecialOp, int[] specialOpTypes) { - // 先处理特殊运算(平方和开根号)- 精确计算 - double[] processedValues = new double[operands.length]; - for (int i = 0; i < operands.length; i++) { - if (hasSpecialOp[i]) { - if (specialOpTypes[i] == 0) { - // 平方 - processedValues[i] = operands[i] * operands[i]; - } else { - // 开根号 - processedValues[i] = Math.sqrt(operands[i]); - } - } else { - processedValues[i] = operands[i]; - } - } - - // 然后按照小学的计算逻辑处理(精确计算) - List numbers = new ArrayList<>(); - List ops = new ArrayList<>(); - - for (double value : processedValues) { - numbers.add(value); - } - for (int operation : operations) { - ops.add(operation); - } - - // 处理乘除法(精确计算) - for (int i = 0; i < ops.size(); i++) { - int op = ops.get(i); - if (op == 2 || op == 3) { - double left = numbers.get(i); - double right = numbers.get(i + 1); - double result; - - if (op == 2) { - result = left * right; - } else { - if (right == 0) return Double.NaN; - result = left / right; - } - - numbers.set(i, result); - numbers.remove(i + 1); - ops.remove(i); - i--; - } - } - - // 处理加减法(精确计算) - double result = numbers.get(0); - for (int i = 0; i < ops.size(); i++) { - int op = ops.get(i); - double nextNum = numbers.get(i + 1); - - if (op == 0) { - result += nextNum; - } else { - result -= nextNum; - } - } - - // 只在最后结果保留2位小数 - return Math.round(result * 100) / 100.0; - } - - // 高中题目相关方法 - private String buildHighSchoolQuestionText(int[] operands, int[] operations, boolean[] hasTrigOp, int[] trigFunctions) { - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < operands.length; i++) { - if (i > 0) { - switch (operations[i - 1]) { - case 0: sb.append(" + "); break; - case 1: sb.append(" - "); break; - case 2: sb.append(" × "); break; - case 3: sb.append(" ÷ "); break; - } - } - - if (hasTrigOp[i]) { - switch (trigFunctions[i]) { - case 0: sb.append("sin(").append(operands[i]).append("°)"); break; - case 1: sb.append("cos(").append(operands[i]).append("°)"); break; - case 2: sb.append("tan(").append(operands[i]).append("°)"); break; - } - } else { - sb.append(operands[i]); - } - } - sb.append(" = ?"); - - return sb.toString(); - } - - private double calculateHighSchoolAnswer(int[] operands, int[] operations, boolean[] hasTrigOp, int[] trigFunctions) { - // 先处理三角函数运算(精确计算) - double[] processedValues = new double[operands.length]; - for (int i = 0; i < operands.length; i++) { - if (hasTrigOp[i]) { - double radians = Math.toRadians(operands[i]); - switch (trigFunctions[i]) { - case 0: - processedValues[i] = Math.sin(radians); - break; - case 1: - processedValues[i] = Math.cos(radians); - break; - case 2: - // 避免tan(90°)等无效值 - if (operands[i] % 180 == 90 && operands[i] % 360 != 270) { - return Double.NaN; - } - processedValues[i] = Math.tan(radians); - break; - default: - processedValues[i] = 0; - } - } else { - processedValues[i] = operands[i]; - } - } - - // 然后按照常规计算逻辑处理(精确计算) - List numbers = new ArrayList<>(); - List ops = new ArrayList<>(); - - for (double value : processedValues) { - numbers.add(value); - } - for (int operation : operations) { - ops.add(operation); - } - - // 处理乘除法(精确计算) - for (int i = 0; i < ops.size(); i++) { - int op = ops.get(i); - if (op == 2 || op == 3) { - double left = numbers.get(i); - double right = numbers.get(i + 1); - double result; - - if (op == 2) { - result = left * right; - } else { - if (right == 0) return Double.NaN; - result = left / right; - } - - numbers.set(i, result); - numbers.remove(i + 1); - ops.remove(i); - i--; - } - } - - // 处理加减法(精确计算) - double result = numbers.get(0); - for (int i = 0; i < ops.size(); i++) { - int op = ops.get(i); - double nextNum = numbers.get(i + 1); - - if (op == 0) { - result += nextNum; - } else { - result -= nextNum; - } - } - - // 只在最后结果保留2位小数 - return Math.round(result * 100) / 100.0; - } - - // 生成选项的通用方法 - private Question generateOptions(String questionText, double answer, String level) { - String[] options = new String[4]; - Set usedValues = new HashSet<>(); - - // 检查答案是否为整数 - boolean isIntegerAnswer = (answer == (int) answer); - - // 格式化正确答案 - String correctAnswer = formatAnswer(answer); - options[0] = correctAnswer; - usedValues.add(correctAnswer); - - // 生成错误答案 - for (int i = 1; i < 4; i++) { - String wrongAnswer; - int attempts = 0; - do { - // 基于正确答案生成错误答案 - double wrongValue; - double offset = (random.nextDouble() * 5) + 1; // 1-6的偏移量 - - if (isIntegerAnswer) { - // 如果答案是整数,生成整数错误答案 - int intAnswer = (int) answer; - int intOffset = random.nextInt(10) + 1; // 1-10的整数偏移 - if (random.nextBoolean()) { - wrongValue = intAnswer + intOffset; - } else { - wrongValue = Math.max(1, intAnswer - intOffset); - } - // 确保是整数 - wrongValue = (int) wrongValue; - } else { - // 如果答案是小数,生成小数错误答案 - if (random.nextBoolean()) { - wrongValue = answer + offset; - } else { - wrongValue = answer - offset; - } - // 保留2位小数 - wrongValue = Math.round(wrongValue * 100) / 100.0; - } - - wrongAnswer = formatAnswer(wrongValue); - attempts++; - } while (usedValues.contains(wrongAnswer) && attempts < 20); - - options[i] = wrongAnswer; - usedValues.add(wrongAnswer); - } - - // 打乱选项顺序 - shuffleArray(options); - - // 找到正确答案的新位置 - int correctIndex = 0; - for (int i = 0; i < options.length; i++) { - if (options[i].equals(correctAnswer)) { - correctIndex = i; - break; - } - } - - return new Question(questionText, options, correctIndex, level); - } - - private String formatAnswer(double answer) { - if (answer == (int) answer) { - return String.valueOf((int) answer); // 整数不显示小数 - } else { - return String.format("%.2f", answer); // 小数保留2位 - } - } - - private void shuffleArray(String[] array) { - for (int i = array.length - 1; i > 0; i--) { - int j = random.nextInt(i + 1); - String temp = array[i]; - array[i] = array[j]; - array[j] = temp; - } - } +public interface QuestionGenerator { + List generateQuestions(String level, int count); } \ No newline at end of file diff --git a/src/com/mathlearning/view/QuizFrame.java b/src/com/mathlearning/view/QuizFrame.java index 48590cf..002e833 100644 --- a/src/com/mathlearning/view/QuizFrame.java +++ b/src/com/mathlearning/view/QuizFrame.java @@ -1,6 +1,5 @@ package com.mathlearning.view; -import com.mathlearning.controller.AuthController; import com.mathlearning.controller.QuestionController; import com.mathlearning.model.Question; import javax.swing.*; @@ -58,12 +57,23 @@ public class QuizFrame extends JFrame { JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - // 进度条 + // 顶部面板(退出按钮和进度条) + JPanel topPanel = new JPanel(new BorderLayout()); + + JButton exitButton = new JButton("退出答题"); + exitButton.setFont(new Font("Microsoft YaHei", Font.PLAIN, 12)); + exitButton.setBackground(new Color(220, 100, 100)); + exitButton.setForeground(Color.WHITE); + exitButton.addActionListener(e -> exitQuiz()); + progressBar = new JProgressBar(0, totalQuestions); progressBar.setValue(0); progressBar.setStringPainted(true); progressBar.setFont(new Font("Microsoft YaHei", Font.PLAIN, 12)); - mainPanel.add(progressBar, BorderLayout.NORTH); + + topPanel.add(exitButton, BorderLayout.WEST); + topPanel.add(progressBar, BorderLayout.CENTER); + mainPanel.add(topPanel, BorderLayout.NORTH); // 问题面板 JPanel questionPanel = new JPanel(new BorderLayout(10, 20)); @@ -84,7 +94,7 @@ public class QuizFrame extends JFrame { // 选项面板 JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 10, 10)); optionsPanel.setBorder( - BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.GRAY), "请选择答案")); + BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.GRAY), "请选择答案")); optionButtons = new JRadioButton[4]; buttonGroup = new ButtonGroup(); @@ -98,11 +108,11 @@ public class QuizFrame extends JFrame { final int index = i; optionButtons[i].addActionListener( - e -> { - // 保存当前选择 - userSelections[currentDisplayIndex] = index; - System.out.println("用户选择: 第" + (currentDisplayIndex + 1) + "题 -> 选项" + index); - }); + e -> { + // 保存当前选择 + userSelections[currentDisplayIndex] = index; + System.out.println("用户选择: 第" + (currentDisplayIndex + 1) + "题 -> 选项" + index); + }); buttonGroup.add(optionButtons[i]); optionsPanel.add(optionButtons[i]); @@ -110,7 +120,6 @@ public class QuizFrame extends JFrame { JPanel optionsContainer = new JPanel(new FlowLayout(FlowLayout.CENTER)); optionsContainer.add(optionsPanel); - mainPanel.add(optionsContainer, BorderLayout.SOUTH); // 控制面板 JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); @@ -141,23 +150,7 @@ public class QuizFrame extends JFrame { // 添加事件监听器 previousButton.addActionListener(new PreviousQuestionAction()); nextButton.addActionListener(new NextQuestionAction()); - // 添加顶部退出答题按钮 - JPanel topPanel = new JPanel(new BorderLayout()); - JButton exitButton = new JButton("退出答题"); - exitButton.setFont(new Font("Microsoft YaHei", Font.PLAIN, 12)); - exitButton.setBackground(new Color(220, 100, 100)); - exitButton.setForeground(Color.WHITE); - exitButton.addActionListener(e -> exitQuiz()); - - progressBar = new JProgressBar(0, totalQuestions); - progressBar.setValue(0); - progressBar.setStringPainted(true); - progressBar.setFont(new Font("Microsoft YaHei", Font.PLAIN, 12)); - - topPanel.add(exitButton, BorderLayout.WEST); - topPanel.add(progressBar, BorderLayout.CENTER); - mainPanel.add(topPanel, BorderLayout.NORTH); add(mainPanel); } @@ -168,16 +161,16 @@ public class QuizFrame extends JFrame { } Question currentQuestion = allQuestions.get(currentDisplayIndex); questionNumberLabel.setText( - String.format("第 %d 题 / 共 %d 题", currentDisplayIndex + 1, totalQuestions)); + String.format("第 %d 题 / 共 %d 题", currentDisplayIndex + 1, totalQuestions)); questionTextLabel.setText( - "
" - + currentQuestion.getQuestionText() - + "
"); + "
" + + currentQuestion.getQuestionText() + + "
"); String[] options = currentQuestion.getOptions(); for (int i = 0; i < 4; i++) { String optionText = - String.format( - "
%c. %s
", (char) ('A' + i), options[i]); + String.format( + "
%c. %s
", (char) ('A' + i), options[i]); optionButtons[i].setText(optionText); } buttonGroup.clearSelection(); // 清除当前选择状态 @@ -191,17 +184,17 @@ public class QuizFrame extends JFrame { // 更新进度条 progressBar.setValue(currentDisplayIndex); progressBar.setString( - String.format( - "%d/%d (%.0f%%)", - currentDisplayIndex, - totalQuestions, - ((double) currentDisplayIndex / totalQuestions) * 100)); + String.format( + "%d/%d (%.0f%%)", + currentDisplayIndex, + totalQuestions, + ((double) currentDisplayIndex / totalQuestions) * 100)); updateButtonStates(); // 更新按钮状态 } private void exitQuiz() { int result = - JOptionPane.showConfirmDialog(this, "确定要退出答题吗?所有进度将丢失。", "退出确认", JOptionPane.YES_NO_OPTION); + JOptionPane.showConfirmDialog(this, "确定要退出答题吗?所有进度将丢失。", "退出确认", JOptionPane.YES_NO_OPTION); if (result == JOptionPane.YES_OPTION) { parentFrame.setVisible(true); this.dispose(); @@ -245,8 +238,8 @@ public class QuizFrame extends JFrame { // 如果没有选择答案,提示用户但允许继续(除了最后一题) if (selectedIndex == -1) { int result = - JOptionPane.showConfirmDialog( - QuizFrame.this, "您还没有选择答案,确定要继续下一题吗?", "确认", JOptionPane.YES_NO_OPTION); + JOptionPane.showConfirmDialog( + QuizFrame.this, "您还没有选择答案,确定要继续下一题吗?", "确认", JOptionPane.YES_NO_OPTION); if (result != JOptionPane.YES_OPTION) { return; } @@ -262,52 +255,27 @@ public class QuizFrame extends JFrame { } } - private void submitAllAnswers() { // 直接在本地计算分数 - final int calculatedScore = calculateLocalScore(); // 使用final - // 创建一个新的QuestionController来显示正确分数 - questionController = - new QuestionController() { - @Override - public int getScore() { - return calculatedScore; // 现在可以访问了 - } - - @Override - public int getTotalQuestions() { - return totalQuestions; - } - - @Override - public double getPercentage() { - return (double) calculatedScore / totalQuestions * 100; - } - - @Override - public int[] getUserAnswers() { - return userSelections; - } - - @Override - public java.util.List getCurrentQuestions() { - return allQuestions; - } - }; - } - - private int calculateLocalScore() { + private void submitAllAnswers() { + // 计算分数 int score = 0; for (int i = 0; i < totalQuestions; i++) { - if (userSelections[i] != -1 && allQuestions.get(i).isCorrect(userSelections[i])) { - score++; + if (userSelections[i] != -1) { + // 使用 QuestionController 来检查答案 + questionController.setCurrentQuestionIndex(i); + if (questionController.submitAnswer(userSelections[i])) { + score++; + } } } - return score; + + // 重置到第一个问题,以便在结果显示时能正确访问 + questionController.setCurrentQuestionIndex(0); } private void finishQuiz() { - ScoreFrame scoreFrame = - new ScoreFrame(questionController, userSelections, allQuestions, parentFrame); + // 使用原有的构造函数 + ScoreFrame scoreFrame = new ScoreFrame(questionController, userSelections, allQuestions, parentFrame); scoreFrame.setVisible(true); this.dispose(); } -} +} // 添加这个闭合大括号 \ No newline at end of file diff --git a/src/com/mathlearning/view/ScoreFrame.java b/src/com/mathlearning/view/ScoreFrame.java index 9e93212..21e9edc 100644 --- a/src/com/mathlearning/view/ScoreFrame.java +++ b/src/com/mathlearning/view/ScoreFrame.java @@ -13,10 +13,10 @@ public class ScoreFrame extends JFrame { private LevelSelectionFrame parentFrame; public ScoreFrame( - QuestionController questionController, - int[] userSelections, - List allQuestions, - LevelSelectionFrame parent) { + QuestionController questionController, + int[] userSelections, + List allQuestions, + LevelSelectionFrame parent) { this.parentFrame = parent; this.questionController = questionController; this.userSelections = userSelections; @@ -34,13 +34,13 @@ public class ScoreFrame extends JFrame { JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - // Score summary + // Score summary - 修复这里 int score = questionController.getScore(); int total = questionController.getTotalQuestions(); - double percentage = questionController.getPercentage(); + double percentage = (double) score / total * 100; // 手动计算百分比 JLabel scoreLabel = - new JLabel(String.format("得分: %d/%d (%.1f%%)", score, total, percentage), JLabel.CENTER); + new JLabel(String.format("得分: %d/%d (%.1f%%)", score, total, percentage), JLabel.CENTER); scoreLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 24)); Color color; @@ -62,21 +62,22 @@ public class ScoreFrame extends JFrame { for (int i = 0; i < allQuestions.size(); i++) { Question q = allQuestions.get(i); int userAnswer = userSelections[i]; - boolean isCorrect = (userAnswer != -1) && q.isCorrect(userAnswer); + // 修复这里:使用 getCorrectIndex() 而不是 getCorrectAnswerIndex() + boolean isCorrect = (userAnswer != -1) && (userAnswer == q.getCorrectIndex()); details.append(String.format("第%d题: %s\n", i + 1, q.getQuestionText())); details.append( - String.format( - "你的答案: %s\n", - userAnswer == -1 - ? "未作答" - : (char) ('A' + userAnswer) + ". " + q.getOptions()[userAnswer])); + String.format( + "你的答案: %s\n", + userAnswer == -1 + ? "未作答" + : (char) ('A' + userAnswer) + ". " + q.getOptions()[userAnswer])); details.append( - String.format( - "正确答案: %s\n", - (char) ('A' + q.getCorrectAnswerIndex()) - + ". " - + q.getOptions()[q.getCorrectAnswerIndex()])); + String.format( + "正确答案: %s\n", + (char) ('A' + q.getCorrectIndex()) + + ". " + + q.getOptions()[q.getCorrectIndex()])); details.append(isCorrect ? "✓ 正确\n" : "✗ 错误\n"); details.append("--------------------\n"); } @@ -108,4 +109,4 @@ public class ScoreFrame extends JFrame { parentFrame.setVisible(true); this.dispose(); } -} +} \ No newline at end of file -- 2.34.1 From 46e201e0026110df5125feead89fd6b9b453c8a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=84=8F?= <1449892469@qq.com> Date: Sun, 12 Oct 2025 09:47:04 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=B0=8F=E5=AD=A6?= =?UTF-8?q?=E9=A2=98=E7=9B=AE=E8=AE=A1=E7=AE=97=E7=B2=BE=E5=BA=A6=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=9A=E5=85=88=E7=B2=BE=E7=A1=AE=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E5=86=8D=E4=BF=9D=E7=95=99=E5=B0=8F=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/BaseQuestionGenerator.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/com/mathlearning/model/BaseQuestionGenerator.java b/src/com/mathlearning/model/BaseQuestionGenerator.java index 88cec22..2a82651 100644 --- a/src/com/mathlearning/model/BaseQuestionGenerator.java +++ b/src/com/mathlearning/model/BaseQuestionGenerator.java @@ -36,6 +36,7 @@ public abstract class BaseQuestionGenerator { return !(answer < 0 || Double.isNaN(answer) || Double.isInfinite(answer)); } + // 修改:使用double进行精确计算,只在最后保留小数 protected double calculateExpression(double[] values, int[] operations) { List numbers = new ArrayList<>(); List ops = new ArrayList<>(); @@ -94,6 +95,7 @@ public abstract class BaseQuestionGenerator { result = performOperation(result, nextNum, op); } + // 修改:只在最后一步进行四舍五入保留2位小数 return Math.round(result * 100) / 100.0; } @@ -104,6 +106,7 @@ public abstract class BaseQuestionGenerator { case 2: return left * right; case 3: if (right == 0) return Double.NaN; + // 修改:直接进行除法,不在这里保留小数 return left / right; default: return left; } @@ -113,8 +116,9 @@ public abstract class BaseQuestionGenerator { String[] options = new String[4]; Set usedValues = new HashSet<>(); - boolean isIntegerAnswer = (answer == (int) answer); - String correctAnswer = formatAnswer(answer); + // 修改:判断是否为整数的逻辑 + boolean isIntegerAnswer = Math.abs(answer - Math.round(answer)) < 1e-10; + String correctAnswer = formatAnswer(answer, isIntegerAnswer); options[0] = correctAnswer; usedValues.add(correctAnswer); @@ -134,7 +138,8 @@ public abstract class BaseQuestionGenerator { do { double wrongValue = generateWrongValue(answer, isIntegerAnswer); - wrongAnswer = formatAnswer(wrongValue); + wrongAnswer = formatAnswer(wrongValue, + Math.abs(wrongValue - Math.round(wrongValue)) < 1e-10); attempts++; } while (usedValues.contains(wrongAnswer) && attempts < 20); @@ -147,20 +152,26 @@ public abstract class BaseQuestionGenerator { double offset = (random.nextDouble() * 5) + 1; if (isIntegerAnswer) { - int intAnswer = (int) answer; + int intAnswer = (int) Math.round(answer); int intOffset = random.nextInt(10) + 1; double wrongValue = random.nextBoolean() ? intAnswer + intOffset : Math.max(1, intAnswer - intOffset); - return (int) wrongValue; + return wrongValue; } else { double wrongValue = random.nextBoolean() ? answer + offset : answer - offset; + // 修改:错误选项也保留2位小数 return Math.round(wrongValue * 100) / 100.0; } } - private String formatAnswer(double answer) { - return (answer == (int) answer) ? - String.valueOf((int) answer) : String.format("%.2f", answer); + // 修改:格式化答案,只在最后一步处理小数位数 + private String formatAnswer(double answer, boolean isInteger) { + if (isInteger) { + return String.valueOf((int) Math.round(answer)); + } else { + // 确保显示2位小数,即使末尾是0 + return String.format("%.2f", answer); + } } private void shuffleArray(String[] array) { -- 2.34.1 From 163c67305d6362360073a652fe469bc5f29c9907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=84=8F?= <1449892469@qq.com> Date: Sun, 12 Oct 2025 10:01:36 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=B0=8F=E5=AD=A6?= =?UTF-8?q?=E9=A2=98=E7=9B=AE=E8=AE=A1=E7=AE=97=E7=B2=BE=E5=BA=A6=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=9A=E9=99=A4=E6=B3=95=E9=99=A4=E6=95=B0=E6=8E=A5?= =?UTF-8?q?=E8=BF=910=E6=97=B6=E4=B8=8D=E8=A2=AB=E8=88=8D=E5=8E=BB?= =?UTF-8?q?=E8=80=8C=E5=AF=BC=E8=87=B4=E7=BB=93=E6=9E=9C=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/PrimaryQuestionGenerator.java | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/com/mathlearning/model/PrimaryQuestionGenerator.java b/src/com/mathlearning/model/PrimaryQuestionGenerator.java index cdcaaad..675322f 100644 --- a/src/com/mathlearning/model/PrimaryQuestionGenerator.java +++ b/src/com/mathlearning/model/PrimaryQuestionGenerator.java @@ -87,10 +87,11 @@ public class PrimaryQuestionGenerator extends BaseQuestionGenerator { } private double calculateWithParentheses(int[] operands, int[] operations, int parenStart, int parenEnd) { - List numbers = new ArrayList<>(); + // 修改:使用Double列表 + List numbers = new ArrayList<>(); List ops = new ArrayList<>(); - for (int operand : operands) numbers.add(operand); + for (int operand : operands) numbers.add((double) operand); for (int operation : operations) ops.add(operation); double parenthesesResult = calculateParenthesesContent(numbers, ops, parenStart, parenEnd); @@ -98,20 +99,22 @@ public class PrimaryQuestionGenerator extends BaseQuestionGenerator { if (!isValidAnswer(parenthesesResult)) return -1; replaceWithParenthesesResult(numbers, ops, parenStart, parenEnd, parenthesesResult); - return calculateWithoutParentheses(numbers, ops); + // 修改:调用新的方法名 + return calculateDoubleList(numbers, ops); } - private double calculateParenthesesContent(List numbers, List ops, int start, int end) { - List parenNumbers = new ArrayList<>(); + private double calculateParenthesesContent(List numbers, List ops, int start, int end) { + List parenNumbers = new ArrayList<>(); List parenOps = new ArrayList<>(); for (int i = start; i <= end; i++) parenNumbers.add(numbers.get(i)); for (int i = start; i < end; i++) parenOps.add(ops.get(i)); - return calculateWithoutParentheses(parenNumbers, parenOps); + // 修改:调用新的方法名 + return calculateDoubleList(parenNumbers, parenOps); } - private void replaceWithParenthesesResult(List numbers, List ops, + private void replaceWithParenthesesResult(List numbers, List ops, int start, int end, double result) { int numCountToRemove = end - start + 1; int opCountToRemove = end - start; @@ -119,6 +122,22 @@ public class PrimaryQuestionGenerator extends BaseQuestionGenerator { for (int i = 0; i < numCountToRemove; i++) numbers.remove(start); for (int i = 0; i < opCountToRemove; i++) ops.remove(start); - numbers.add(start, (int) Math.round(result)); + // 修改:直接添加double结果,不转换为int + numbers.add(start, result); + } + + // 修改:重命名方法,避免与父类冲突 + private double calculateDoubleList(List numbers, List ops) { + double[] doubleNumbers = new double[numbers.size()]; + for (int i = 0; i < numbers.size(); i++) { + doubleNumbers[i] = numbers.get(i); + } + + int[] intOps = new int[ops.size()]; + for (int i = 0; i < ops.size(); i++) { + intOps[i] = ops.get(i); + } + + return calculateExpression(doubleNumbers, intOps); } } \ No newline at end of file -- 2.34.1