初始化仓库

pull/2/head
宋奇峰 5 months ago
parent 6ac35a13fa
commit f1cff9bccf

BIN
.DS_Store vendored

Binary file not shown.

BIN
src/.DS_Store vendored

Binary file not shown.

@ -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<String, Account> 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<String> existing = loadExistingQuestions(user.username);
QuestionGenerator qg = new QuestionGenerator(currentLevel, existing);
List<String> 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<String> loadExistingQuestions(String username) {
List<String> all = new ArrayList<>();
Path userDir = Paths.get("data", username);
if (!Files.exists(userDir)) return all;
try {
DirectoryStream<Path> ds = Files.newDirectoryStream(userDir, "*.txt");
for (Path p : ds) {
List<String> 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<String> 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<String> existing;
private final int maxAttempts = 2000; // 防止死循环
QuestionGenerator(Level level, List<String> existingQuestions) {
this.level = level;
this.existing = existingQuestions.stream().map(String::trim).collect(Collectors.toSet());
}
List<String> generatePaper(int n) {
Set<String> 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<String> 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<operands;i++) {
sb.append(" ").append(randomChoice(ops)).append(" ").append(randInt(1,100));
}
} else {
// 直连
sb.append(randInt(1,100));
for (int i=1;i<operands;i++) {
sb.append(" ").append(randomChoice(ops)).append(" ").append(randInt(1,100));
}
}
return sb.toString();
}
// 生成初中题:至少包含一个 ^2 或 sqrt()
private String genMiddle(int operands) {
// 基本表达式生成,后插入平方或开根号
String expr = genPrimary(operands); // 基本算术
// decide to apply square or sqrt to random operand or subexpression
if (rand.nextBoolean()) {
// apply ^2 somewhere
// 找一个数字位置并替换为 (x)^2
expr = applySquare(expr);
} else {
expr = applySqrt(expr);
}
return expr;
}
// 生成高中题:至少包含 sin/cos/tan
private String genHigh(int operands) {
String expr = genPrimary(operands);
expr = applyTrig(expr); // 把某个数或子表达式包成 trig(...)
return expr;
}
// 把表达式中某个数字替换为 (x)^2
private String applySquare(String expr) {
// 寻找所有数字的片段
List<int[]> 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<int[]> 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<int[]> 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<int[]> findNumberSpans(String expr) {
List<int[]> 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> T randomChoice(List<T> list) {
return list.get(rand.nextInt(list.size()));
}
}
}
Loading…
Cancel
Save