最新版本 #1

Open
hnu202326010221 wants to merge 5 commits from liuyi_branch into develop

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" project-jdk-name="23" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" project-jdk-name="23" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

@ -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<Question> 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<Question> generateQuestions(String level, int count) {
return questionGenerator.generateQuestions(level, count);
}
// 原有的显示问题方法
public void displayQuestions(String level, int count) {
List<Question> 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<Question> 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<Question> getCurrentQuestions() {
// 获取所有问题(用于显示结果等)
public List<Question> 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;
}
}
}

@ -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<Question> generateQuestions(String level, int count) {
List<Question> questions = new ArrayList<>();
Set<String> 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<String> 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);
}

@ -0,0 +1,192 @@
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));
}
// 修改使用double进行精确计算只在最后保留小数
protected double calculateExpression(double[] values, int[] operations) {
List<Double> numbers = new ArrayList<>();
List<Integer> 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<Integer> numbers, List<Integer> 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<Double> numbers, List<Integer> 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<Double> numbers, List<Integer> 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);
}
// 修改只在最后一步进行四舍五入保留2位小数
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<String> usedValues = new HashSet<>();
// 修改:判断是否为整数的逻辑
boolean isIntegerAnswer = Math.abs(answer - Math.round(answer)) < 1e-10;
String correctAnswer = formatAnswer(answer, isIntegerAnswer);
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<String> 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,
Math.abs(wrongValue - Math.round(wrongValue)) < 1e-10);
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) Math.round(answer);
int intOffset = random.nextInt(10) + 1;
double wrongValue = random.nextBoolean() ?
intAnswer + intOffset : Math.max(1, intAnswer - intOffset);
return wrongValue;
} else {
double wrongValue = random.nextBoolean() ? answer + offset : answer - offset;
// 修改错误选项也保留2位小数
return Math.round(wrongValue * 100) / 100.0;
}
}
// 修改:格式化答案,只在最后一步处理小数位数
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) {
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;
}
}

@ -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);
}
}
}

@ -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;
}
}
}

@ -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;
}
}

@ -0,0 +1,143 @@
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<Integer> 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) {
// 修改使用Double列表
List<Double> numbers = new ArrayList<>();
List<Integer> ops = new ArrayList<>();
for (int operand : operands) numbers.add((double) 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 calculateDoubleList(numbers, ops);
}
private double calculateParenthesesContent(List<Double> numbers, List<Integer> ops, int start, int end) {
List<Double> parenNumbers = new ArrayList<>();
List<Integer> 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 calculateDoubleList(parenNumbers, parenOps);
}
private void replaceWithParenthesesResult(List<Double> numbers, List<Integer> 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);
// 修改直接添加double结果不转换为int
numbers.add(start, result);
}
// 修改:重命名方法,避免与父类冲突
private double calculateDoubleList(List<Double> numbers, List<Integer> 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);
}
}

@ -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;
}
}
}

@ -1,486 +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<Question> generateQuestions(String level, int count) {
List<Question> questions = new ArrayList<>();
Set<String> 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;
List<Integer> operands = new ArrayList<>();
List<Character> operators = new ArrayList<>();
// 生成操作数和运算符,确保结果非负
generateSafePrimaryExpression(operands, operators, operandCount);
String questionText;
int result;
int attempts = 0;
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);
}
attempts++;
if (result < 0) {
// 重新生成安全的表达式
generateSafePrimaryExpression(operands, operators, operandCount);
}
} 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);
}
return generateIntOptions(questionText + " = ?", result, "小学");
}
private void generateSafePrimaryExpression(List<Integer> operands, List<Character> operators, int operandCount) {
operands.clear();
operators.clear();
// 首先生成所有操作数
for (int i = 0; i < operandCount; i++) {
operands.add(random.nextInt(100) + 1);
}
// 然后生成安全的运算符序列
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;
}
}
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 (expression.result < 0) {
expression = createSquareExpression(); // 重新生成一个肯定为正的题目
}
return generateIntOptions(expression.questionText + " = ?", expression.result, "初中");
}
private MathExpression createSquareExpression() {
int a = random.nextInt(10) + 1;
int b = random.nextInt(100) + 1;
int c = random.nextInt(100) + 1;
// 只使用加法确保结果为正
char op = '+';
String questionText = a + "² " + op + " " + b + " × " + c;
int result = a * a + b * c;
return new MathExpression(questionText, result);
}
private MathExpression createSqrtExpression() {
int sqrtBase = findPerfectSquare(100);
int sqrtVal = (int) Math.sqrt(sqrtBase);
int d = random.nextInt(100) + 1;
// 只使用加法确保结果为正
char op = '+';
String questionText = "√" + sqrtBase + " " + op + " " + d;
int result = sqrtVal + d;
return new MathExpression(questionText, result);
}
private MathExpression createSquareProductExpression() {
int e = random.nextInt(100) + 1;
int f = random.nextInt(10) + 1;
int g = random.nextInt(100) + 1;
// 只使用加法确保结果为正
char op = '+';
String questionText = e + " × " + f + "² " + op + " " + g;
int result = e * f * f + g;
return new MathExpression(questionText, result);
}
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);
String questionText = "(" + h + " + " + i + ")² ÷ " + j;
int result = square / j;
return new MathExpression(questionText, result);
}
private Question generateHighSchoolQuestion(int index) {
int type = random.nextInt(4);
TrigExpression expression = new TrigExpression();
switch (type) {
case 0:
expression = createSinExpression();
break;
case 1:
expression = createCosExpression();
break;
case 2:
expression = createTanExpression();
break;
case 3:
expression = createSinCosExpression();
break;
}
return generateDoubleOptions(expression.questionText + " = ?", expression.result, "高中");
}
private TrigExpression createSinExpression() {
String[] angles = {"0°", "30°", "45°", "60°", "90°"};
double[] sinValues = {0, 0.5, 0.71, 0.87, 1.0};
int idx = random.nextInt(angles.length);
int b = random.nextInt(100) + 1;
// 只使用加法确保结果为正
char op = '+';
String questionText = "sin(" + angles[idx] + ") " + op + " " + b;
double result = sinValues[idx] + b;
return new TrigExpression(questionText, result);
}
private TrigExpression createCosExpression() {
String[] angles = {"0°", "30°", "45°", "60°"};
double[] cosValues = {1.0, 0.87, 0.71, 0.5};
int idx = random.nextInt(angles.length);
int a = random.nextInt(100) + 1;
String questionText = a + " × cos(" + angles[idx] + ")";
double result = a * cosValues[idx];
return new TrigExpression(questionText, result);
}
private TrigExpression createTanExpression() {
String[] angles = {"0°", "30°", "45°", "60°"};
double[] tanValues = {0, 0.58, 1.0, 1.73};
int idx = random.nextInt(angles.length);
int c = random.nextInt(20) + 1;
String questionText = "tan(" + angles[idx] + ") ÷ " + c;
double result = tanValues[idx] / c;
return new TrigExpression(questionText, result);
}
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};
int idx1 = random.nextInt(angles.length);
int idx2 = random.nextInt(angles.length);
int d = random.nextInt(100) + 1;
// 只使用加法确保结果为正
char op = '+';
String questionText = "sin(" + angles[idx1] + ") × cos(" + angles[idx2] + ") " + op + " " + d;
double product = sinValues[idx1] * cosValues[idx2];
double result = product + d;
return new TrigExpression(questionText, result);
}
private char getRandomOperation(String operations) {
return operations.charAt(random.nextInt(operations.length()));
}
private String buildSimpleExpression(List<Integer> operands, List<Character> 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(" ");
}
}
return sb.toString();
}
private String buildParenthesesExpression(List<Integer> operands, List<Character> 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(" ");
}
}
return sb.toString();
}
private int evaluateSequential(List<Integer> operands, List<Character> 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<Integer> operands, List<Character> operators, int parenPos) {
int parenResult = calculate(operands.get(parenPos), operands.get(parenPos + 1), operators.get(parenPos));
List<Integer> newOperands = new ArrayList<>();
List<Character> newOperators = new ArrayList<>();
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 < operators.size(); i++) {
if (i != parenPos) {
newOperators.add(operators.get(i));
}
}
return evaluateSequential(newOperands, newOperators);
}
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 int findPerfectSquare(int max) {
List<Integer> squares = new ArrayList<>();
for (int i = 1; i * i <= max; i++) {
squares.add(i * i);
}
return squares.get(random.nextInt(squares.size()));
}
private int findDivisor(int number) {
List<Integer> divisors = new ArrayList<>();
for (int i = 2; i <= Math.min(20, number); i++) {
if (number % i == 0) {
divisors.add(i);
}
}
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<Integer> usedValues = new HashSet<>();
String correctAnswer = String.valueOf(answer);
options[0] = correctAnswer;
usedValues.add(answer);
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;
}
} while (wrongAnswer == answer || usedValues.contains(wrongAnswer));
options[i] = String.valueOf(wrongAnswer);
usedValues.add(wrongAnswer);
}
shuffleArray(options);
int correctIndex = findCorrectIndex(options, correctAnswer);
return new Question(questionText, options, correctIndex, level);
}
private Question generateDoubleOptions(String questionText, double answer, String level) {
String[] options = new String[4];
Set<String> usedValues = new HashSet<>();
String correctAnswer = formatDouble(answer);
options[0] = correctAnswer;
usedValues.add(correctAnswer);
for (int i = 1; i < 4; i++) {
String 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)); // 确保错误答案不为负
attempts++;
if (attempts > 20) {
wrongAnswer = formatDouble(answer + (i + 1) * 2.5);
break;
}
} while (usedValues.contains(wrongAnswer) || wrongAnswer.equals(correctAnswer));
options[i] = 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) {
for (int i = 0; i < options.length; i++) {
if (options[i].equals(correctAnswer)) return i;
}
return 0;
}
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;
}
}
public interface QuestionGenerator {
List<Question> generateQuestions(String level, int count);
}

@ -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(
"<html><div style='text-align: center;'>"
+ currentQuestion.getQuestionText()
+ "</div></html>");
"<html><div style='text-align: center;'>"
+ currentQuestion.getQuestionText()
+ "</div></html>");
String[] options = currentQuestion.getOptions();
for (int i = 0; i < 4; i++) {
String optionText =
String.format(
"<html><div style='padding: 5px;'>%c. %s</div></html>", (char) ('A' + i), options[i]);
String.format(
"<html><div style='padding: 5px;'>%c. %s</div></html>", (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<Question> 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();
}
}
} // 添加这个闭合大括号

@ -13,10 +13,10 @@ public class ScoreFrame extends JFrame {
private LevelSelectionFrame parentFrame;
public ScoreFrame(
QuestionController questionController,
int[] userSelections,
List<Question> allQuestions,
LevelSelectionFrame parent) {
QuestionController questionController,
int[] userSelections,
List<Question> 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();
}
}
}
Loading…
Cancel
Save