1 #1

Merged
hnu202326010410 merged 3 commits from develop into main 5 months ago

@ -0,0 +1,53 @@
import java.util.HashMap;
import java.util.Map;
/**
* .
*/
public final class AccountManager {
private final Map<String, String> userToPassword = new HashMap<>();
private final Map<String, String> userToLevel = new HashMap<>();
/**
* .
*/
public AccountManager() {
register("张三1", "123", "小学");
register("张三2", "123", "小学");
register("张三3", "123", "小学");
register("李四1", "123", "初中");
register("李四2", "123", "初中");
register("李四3", "123", "初中");
register("王五1", "123", "高中");
register("王五2", "123", "高中");
register("王五3", "123", "高中");
}
private void register(String user, String pwd, String level) {
userToPassword.put(user, pwd);
userToLevel.put(user, level);
}
/**
* .
*/
public boolean validate(String user, String pwd) {
return user != null && pwd != null && pwd.equals(userToPassword.get(user));
}
/**
* .
*/
public String getLevel(String user) {
return userToLevel.get(user);
}
}

@ -0,0 +1,79 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
/**
* . {@link #generateQuestion()} .
*/
public abstract class BaseQuestionGenerator {
/**
* .
*/
protected final Random random = new Random();
/**
* .
*
* @param count .
* @param existingQuestions .
* @return .
*/
public List<String> generate(int count, Set<String> existingQuestions) {
List<String> result = new ArrayList<>();
Set<String> seen = new HashSet<>(existingQuestions == null ? 0 : existingQuestions.size());
if (existingQuestions != null) {
seen.addAll(existingQuestions);
}
int attempts = 0;
while (result.size() < count && attempts < count * 20) {
String question = generateQuestion();
if (!seen.contains(question)) {
result.add(question);
seen.add(question);
}
attempts++;
}
return result;
}
/**
* .
*
* @param a .
* @param b .
* @return a b .
*/
protected int randInt(int a, int b) {
return a + random.nextInt(b - a + 1);
}
/**
* : "+", "-", "*", "/".
*
* @return .
*/
protected String randomBasicOperator() {
return switch (random.nextInt(4)) {
case 0 -> "+";
case 1 -> "-";
case 2 -> "*";
default -> "/";
};
}
/**
* . .
*
* @return .
*/
protected abstract String generateQuestion();
}

@ -0,0 +1,16 @@
/** 常量定义. */
public final class Constants {
public static final String BASE_OUTPUT_DIR = "output"; // 主输出文件夹
public static final int MIN_QUESTIONS = 10;
public static final int MAX_QUESTIONS = 30;
public static final int MIN_OPERANDS = 1;
public static final int MAX_OPERANDS = 5;
public static final int MIN_VALUE = 1;
public static final int MAX_VALUE = 100;
private Constants() {
// no instances
}
}

@ -0,0 +1,99 @@
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* . {@code Constants.BASE_OUTPUT_DIR}
* {@code yyyy-MM-dd-HH-mm-ss.txt} .
*/
public final class FileManager {
/**
* .
*/
private final Path baseDir;
/**
* . {@link Constants#BASE_OUTPUT_DIR} .
*
* @throws RuntimeException .
*/
public FileManager() {
this.baseDir = Paths.get(Constants.BASE_OUTPUT_DIR);
try {
Files.createDirectories(baseDir);
} catch (IOException e) {
throw new RuntimeException("无法创建输出目录: " + baseDir, e);
}
}
/**
* . <code>.txt</code>
* "第x题:".
*
* @param username
* @return .
*/
public Set<String> loadExistingQuestions(String username) {
Path userDir = baseDir.resolve(username);
Set<String> existing = new HashSet<>();
if (!Files.exists(userDir)) {
return existing;
}
try {
Files.list(userDir)
.filter(p -> p.toString().endsWith(".txt"))
.forEach(p -> {
try {
List<String> lines = Files.readAllLines(p, StandardCharsets.UTF_8);
for (String line : lines) {
String t = line.trim();
// 只保留题目正文
if (!t.isEmpty() && !t.matches("^第\\d+题.*$")) {
existing.add(t);
}
}
} catch (IOException ignore) {
// 忽略单个文件失败
}
});
} catch (IOException ignore) {
// 遍历目录失败时忽略
}
return existing;
}
/**
* . BASE_OUTPUT_DIR//yyyy-MM-dd-HH-mm-ss.txt n:
* .
*
* @param username
* @param questions
* @return {@link Path}
* @throws IOException .
*/
public Path saveExam(String username, List<String> questions) throws IOException {
Path userDir = baseDir.resolve(username);
Files.createDirectories(userDir);
String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + ".txt";
Path out = userDir.resolve(name);
try (BufferedWriter w = Files.newBufferedWriter(out, StandardCharsets.UTF_8)) {
for (int i = 0; i < questions.size(); i++) {
w.write(String.format("第%d题: %s", i + 1, questions.get(i)));
w.newLine();
w.newLine();
}
}
return out;
}
}

@ -0,0 +1,47 @@
import java.util.ArrayList;
import java.util.List;
/**
* .
*/
class HighQuestionGenerator extends BaseQuestionGenerator {
@Override
protected String generateQuestion() {
int ops = randInt(Constants.MIN_OPERANDS, Constants.MAX_OPERANDS);
List<String> tokens = new ArrayList<>();
boolean hasTrig = false;
for (int i = 0; i < ops; i++) {
if (!hasTrig && random.nextDouble() < 0.5) {
tokens.add(randomTrig());
hasTrig = true;
} else if (random.nextDouble() < 0.2) {
tokens.add("(" + randInt(Constants.MIN_VALUE, Constants.MAX_VALUE) + "^2)");
} else {
tokens.add(String.valueOf(randInt(Constants.MIN_VALUE, Constants.MAX_VALUE)));
}
if (i < ops - 1) {
tokens.add(randomBasicOperator());
}
}
if (!hasTrig) {
int pos = Math.max(0, random.nextInt(tokens.size() / 2 + 1) * 2);
tokens.add(pos, randomTrig());
if (pos + 1 < tokens.size()) {
tokens.add(pos + 1, "+");
}
}
return String.join(" ", tokens);
}
private String randomTrig() {
return switch (random.nextInt(3)) {
case 0 -> "sin(" + randInt(0, 90) + ")";
case 1 -> "cos(" + randInt(0, 90) + ")";
default -> "tan(" + randInt(0, 60) + ")";
};
}
}

@ -0,0 +1,155 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
/**
* .
*/
public final class MathExamGenerator {
private final AccountManager accountManager = new AccountManager();
private final FileManager fileManager = new FileManager();
/**
* .
*
* @param unused 使
*/
public static void main(String[] unused) {
new MathExamGenerator().run();
}
/**
* .
*/
private void run() {
try (Scanner scanner = new Scanner(System.in)) {
while (true) {
String currentUser = login(scanner);
String currentLevel = accountManager.getLevel(currentUser);
while (true) {
System.out.printf(
"准备生成 %s 数学题目,请输入生成题目数量(输入-1将退出当前用户重新登录",
currentLevel);
String line = scanner.nextLine().trim();
if ("-1".equals(line)) {
break;
}
if (line.startsWith("切换为")) {
currentLevel = handleLevelSwitch(line, currentLevel);
continue;
}
handleQuestionGeneration(currentUser, currentLevel, line);
}
}
}
}
/**
* .
*
* @param scanner .
* @return .
*/
private String login(Scanner scanner) {
while (true) {
System.out.print("请输入用户名 密码(用空格分隔):");
String line = scanner.nextLine().trim();
if (line.isEmpty()) {
continue;
}
String[] parts = line.split("\\s+");
if (parts.length < 2) {
System.out.println("请输入正确的用户名、密码");
continue;
}
if (accountManager.validate(parts[0], parts[1])) {
String username = parts[0];
System.out.println("当前选择为 " + accountManager.getLevel(username) + " 出题");
return username;
} else {
System.out.println("用户名或密码错误,请重新输入");
}
}
}
/**
* .
*
* @param line
* @param currentLevel
* @return
*/
private String handleLevelSwitch(String line, String currentLevel) {
String opt = line.replaceFirst("切换为", "").trim();
if (!"小学".equals(opt) && !"初中".equals(opt) && !"高中".equals(opt)) {
System.out.println("请输入小学、初中或高中三个选项中的一个");
return currentLevel;
}
System.out.println("当前选择为 " + opt + " 出题");
return opt;
}
/**
* .
*
* @param username
* @param level
* @param line
*/
private void handleQuestionGeneration(String username, String level, String line) {
int num;
try {
num = Integer.parseInt(line);
} catch (NumberFormatException e) {
System.out.println("请输入有效的整数数量或-1退出");
return;
}
if (num < Constants.MIN_QUESTIONS || num > Constants.MAX_QUESTIONS) {
System.out.printf(
"题目数量的有效输入范围是 %d-%d%n", Constants.MIN_QUESTIONS, Constants.MAX_QUESTIONS);
return;
}
BaseQuestionGenerator generator = getGenerator(level);
Set<String> existing = fileManager.loadExistingQuestions(username);
List<String> questions = generator.generate(num, existing);
if (questions.size() < num) {
System.out.println("无法生成足够不重复的题目,请删除部分历史题目或减少生成数量");
return;
}
try {
Path path = fileManager.saveExam(username, questions);
// 转为 String 打印相对路径
System.out.println("试卷已保存:" + path);
} catch (IOException e) {
System.out.println("保存文件时出错:" + e.getMessage());
}
}
/**
* .
*
* @param level
* @return BaseQuestionGenerator
*/
private BaseQuestionGenerator getGenerator(String level) {
return switch (level) {
case "小学" -> new PrimaryQuestionGenerator();
case "初中" -> new MiddleQuestionGenerator();
case "高中" -> new HighQuestionGenerator();
default -> new PrimaryQuestionGenerator();
};
}
}

@ -0,0 +1,41 @@
import java.util.ArrayList;
import java.util.List;
/**
* .
*/
class MiddleQuestionGenerator extends BaseQuestionGenerator {
@Override
protected String generateQuestion() {
int ops = randInt(Constants.MIN_OPERANDS, Constants.MAX_OPERANDS);
List<String> tokens = new ArrayList<>();
boolean hasPowerOrSqrt = false;
for (int i = 0; i < ops; i++) {
if (!hasPowerOrSqrt && random.nextDouble() < 0.4) {
if (random.nextBoolean()) {
tokens.add("(" + randInt(Constants.MIN_VALUE, Constants.MAX_VALUE) + "^2)");
} else {
tokens.add("sqrt(" + randInt(Constants.MIN_VALUE, Constants.MAX_VALUE) + ")");
}
hasPowerOrSqrt = true;
} else {
tokens.add(String.valueOf(randInt(Constants.MIN_VALUE, Constants.MAX_VALUE)));
}
if (i < ops - 1) {
tokens.add(randomBasicOperator());
}
}
if (!hasPowerOrSqrt) {
int pos = Math.max(0, random.nextInt(tokens.size() / 2 + 1) * 2);
tokens.add(pos, "sqrt(4)");
if (pos + 1 < tokens.size()) {
tokens.add(pos + 1, "+");
}
}
return String.join(" ", tokens);
}
}

@ -0,0 +1,94 @@
import java.util.ArrayList;
import java.util.List;
class PrimaryQuestionGenerator extends BaseQuestionGenerator {
@Override
protected String generateQuestion() {
List<String> numbers = generateNumbers();
List<String> operators = generateOperators(numbers);
List<String> tokens = combineTokens(numbers, operators);
addRandomBrackets(tokens);
return String.join(" ", tokens);
}
/**
* .
*/
private List<String> generateNumbers() {
int ops = randInt(2, Constants.MAX_OPERANDS);
List<String> numbers = new ArrayList<>();
for (int i = 0; i < ops; i++) {
numbers.add(String.valueOf(randInt(1, Constants.MAX_VALUE)));
}
return numbers;
}
/**
* .
*/
private List<String> generateOperators(List<String> numbers) {
List<String> operators = new ArrayList<>();
String current = numbers.getFirst();
for (int i = 1; i < numbers.size(); i++) {
String op = randomBasicOperator();
if ("-".equals(op)) {
int left = Integer.parseInt(current.replaceAll("[^0-9]", ""));
int right = Integer.parseInt(numbers.get(i));
if (left < right) {
op = random.nextBoolean() ? "+" : "*";
}
}
operators.add(op);
current = computeIntermediate(current, numbers.get(i), op);
}
return operators;
}
/**
* tokens .
*/
private List<String> combineTokens(List<String> numbers, List<String> operators) {
List<String> tokens = new ArrayList<>();
tokens.add(numbers.getFirst());
for (int i = 1; i < numbers.size(); i++) {
tokens.add(operators.get(i - 1));
tokens.add(numbers.get(i));
}
return tokens;
}
/**
* .
*/
private void addRandomBrackets(List<String> tokens) {
int ops = (tokens.size() + 1) / 2;
if (ops < 2 || !random.nextBoolean()) {
return;
}
int pairCount = ops - 1;
int chosenOpIndex = random.nextInt(pairCount);
int leftIndex = chosenOpIndex * 2;
int rightIndex = chosenOpIndex * 2 + 2;
if (!(leftIndex == 0 && rightIndex == tokens.size() - 1)) {
tokens.add(leftIndex, "(");
tokens.add(rightIndex + 2, ")");
}
}
/**
* .
*/
private String computeIntermediate(String current, String next, String op) {
int c = Integer.parseInt(current);
int n = Integer.parseInt(next);
return switch (op) {
case "*" -> String.valueOf(c * n);
case "+" -> String.valueOf(c + n);
case "/" -> n != 0 ? String.valueOf(c / n) : String.valueOf(c);
default -> String.valueOf(c); // 避免减法导致负数
};
}
}
Loading…
Cancel
Save