From 2c5ac1e9415858373eee541221258aef029027d9 Mon Sep 17 00:00:00 2001 From: relentless <2464869638@qq.com> Date: Sun, 28 Sep 2025 20:10:41 +0800 Subject: [PATCH 1/3] first commit --- README.md | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index d67864c..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# relentless - -- 2.34.1 From 12a3323c625d06327a4216ec6a59309e36335850 Mon Sep 17 00:00:00 2001 From: relentless <2464869638@qq.com> Date: Sun, 28 Sep 2025 20:15:05 +0800 Subject: [PATCH 2/3] 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 deletions(-) delete mode 100644 ElementaryQuestionStrategy.java delete mode 100644 FileUtils.java delete mode 100644 HighSchoolQuestionStrategy.java delete mode 100644 Main.java delete mode 100644 MathQuestion.java delete mode 100644 MiddleSchoolQuestionStrategy.java delete mode 100644 QuestionGenerator.java delete mode 100644 QuestionRepository.java delete mode 100644 QuestionStrategy.java delete mode 100644 QuestionStrategyFactory.java delete mode 100644 User.java diff --git a/ElementaryQuestionStrategy.java b/ElementaryQuestionStrategy.java deleted file mode 100644 index 120e106..0000000 --- a/ElementaryQuestionStrategy.java +++ /dev/null @@ -1,151 +0,0 @@ -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 deleted file mode 100644 index a24ab81..0000000 --- a/FileUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -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 deleted file mode 100644 index 85812b9..0000000 --- a/HighSchoolQuestionStrategy.java +++ /dev/null @@ -1,42 +0,0 @@ -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 deleted file mode 100644 index 3a5deeb..0000000 --- a/Main.java +++ /dev/null @@ -1,100 +0,0 @@ -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 deleted file mode 100644 index c727b28..0000000 --- a/MathQuestion.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 数学题目类,表示每一道数学题。 - */ -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 deleted file mode 100644 index aa4c595..0000000 --- a/MiddleSchoolQuestionStrategy.java +++ /dev/null @@ -1,47 +0,0 @@ -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 deleted file mode 100644 index bd56c29..0000000 --- a/QuestionGenerator.java +++ /dev/null @@ -1,51 +0,0 @@ -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 deleted file mode 100644 index d4fbc17..0000000 --- a/QuestionRepository.java +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index 079889b..0000000 --- a/QuestionStrategy.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 出题策略接口 - */ -public interface QuestionStrategy { - String generateQuestion(); -} diff --git a/QuestionStrategyFactory.java b/QuestionStrategyFactory.java deleted file mode 100644 index e565ff5..0000000 --- a/QuestionStrategyFactory.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * 出题策略工厂类,根据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 deleted file mode 100644 index 56f2691..0000000 --- a/User.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * 用户类,包含登录验证和角色管理。 - */ -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 From 548102396bd34955d01320becfe8ea702af22994 Mon Sep 17 00:00:00 2001 From: relentless <2464869638@qq.com> Date: Sun, 28 Sep 2025 20:16:22 +0800 Subject: [PATCH 3/3] first commit --- doc/README.md | 111 +++++++++++++++++++ src/ElementaryQuestionStrategy.java | 151 ++++++++++++++++++++++++++ src/FileUtils.java | 64 +++++++++++ src/HighSchoolQuestionStrategy.java | 42 +++++++ src/Main.java | 100 +++++++++++++++++ src/MathQuestion.java | 21 ++++ src/MiddleSchoolQuestionStrategy.java | 47 ++++++++ src/QuestionGenerator.java | 51 +++++++++ src/QuestionRepository.java | 26 +++++ src/QuestionStrategy.java | 6 + src/QuestionStrategyFactory.java | 17 +++ src/User.java | 52 +++++++++ 12 files changed, 688 insertions(+) create mode 100644 doc/README.md create mode 100644 src/ElementaryQuestionStrategy.java create mode 100644 src/FileUtils.java create mode 100644 src/HighSchoolQuestionStrategy.java create mode 100644 src/Main.java create mode 100644 src/MathQuestion.java create mode 100644 src/MiddleSchoolQuestionStrategy.java create mode 100644 src/QuestionGenerator.java create mode 100644 src/QuestionRepository.java create mode 100644 src/QuestionStrategy.java create mode 100644 src/QuestionStrategyFactory.java create mode 100644 src/User.java diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..3360855 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,111 @@ +# 中小学数学卷子自动生成程序 + +## 项目简介 +这是一个命令行界面的数学题目自动生成程序,专为小学、初中和高中数学老师设计。程序可以根据不同难度级别(小学、初中、高中)自动生成符合相应教学要求的数学题目,并支持题目查重功能,确保每位老师生成的题目不会重复。 + +## 功能特点 + +### 1. 用户登录验证 +- 支持小学、初中和高中三个级别的用户账号 +- 每个级别预设3个账号,密码统一为123 +- 登录成功后显示当前选择的出题级别 + +### 2. 题目生成功能 +- 根据账号类型自动选择对应的难度级别生成题目 +- 支持自定义生成题目数量(范围:10-30题) +- 操作数范围:1-100 +- 操作数个数:2-5个 + +### 3. 多难度级别支持 +- **小学**:仅包含加减乘除四则运算和括号 +- **初中**:在小学基础上增加平方和开根号运算,且每道题至少包含一个平方或开根号 +- **高中**:在初中基础上增加三角函数(sin、cos、tan),且每道题至少包含一个三角函数 + +### 4. 题目类型切换 +- 支持在登录状态下切换不同难度级别的题目生成 +- 输入格式:`切换为 小学/初中/高中` + +### 5. 题目查重功能 +- 自动检测新生成的题目是否与当前用户历史生成的题目重复 +- 确保每个用户生成的题目都是唯一的 + +### 6. 文件保存功能 +- 题目以文本文件形式保存 +- 文件名格式:`年-月-日-时-分-秒.txt` +- 每个用户有独立的文件夹存储题目 +- 题目包含题号,每题之间空一行 + +## 使用说明 + +### 登录 +1. 运行程序后,输入用户名和密码(用空格分隔) +2. 验证成功后,系统提示当前选择的出题级别 +3. 验证失败则提示重新输入 + +### 生成题目 +1. 登录成功后,系统提示输入题目数量 +2. 输入10-30之间的数字生成对应数量的题目 +3. 输入-1退出当前用户,返回登录界面 + +### 切换难度 +- 在任何提示输入题目数量的界面,输入`切换为 小学/初中/高中`即可切换题目难度 +- 切换成功后,系统会提示新的难度级别 + +## 账号列表 + +| 级别 | 用户名 | 密码 | +|------|--------|------| +| 小学 | 张三1 | 123 | +| 小学 | 张三2 | 123 | +| 小学 | 张三3 | 123 | +| 初中 | 李四1 | 123 | +| 初中 | 李四2 | 123 | +| 初中 | 李四3 | 123 | +| 高中 | 王五1 | 123 | +| 高中 | 王五2 | 123 | +| 高中 | 王五3 | 123 | + +## 项目结构 +``` +quesproject/ +├── src/ # 源代码目录 +│ ├── Main.java # 程序入口类 +│ ├── User.java # 用户类 +│ ├── MathQuestion.java # 数学题目类 +│ ├── QuestionGenerator.java # 题目生成器 +│ ├── QuestionStrategy.java # 题目策略接口 +│ ├── QuestionStrategyFactory.java # 策略工厂类 +│ ├── ElementaryQuestionStrategy.java # 小学题目策略 +│ ├── MiddleSchoolQuestionStrategy.java # 初中题目策略 +│ ├── HighSchoolQuestionStrategy.java # 高中题目策略 +│ └── FileUtils.java # 文件操作工具类 +├── doc/ # 文档目录 +│ └── README.md # 项目说明文档 +├── [用户名]/ # 用户题目文件夹(运行时自动创建) +``` + +## 技术实现 + +### 设计模式 +- **工厂模式**:通过QuestionStrategyFactory创建不同级别的题目生成策略 +- **策略模式**:通过QuestionStrategy接口及其实现类实现不同难度级别的题目生成 + +### 核心功能实现 +1. **用户验证**:通过预设的账号列表进行验证 +2. **题目生成**:根据不同难度级别使用不同的策略生成题目 +3. **查重功能**:读取用户历史题目文件,确保新生成的题目不重复 +4. **文件管理**:自动创建用户文件夹,按指定格式保存题目文件 + +## 运行环境 +- Java 8及以上版本 +- 支持Windows、Linux、macOS等操作系统 + +## 编译运行 +1. 编译:`javac -d out src/*.java` +2. 运行:`java -cp out Main` + +## 注意事项 +1. 题目数量必须在10-30之间,否则会提示重新输入 +2. 切换难度时,请确保输入格式正确:`切换为 级别名称` +3. 程序会在当前目录下创建用户文件夹存储题目文件 +4. 为保证查重功能正常,请勿手动修改用户文件夹中的题目文件格式 \ No newline at end of file diff --git a/src/ElementaryQuestionStrategy.java b/src/ElementaryQuestionStrategy.java new file mode 100644 index 0000000..120e106 --- /dev/null +++ b/src/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/src/FileUtils.java b/src/FileUtils.java new file mode 100644 index 0000000..a24ab81 --- /dev/null +++ b/src/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/src/HighSchoolQuestionStrategy.java b/src/HighSchoolQuestionStrategy.java new file mode 100644 index 0000000..85812b9 --- /dev/null +++ b/src/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/src/Main.java b/src/Main.java new file mode 100644 index 0000000..3a5deeb --- /dev/null +++ b/src/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/src/MathQuestion.java b/src/MathQuestion.java new file mode 100644 index 0000000..c727b28 --- /dev/null +++ b/src/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/src/MiddleSchoolQuestionStrategy.java b/src/MiddleSchoolQuestionStrategy.java new file mode 100644 index 0000000..aa4c595 --- /dev/null +++ b/src/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/src/QuestionGenerator.java b/src/QuestionGenerator.java new file mode 100644 index 0000000..bd56c29 --- /dev/null +++ b/src/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/src/QuestionRepository.java b/src/QuestionRepository.java new file mode 100644 index 0000000..d4fbc17 --- /dev/null +++ b/src/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/src/QuestionStrategy.java b/src/QuestionStrategy.java new file mode 100644 index 0000000..079889b --- /dev/null +++ b/src/QuestionStrategy.java @@ -0,0 +1,6 @@ +/** + * 出题策略接口 + */ +public interface QuestionStrategy { + String generateQuestion(); +} diff --git a/src/QuestionStrategyFactory.java b/src/QuestionStrategyFactory.java new file mode 100644 index 0000000..e565ff5 --- /dev/null +++ b/src/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/src/User.java b/src/User.java new file mode 100644 index 0000000..56f2691 --- /dev/null +++ b/src/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