From 76095bfd75db777ebafeb0751305877d989d9a8b Mon Sep 17 00:00:00 2001 From: relentless <2464869638@qq.com> Date: Sun, 28 Sep 2025 19:58:45 +0800 Subject: [PATCH] first commit --- ElementaryQuestionStrategy.java | 151 ++++++++++++++++++++++++++++++ FileUtils.java | 64 +++++++++++++ HighSchoolQuestionStrategy.java | 42 +++++++++ Main.java | 100 ++++++++++++++++++++ MathQuestion.java | 21 +++++ MiddleSchoolQuestionStrategy.java | 47 ++++++++++ QuestionGenerator.java | 51 ++++++++++ QuestionRepository.java | 26 +++++ QuestionStrategy.java | 6 ++ QuestionStrategyFactory.java | 17 ++++ User.java | 52 ++++++++++ 11 files changed, 577 insertions(+) create mode 100644 ElementaryQuestionStrategy.java create mode 100644 FileUtils.java create mode 100644 HighSchoolQuestionStrategy.java create mode 100644 Main.java create mode 100644 MathQuestion.java create mode 100644 MiddleSchoolQuestionStrategy.java create mode 100644 QuestionGenerator.java create mode 100644 QuestionRepository.java create mode 100644 QuestionStrategy.java create mode 100644 QuestionStrategyFactory.java create mode 100644 User.java 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; + } +} -- 2.34.1