diff --git a/ElementaryQuestionStrategy.java b/ElementaryQuestionStrategy.java new file mode 100644 index 0000000..120e106 --- /dev/null +++ b/ElementaryQuestionStrategy.java @@ -0,0 +1,151 @@ +import java.util.Random; + +public class ElementaryQuestionStrategy implements QuestionStrategy { + private final Random random = new Random(); + private final String[] operators = {"+", "-", "*", "/"}; + + @Override + public String generateQuestion() { + int operandsCount = random.nextInt(4) + 2; // 操作数 2~5 + StringBuilder sb = new StringBuilder(); + + // 生成运算符序列 + String[] chosenOps = generateOperators(operandsCount); + + // 随机决定括号 + int[] parentheses = decideParentheses(operandsCount, chosenOps); + + // 拼接表达式 + buildExpression(sb, operandsCount, chosenOps, parentheses); + + return sb.toString(); + } + + /** + * 生成运算符序列 + * 根据操作数数量,随机生成对应数量的运算符数组 + * + * @param operandsCount 操作数数量 + * @return 随机生成的运算符数组,长度为操作数数量-1 + */ + private String[] generateOperators(int operandsCount) { + String[] chosenOps = new String[operandsCount - 1]; + for (int i = 0; i < operandsCount - 1; i++) { + chosenOps[i] = operators[random.nextInt(operators.length)]; + } + return chosenOps; + } + + /** + * 决定是否需要括号,并返回括号的起始和结束位置 + * 对于3个及以上操作数,随机决定是否添加括号,并检查括号是否有效 + * + * @param operandsCount 操作数数量 + * @param chosenOps 运算符数组 + * @return 包含开括号和闭括号位置的数组,格式为[openParenIndex, closeParenIndex],-1表示不添加括号 + */ + private int[] decideParentheses(int operandsCount, String[] chosenOps) { + int openParenIndex = -1; + int closeParenIndex = -1; + + if (operandsCount > 2 && random.nextBoolean()) { + openParenIndex = random.nextInt(operandsCount - 1); + closeParenIndex = random.nextInt(operandsCount - openParenIndex - 1) + openParenIndex + 1; + + // 如果括号包裹整个表达式,则不需要括号 + if (openParenIndex == 0 && closeParenIndex == operandsCount - 1) { + openParenIndex = -1; + closeParenIndex = -1; + } else { + // 检查括号内的运算符优先级是否相同,如果相同则不需要括号 + boolean samePrecedence = checkPrecedenceEquality(chosenOps, openParenIndex, closeParenIndex); + if (samePrecedence) { + openParenIndex = -1; + closeParenIndex = -1; + } + } + } + return new int[]{openParenIndex, closeParenIndex}; + } + + /** + * 判断括号内的运算符优先级是否相同 + * 用于决定是否需要添加括号(如果优先级都相同,则括号是冗余的) + * + * @param chosenOps 运算符数组 + * @param openParenIndex 开括号位置 + * @param closeParenIndex 闭括号位置 + * @return 如果括号内所有运算符优先级相同则返回true,否则返回false + */ + private boolean checkPrecedenceEquality(String[] chosenOps, int openParenIndex, int closeParenIndex) { + int precedence = getPrecedence(chosenOps[openParenIndex]); + for (int i = openParenIndex; i < closeParenIndex; i++) { + if (getPrecedence(chosenOps[i]) != precedence) { + return false; + } + } + return true; + } + + /** + * 构建数学表达式 + * 根据操作数、运算符和括号位置,拼接完整的数学表达式 + * + * @param sb StringBuilder对象,用于构建表达式 + * @param operandsCount 操作数数量 + * @param chosenOps 运算符数组 + * @param parentheses 括号位置数组,格式为[openParenIndex, closeParenIndex] + */ + private void buildExpression(StringBuilder sb, int operandsCount, String[] chosenOps, int[] parentheses) { + int prevNum = random.nextInt(100) + 1; + int openParenIndex = parentheses[0]; + int closeParenIndex = parentheses[1]; + + // 处理第一个操作数可能的开括号 + if (openParenIndex == 0) sb.append("("); + sb.append(prevNum); + + // 处理最后一个操作数可能的闭括号 + if (closeParenIndex == operandsCount - 1) sb.append(")"); + + // 构建剩余的操作数和运算符 + for (int i = 1; i < operandsCount; i++) { + String op = chosenOps[i - 1]; + sb.append(" ").append(op).append(" "); + + // 根据运算符生成下一个操作数 + int num = generateOperand(op, prevNum); + + // 添加括号(如果需要) + if (i == openParenIndex) sb.append("("); + sb.append(num); + if (i == closeParenIndex) sb.append(")"); + + prevNum = num; // 更新上一个数字,供下一轮计算使用 + } + } + + /** + * 生成每个操作数 + * 确保生成的操作数符合题目要求,特别是减法不会导致负数结果 + * + * @param op 当前运算符 + * @param prevNum 前一个操作数的值 + * @return 生成的操作数 + */ + private int generateOperand(String op, int prevNum) { + int num = random.nextInt(100) + 1; // 默认生成1-100之间的随机数 + + // 如果是减法运算符,确保不会出现负数结果 + if (op.equals("-") && num > prevNum) { + num = random.nextInt(prevNum) + 1; // 保证 num <= prevNum,避免负数结果 + } + return num; + } + + private int getPrecedence(String op) { + if (op.equals("+") || op.equals("-")) return 1; + if (op.equals("*") || op.equals("/")) return 2; + return 0; + } +} diff --git a/FileUtils.java b/FileUtils.java new file mode 100644 index 0000000..a24ab81 --- /dev/null +++ b/FileUtils.java @@ -0,0 +1,64 @@ +import java.io.*; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * 文件工具类:生成文件名、保存题目、读取已有题目 + */ +public class FileUtils { + + // 生成文件名 + public static String generateFileName() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); + return sdf.format(new Date()) + ".txt"; + } + + // 保存题目到用户文件夹 + public static void saveQuestionsToFile(String userId, String fileName, List questions) { + File userFolder = new File(userId); + if (!userFolder.exists()) { + userFolder.mkdirs(); + } + + File file = new File(userFolder, fileName); + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + for (MathQuestion question : questions) { + writer.write("题号 " + question.getQuestionNumber() + ": " + question.getQuestionText()); + writer.newLine(); + writer.newLine(); + } + writer.flush(); + } catch (IOException e) { + throw new RuntimeException("写文件失败: " + file.getAbsolutePath(), e); + } + } + + // 读取用户文件夹下所有 txt 文件中已存在的题目 + public static Set loadExistingQuestions(String userId) { + Set existingQuestions = new HashSet<>(); + File userFolder = new File(userId); + if (!userFolder.exists() || !userFolder.isDirectory()) return existingQuestions; + + File[] files = userFolder.listFiles((dir, name) -> name != null && name.endsWith(".txt")); + if (files == null) return existingQuestions; + + for (File f : files) { + try (BufferedReader reader = new BufferedReader(new FileReader(f))) { + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) continue; + if (line.startsWith("题号")) { + int idx = line.indexOf(":"); + if (idx != -1 && idx + 1 < line.length()) { + existingQuestions.add(line.substring(idx + 1).trim()); + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return existingQuestions; + } +} diff --git a/HighSchoolQuestionStrategy.java b/HighSchoolQuestionStrategy.java new file mode 100644 index 0000000..85812b9 --- /dev/null +++ b/HighSchoolQuestionStrategy.java @@ -0,0 +1,42 @@ +import java.util.Random; + +public class HighSchoolQuestionStrategy implements QuestionStrategy { + private final Random random = new Random(); + private final String[] basicOps = {"+", "-", "*", "/"}; + private final String[] trigFuncs = {"sin", "cos", "tan"}; + + @Override + public String generateQuestion() { + // 随机操作数个数 2-5 + int operandsCount = random.nextInt(4) + 2; + StringBuilder sb = new StringBuilder(); + boolean hasTrig = false; + + for (int i = 0; i < operandsCount; i++) { + int num = random.nextInt(100) + 1; + + // 每个操作数有概率加三角函数 + if (random.nextBoolean()) { + String func = trigFuncs[random.nextInt(trigFuncs.length)]; + sb.append(func).append("(").append(num).append(")"); + hasTrig = true; + } else { + sb.append(num); + } + + if (i != operandsCount - 1) { + String op = basicOps[random.nextInt(basicOps.length)]; + sb.append(" ").append(op).append(" "); + } + } + + // 确保至少一个三角函数 + if (!hasTrig) { + String func = trigFuncs[random.nextInt(trigFuncs.length)]; + int num = random.nextInt(100) + 1; + return func + "(" + num + ") + " + sb.toString(); + } + + return sb.toString(); + } +} diff --git a/Main.java b/Main.java new file mode 100644 index 0000000..3a5deeb --- /dev/null +++ b/Main.java @@ -0,0 +1,100 @@ +import java.io.IOException; +import java.util.Scanner; + +/** + * 程序入口类,负责处理登录和题目生成。 + */ +public class Main { + public static void main(String[] args) throws IOException { + Scanner scanner = new Scanner(System.in); + User currentUser = null; + + // 登录流程 + while (true) { + while (currentUser == null) { + System.out.print("请输入用户名和密码,用空格分隔:"); + String username = scanner.next(); + String password = scanner.next(); + + currentUser = User.login(username, password); + + if (currentUser == null) { + System.out.println("请输入正确的用户名、密码"); + } else { + clearScreen(); //登录成功清屏 + System.out.println("当前选择为 " + currentUser.getRole() + " 出题"); + } + } + + // 每次登录后初始化当前出题类型(账号默认类型) + String currentRole = currentUser.getRole(); + + // 题目生成流程 + while (true) { + System.out.println("准备生成 " + currentRole + + " 数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):"); + + String input = scanner.next(); + + // 退出登录 + if (input.equals("-1")) { + currentUser = null; + clearScreen(); + System.out.println("已退出当前用户,重新登录..."); + break; + } + + // 检测切换命令 + if (input.startsWith("切换为")) { + String newRole = input.substring(3).trim(); + if (!newRole.equals("小学") && !newRole.equals("初中") && !newRole.equals("高中")) { + System.out.println("请输入小学、初中和高中三个选项中的一个"+"(当前类型为"+currentRole+")"); + continue; + } + currentRole = newRole; + clearScreen(); + System.out.println("系统提示:准备生成 " + currentRole + " 数学题目,请输入生成题目数量"); + continue; + } + + // 输入题目数量 + int questionCount; + try { + questionCount = Integer.parseInt(input); + } catch (NumberFormatException e) { + System.out.println("请输入有效的数字或使用“切换为小学/初中/高中”命令"+"(当前类型为"+currentRole+")"); + continue; + } + + if (questionCount < 10 || questionCount > 30) { + System.out.println("请输入有效的题目数量 (10-30) 或 -1 退出"); + continue; + } + + clearScreen(); + + // 把 currentRole 传给 QuestionGenerator + QuestionGenerator generator = new QuestionGenerator(currentUser, currentRole); + generator.generateQuestions(questionCount); + System.out.println("题目已生成并保存!\n"); + } + } + } + + /** + * 清屏方法 + */ + public static void clearScreen() { + try { + if (System.getProperty("os.name").contains("Windows")) { + new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor(); + } else { + new ProcessBuilder("clear").inheritIO().start().waitFor(); + } + } catch (Exception e) { + for (int i = 0; i < 50; i++) { + System.out.println(); + } + } + } +} diff --git a/MathQuestion.java b/MathQuestion.java new file mode 100644 index 0000000..c727b28 --- /dev/null +++ b/MathQuestion.java @@ -0,0 +1,21 @@ +/** + * 数学题目类,表示每一道数学题。 + */ +public class MathQuestion { + private int questionNumber; + private String questionText; + + // 构造方法 + public MathQuestion(int questionNumber, String questionText) { + this.questionNumber = questionNumber; + this.questionText = questionText; + } + + public int getQuestionNumber() { + return questionNumber; + } + + public String getQuestionText() { + return questionText; + } +} diff --git a/MiddleSchoolQuestionStrategy.java b/MiddleSchoolQuestionStrategy.java new file mode 100644 index 0000000..aa4c595 --- /dev/null +++ b/MiddleSchoolQuestionStrategy.java @@ -0,0 +1,47 @@ +import java.util.Random; + +public class MiddleSchoolQuestionStrategy implements QuestionStrategy { + private final Random random = new Random(); + private final String[] basicOps = {"+", "-", "*", "/"}; // 基本运算符 + + @Override + public String generateQuestion() { + // 随机操作数个数 2-5 + int operandsCount = random.nextInt(4) + 2; + StringBuilder sb = new StringBuilder(); + boolean hasSquareOrRoot = false; + + // 生成运算符和操作数 + for (int i = 0; i < operandsCount; i++) { + int num = random.nextInt(100) + 1; // 生成1-100之间的数字 + + // 每个操作数有概率平方或开根号 + if (random.nextBoolean()) { + if (random.nextBoolean()) { + sb.append("(").append(num).append(")^2"); + hasSquareOrRoot = true; // 标记是否已经使用了平方或根号 + } else { + // 确保根号下的数为正 + int rootNumber = random.nextInt(100) + 1; // 始终生成正整数 + sb.append("√(").append(rootNumber).append(")"); + hasSquareOrRoot = true; // 标记是否已经使用了平方或根号 + } + } else { + sb.append(num); // 普通数字 + } + + // 添加运算符(除最后一个操作数外) + if (i != operandsCount - 1) { + String op = basicOps[random.nextInt(basicOps.length)]; + sb.append(" ").append(op).append(" "); + } + } + // 如果没有平方或根号,强制添加一个 + if (!hasSquareOrRoot) { + // 确保根号下的数为正 + int rootNumber = random.nextInt(100) + 1; // 始终生成正整数 + sb.append(" + √(").append(rootNumber).append(")"); + } + return sb.toString(); + } +} diff --git a/QuestionGenerator.java b/QuestionGenerator.java new file mode 100644 index 0000000..bd56c29 --- /dev/null +++ b/QuestionGenerator.java @@ -0,0 +1,51 @@ +import java.io.IOException; +import java.util.*; + +/** + * 题目生成器类 + */ +public class QuestionGenerator { + private User user; + private String role; + + public QuestionGenerator(User user, String role) { + this.user = user; + this.role = role; + } + + public void generateQuestions(int questionCount) throws IOException { + // 获取策略 + QuestionStrategy strategy = QuestionStrategyFactory.getStrategy(role); + + // 读出该用户所有历史题目 + Set existingQuestions = FileUtils.loadExistingQuestions(user.getUsername()); + Set newQuestionsSet = new HashSet<>(); + List questions = new ArrayList<>(); + + System.out.println("以下为生成的题目列表:\n"); + + int i = 1; + int tryCount = 0; + while (questions.size() < questionCount) { + String questionText = strategy.generateQuestion(); + tryCount++; + if (existingQuestions.contains(questionText) || newQuestionsSet.contains(questionText)) { + // 如果题库太小可能死循环,这里设置最大尝试次数 + if (tryCount > questionCount * 100) { + System.out.println("题库不足,无法生成足够不重复的题目"); + break; + } + continue; + } + + newQuestionsSet.add(questionText); + MathQuestion question = new MathQuestion(i, questionText); + questions.add(question); + System.out.println("题号 " + i + ":" + questionText + "\n"); + i++; + } + + String fileName = FileUtils.generateFileName(); + FileUtils.saveQuestionsToFile(user.getUsername(), fileName, questions); + } +} diff --git a/QuestionRepository.java b/QuestionRepository.java new file mode 100644 index 0000000..d4fbc17 --- /dev/null +++ b/QuestionRepository.java @@ -0,0 +1,26 @@ +import java.util.HashSet; +import java.util.Set; + +/** + * 题目仓库类,负责管理已生成的题目,避免重复。 + */ +public class QuestionRepository { + private Set generatedQuestions = new HashSet<>(); + + /** + * 检查题目是否重复 + * @param question 题目 + * @return 是否重复 + */ + public boolean isDuplicate(String question) { + return generatedQuestions.contains(question); + } + + /** + * 将题目添加到仓库 + * @param question 题目 + */ + public void addQuestion(String question) { + generatedQuestions.add(question); + } +} diff --git a/QuestionStrategy.java b/QuestionStrategy.java new file mode 100644 index 0000000..079889b --- /dev/null +++ b/QuestionStrategy.java @@ -0,0 +1,6 @@ +/** + * 出题策略接口 + */ +public interface QuestionStrategy { + String generateQuestion(); +} diff --git a/QuestionStrategyFactory.java b/QuestionStrategyFactory.java new file mode 100644 index 0000000..e565ff5 --- /dev/null +++ b/QuestionStrategyFactory.java @@ -0,0 +1,17 @@ +/** + * 出题策略工厂类,根据role返回对应策略 + */ +public class QuestionStrategyFactory { + public static QuestionStrategy getStrategy(String role) { + switch (role) { + case "小学": + return new ElementaryQuestionStrategy(); + case "初中": + return new MiddleSchoolQuestionStrategy(); + case "高中": + return new HighSchoolQuestionStrategy(); + default: + throw new IllegalArgumentException("未知的角色: " + role); + } + } +} diff --git a/User.java b/User.java new file mode 100644 index 0000000..56f2691 --- /dev/null +++ b/User.java @@ -0,0 +1,52 @@ +/** + * 用户类,包含登录验证和角色管理。 + */ +public class User { + private String username; + private String password; + private String role; + + // 构造方法 + public User(String username, String password, String role) { + this.username = username; + this.password = password; + this.role = role; + } + + /** + * 登录验证 + * @param username 用户名 + * @param password 密码 + * @return 返回用户对象或null + */ + public static User login(String username, String password) { + String[][] users = { + {"张三1", "123", "小学"}, {"张三2", "123", "小学"}, {"张三3", "123", "小学"}, + {"李四1", "123", "初中"}, {"李四2", "123", "初中"}, {"李四3", "123", "初中"}, + {"王五1", "123", "高中"}, {"王五2", "123", "高中"}, {"王五3", "123", "高中"} + }; + + for (String[] user : users) { + if (user[0].equals(username) && user[1].equals(password)) { + return new User(username, password, user[2]); + } + } + return null; + } + + /** + * 获取用户角色 + * @return 用户角色 + */ + public String getRole() { + return this.role; + } + + /** + * 获取用户名 + * @return 用户名 + */ + public String getUsername() { + return this.username; + } +}