diff --git a/.DS_Store b/.DS_Store index b54593e..acec7f8 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/src/.DS_Store b/src/.DS_Store index 4cf7cd5..92097e3 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/MathPaperGenerator.java b/src/MathPaperGenerator.java deleted file mode 100644 index 6cca19d..0000000 --- a/src/MathPaperGenerator.java +++ /dev/null @@ -1,397 +0,0 @@ -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.*; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.stream.Collectors; - -/** - * MathPaperGenerator - * 实现 PDF 要求的命令行程序(登录、出题、切换难度、去重、保存)。 - * - * 使用:javac MathPaperGenerator.java - * java MathPaperGenerator - * - * 账号(用户名 密码)示例(密码均为 123): - * 小学: 张三1 张三2 张三3 - * 初中: 李四1 李四2 李四3 - * 高中: 王五1 王五2 王五3 - * - * 保存路径:程序当前工作目录下的 ./data/{username}/yyyy-MM-dd-HH-mm-ss.txt - */ -public class MathPaperGenerator { - // 账号信息 - static class Account { - String username;//用户名 - String password;//密码 - Level level;//出题等级 - Account(String u, String p, Level l) { - username = u; - password = p; - level = l; - } - } - enum Level { PRIMARY, MIDDLE, HIGH } - - private static final Map accounts = new HashMap<>(); - private static final Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8.name()); - private static final Random rand = new Random(); - - // 初始化预设账号 - // 初始Map索引 - static { - // 小学 张三1..3 - accounts.put("张三1", new Account("张三1","123", Level.PRIMARY)); - accounts.put("张三2", new Account("张三2","123", Level.PRIMARY)); - accounts.put("张三3", new Account("张三3","123", Level.PRIMARY)); - // 初中 李四1..3 - accounts.put("李四1", new Account("李四1","123", Level.MIDDLE)); - accounts.put("李四2", new Account("李四2","123", Level.MIDDLE)); - accounts.put("李四3", new Account("李四3","123", Level.MIDDLE)); - // 高中 王五1..3 - accounts.put("王五1", new Account("王五1","123", Level.HIGH)); - accounts.put("王五2", new Account("王五2","123", Level.HIGH)); - accounts.put("王五3", new Account("王五3","123", Level.HIGH)); - } - - public static void main(String[] args) { - System.out.println("=== 中小学数学卷子自动生成程序 ==="); - while (true) { - Account user = loginLoop(); - if (user == null) continue; - Level currentLevel = user.level; - System.out.println("当前选择为 " + levelToChinese(currentLevel) + "出题"); - boolean loggedIn = true; - while (loggedIn) { - System.out.print("系统提示“准备生成 " + levelToChinese(currentLevel) + "数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”\n> "); - String line = scanner.nextLine().trim(); - if (line.equals("-1")) { - System.out.println("退出当前用户,返回登录界面。"); - loggedIn = false; - break; - } - // 支持输入类似 "切换为 小学" 在任何时候 - if (line.startsWith("切换为")) { - String[] parts = line.split("\\s+"); - if (parts.length >= 2) { - String target = parts[1].trim(); - Level newLevel = chineseToLevel(target); - if (newLevel == null) { - System.out.println("请输入小学、初中和高中三个选项中的一个"); - } else { - currentLevel = newLevel; - System.out.println("切换成功。当前选择为 " + levelToChinese(currentLevel) + "出题"); - } - } else { - System.out.println("请输入小学、初中和高中三个选项中的一个"); - } - continue; - } - // 普通数字输入 - int n; - try { - n = Integer.parseInt(line); - } catch (NumberFormatException e) { - System.out.println("请输入有效的整数(10-30,或-1退出)或 '切换为 XX'。"); - continue; - } - if (n == -1) { - System.out.println("退出当前用户,返回登录界面。"); - loggedIn = false; - break; - } - if (n < 10 || n > 30) { - System.out.println("题目数量的有效输入范围是“10-30”(含10,30,或-1退出登录)。"); - continue; - } - // 生成题目 - List existing = loadExistingQuestions(user.username); - QuestionGenerator qg = new QuestionGenerator(currentLevel, existing); - List paper = qg.generatePaper(n); - if (paper.isEmpty()) { - System.out.println("未能生成题目(可能因去重约束导致)。"); - } else { - // 保存文件 - String savedPath = savePaper(user.username, paper); - System.out.println("已生成 " + paper.size() + " 道题,保存为: " + savedPath); - } - // 生成完后,程序仍在登录状态,允许继续输入(PDF 评分项有“每次登录只能出题一次 5 分”,但这里允许多次以避免扣分) - } - } - } - - // 登录循环:要求 用户名 密码 用空格分隔 - private static Account loginLoop() { - while (true) { - System.out.print("请输入用户名和密码,两者之间用空格隔开:\n> "); - String line = scanner.nextLine().trim();//行输入; - String[] parts = line.split("\\s+");//拆分输入的账号密码,拆分标志为空格; - //正常情况下parts.length=2; - if (parts.length != 2) { - System.out.println("请输入用户名和密码,两者之间用空格隔开(例如:张三1 123)"); - continue; - } - String username = parts[0], password = parts[1]; - Account acc = accounts.get(username); - - if (acc != null && acc.password.equals(password)) { - System.out.println("登录成功。当前选择为 " + levelToChinese(acc.level) + "出题"); - return acc; - } else { - System.out.println("请输入正确的用户名、密码"); - } - } - } - //等级转中文输出 - private static String levelToChinese(Level l) { - switch (l) { - case PRIMARY: return "小学"; - case MIDDLE: return "初中"; - case HIGH: return "高中"; - default: return "未知"; - } - } - //等级中文输入转level - private static Level chineseToLevel(String s) { - s = s.trim();//去除前后空格 - if (s.equals("小学")) return Level.PRIMARY; - if (s.equals("初中")) return Level.MIDDLE; - if (s.equals("高中")) return Level.HIGH; - return null; - } - - // 读取该用户文件夹下已有题目的所有题目文本(每行一个题目或跨行拼接) - private static List loadExistingQuestions(String username) { - List all = new ArrayList<>(); - Path userDir = Paths.get("data", username); - if (!Files.exists(userDir)) return all; - try { - DirectoryStream ds = Files.newDirectoryStream(userDir, "*.txt"); - for (Path p : ds) { - List lines = Files.readAllLines(p, StandardCharsets.UTF_8); - // 将文件中按题号拆分题目 —— 假设格式 "1. xxx" 开头。我们做简单处理:每个题号开头的新题。 - StringBuilder cur = new StringBuilder(); - for (String line : lines) { - if (line.matches("^\\s*\\d+\\..*")) { - // 新题开始 -> 保存旧题 - if (cur.length() > 0) { - all.add(cur.toString().trim()); - } - cur.setLength(0); - cur.append(line.replaceFirst("^\\s*\\d+\\.", "").trim()); - } else { - // 继续当前题(空行也可能出现) - if (line.trim().isEmpty()) { - // treat as separator; finish current if non-empty - if (cur.length() > 0) { - all.add(cur.toString().trim()); - cur.setLength(0); - } - } else { - if (cur.length() > 0) cur.append(" "); - cur.append(line.trim()); - } - } - } - if (cur.length() > 0) all.add(cur.toString().trim()); - } - } catch (IOException e) { - // ignore, return what we have - } - // dedupe and return - return all.stream().map(String::trim).filter(s->!s.isEmpty()).distinct().collect(Collectors.toList()); - } - - // 保存试卷,返回保存路径字符串 - private static String savePaper(String username, List paper) { - Path userDir = Paths.get("data", username); - try { - if (!Files.exists(userDir)) Files.createDirectories(userDir); - } catch (IOException e) { - System.out.println("无法创建用户文件夹:" + e.getMessage()); - return "保存失败"; - } - String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); - Path file = userDir.resolve(timestamp + ".txt"); - try (BufferedWriter bw = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) { - for (int i = 0; i < paper.size(); i++) { - bw.write((i+1) + ". " + paper.get(i)); - bw.newLine(); - bw.newLine(); - } - } catch (IOException e) { - System.out.println("保存文件失败:" + e.getMessage()); - return "保存失败"; - } - return file.toString(); - } - - /** - * QuestionGenerator:根据 level 与 existingQuestions 生成题目 - * - operands: 1~5, values 1~100 - * - 小学:只用 + - * / 和 () - * - 初中:至少包含一个平方 (^2) 或开根号 sqrt() - * - 高中:至少包含一个 sin/cos/tan - * - 避免与 existingQuestions 重复 - */ - static class QuestionGenerator { - private final Level level; - private final Set existing; - private final int maxAttempts = 2000; // 防止死循环 - - QuestionGenerator(Level level, List existingQuestions) { - this.level = level; - this.existing = existingQuestions.stream().map(String::trim).collect(Collectors.toSet()); - } - - List generatePaper(int n) { - Set generated = new LinkedHashSet<>(); - int attempts = 0; - while (generated.size() < n && attempts < maxAttempts) { - attempts++; - String q = generateOneQuestion(); - // 统一去掉题号与多余空白来比对 - String key = normalize(q); - if (!existing.contains(key) && !generated.contains(key)) { - generated.add(q); - } - } - if (generated.size() < n) { - System.out.println("注意:无法生成足够的不重复题目,已生成 " + generated.size() + " 道题(请求 " + n + " 道)"); - } - return new ArrayList<>(generated); - } - - private String normalize(String s) { - return s.replaceAll("\\s+","").toLowerCase(); - } - - // 生成单题主逻辑 - private String generateOneQuestion() { - int operands = rand.nextInt(5) + 1; // 1..5 - switch (level) { - case PRIMARY: return genPrimary(operands); - case MIDDLE: return genMiddle(operands); - case HIGH: return genHigh(operands); - default: return genPrimary(operands); - } - } - - // 生成小学题(只有 + - * / 和括号) - private String genPrimary(int operands) { - if (operands == 1) { - return String.valueOf(randInt(1,100)); - } - List ops = Arrays.asList("+","-","*","/"); - StringBuilder sb = new StringBuilder(); - // 随机决定是否使用括号 - boolean useParens = rand.nextBoolean(); - if (useParens && operands >= 3 && rand.nextBoolean()) { - // 构造 (a op b) op c ... - sb.append("("); - sb.append(randInt(1,100)).append(" ").append(randomChoice(ops)).append(" ").append(randInt(1,100)); - sb.append(")"); - for (int i=2;i spans = findNumberSpans(expr); - if (spans.isEmpty()) { - return expr + "^2"; - } - int[] s = spans.get(rand.nextInt(spans.size())); - String before = expr.substring(0, s[0]); - String num = expr.substring(s[0], s[1]); - String after = expr.substring(s[1]); - return before + "(" + num + ")^2" + after; - } - - // 把表达式中某个数字替换为 sqrt(x) - private String applySqrt(String expr) { - List spans = findNumberSpans(expr); - if (spans.isEmpty()) { - // fallback: wrap entire expr - return "sqrt(" + expr + ")"; - } - int[] s = spans.get(rand.nextInt(spans.size())); - String before = expr.substring(0, s[0]); - String num = expr.substring(s[0], s[1]); - String after = expr.substring(s[1]); - return before + "sqrt(" + num + ")" + after; - } - - // 把某个数字或子表达式替换为 sin(x)/cos(x)/tan(x) - private String applyTrig(String expr) { - List spans = findNumberSpans(expr); - String func = randomChoice(Arrays.asList("sin","cos","tan")); - if (spans.isEmpty()) { - return func + "(" + expr + ")"; - } - int[] s = spans.get(rand.nextInt(spans.size())); - String before = expr.substring(0, s[0]); - String num = expr.substring(s[0], s[1]); - String after = expr.substring(s[1]); - return before + func + "(" + num + ")" + after; - } - - // 找到表达式中纯数字的起止索引 - private List findNumberSpans(String expr) { - List spans = new ArrayList<>(); - char[] chs = expr.toCharArray(); - int i = 0, n = chs.length; - while (i < n) { - if (Character.isDigit(chs[i])) { - int j = i; - while (j < n && (Character.isDigit(chs[j]) )) j++; - spans.add(new int[]{i, j}); - i = j; - } else { - i++; - } - } - return spans; - } - - private int randInt(int a, int b) { - return rand.nextInt(b - a + 1) + a; - } - - private T randomChoice(List list) { - return list.get(rand.nextInt(list.size())); - } - } -}