|
|
|
|
@ -13,105 +13,82 @@ import javax.script.ScriptException;
|
|
|
|
|
*/
|
|
|
|
|
public class Create {
|
|
|
|
|
|
|
|
|
|
// 评估表达式数值(double),若失败抛异常
|
|
|
|
|
private static double evalExpression(String expr) throws ScriptException {
|
|
|
|
|
return ExpressionEvaluator.evaluate(expr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成试卷:返回 Paper 对象(questions, options, correctIndex)
|
|
|
|
|
*/
|
|
|
|
|
public static Paper create(int n, Login.Level currentLevel, Login.Account user) {
|
|
|
|
|
List<String> existing = LoadFile.loadExistingQuestions(user.username);
|
|
|
|
|
Generator qg = new Generator(currentLevel, existing);
|
|
|
|
|
List<String> paper = qg.generatePaper(n);
|
|
|
|
|
if (paper.isEmpty()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
List<String> paper = generatePaper(n, currentLevel, existing);
|
|
|
|
|
if (paper.isEmpty()) return null;
|
|
|
|
|
|
|
|
|
|
// 为每道题生成 4 个选项:一个正确值(三位有效数)加上 3 个干扰项
|
|
|
|
|
List<String[]> optionsList = new ArrayList<>();
|
|
|
|
|
int[] correctIdx = new int[paper.size()];
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < paper.size(); i++) {
|
|
|
|
|
String q = paper.get(i);
|
|
|
|
|
double correctVal;
|
|
|
|
|
try {
|
|
|
|
|
correctVal = evalExpression(q);
|
|
|
|
|
} catch (Exception ex) {
|
|
|
|
|
// 如果无法精确计算(如复杂表达式),我们退而使用字符串匹配:将题目文本作为正确选项,生成三个变体文本
|
|
|
|
|
String[] opts = new String[4];
|
|
|
|
|
opts[0] = q;
|
|
|
|
|
opts[1] = q + " + 1";
|
|
|
|
|
opts[2] = q + " - 1";
|
|
|
|
|
opts[3] = "错误:" + q;
|
|
|
|
|
shuffleArray(opts);
|
|
|
|
|
int idx = findIndex(opts, q);
|
|
|
|
|
optionsList.add(opts);
|
|
|
|
|
correctIdx[i] = idx;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 格式化正确值为保留3位小数(若为整数则不显示小数)
|
|
|
|
|
String correctStr = formatNumber(correctVal);
|
|
|
|
|
|
|
|
|
|
// 判断是否为整数答案
|
|
|
|
|
boolean isIntAnswer = Math.abs(correctVal - Math.round(correctVal)) < 1e-6;
|
|
|
|
|
|
|
|
|
|
// 产生三个干扰值
|
|
|
|
|
Set<String> optsSet = new LinkedHashSet<>();
|
|
|
|
|
optsSet.add(correctStr);
|
|
|
|
|
Random r = new Random();
|
|
|
|
|
int attempts = 0;
|
|
|
|
|
while (optsSet.size() < 4 && attempts < 50) {
|
|
|
|
|
attempts++;
|
|
|
|
|
double delta;
|
|
|
|
|
if (isIntAnswer) {
|
|
|
|
|
// 整数题:干扰项也是整数(偏差 ±1~±10)
|
|
|
|
|
delta = r.nextInt(10) + 1;
|
|
|
|
|
if (r.nextBoolean()) {
|
|
|
|
|
delta = -delta;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 小数题:随机偏差比例
|
|
|
|
|
delta = (Math.abs(correctVal) < 1e-6)
|
|
|
|
|
? (r.nextDouble() * 5 + 1)
|
|
|
|
|
: (r.nextGaussian() * Math.max(1, Math.abs(correctVal) * 0.15));
|
|
|
|
|
}
|
|
|
|
|
double cand = correctVal + delta;
|
|
|
|
|
String s = formatNumber(cand);
|
|
|
|
|
optsSet.add(s);
|
|
|
|
|
}
|
|
|
|
|
// 不足 4 个时补齐
|
|
|
|
|
while (optsSet.size() < 4) {
|
|
|
|
|
double cand = isIntAnswer
|
|
|
|
|
? correctVal + (r.nextInt(10) - 5)
|
|
|
|
|
: correctVal + (r.nextDouble() * 2 - 1);
|
|
|
|
|
optsSet.add(formatNumber(cand));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String[] optsArr = optsSet.toArray(new String[0]);
|
|
|
|
|
// 随机打乱并记录正确项位置
|
|
|
|
|
shuffleArray(optsArr);
|
|
|
|
|
int idx = findIndex(optsArr, correctStr);
|
|
|
|
|
optionsList.add(optsArr);
|
|
|
|
|
correctIdx[i] = idx;
|
|
|
|
|
optionsList.add(generateOptions(q, correctIdx, i));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
savePaper(user.username, paper);
|
|
|
|
|
|
|
|
|
|
return new Paper(paper, optionsList, correctIdx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static List<String> generatePaper(int n, Login.Level level, List<String> existing) {
|
|
|
|
|
Generator qg = new Generator(level, existing);
|
|
|
|
|
return qg.generatePaper(n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static String[] generateOptions(String question, int[] correctIdx, int i) {
|
|
|
|
|
double correctVal;
|
|
|
|
|
try {
|
|
|
|
|
correctVal = evalExpression(question);
|
|
|
|
|
return generateNumericOptions(correctVal, correctIdx, i);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
return generateTextOptions(question, correctIdx, i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static String[] generateTextOptions(String question, int[] correctIdx, int i) {
|
|
|
|
|
String[] opts = new String[]{question, question + " + 1", question + " - 1", "错误:" + question};
|
|
|
|
|
shuffleArray(opts);
|
|
|
|
|
correctIdx[i] = findIndex(opts, question);
|
|
|
|
|
return opts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static String[] generateNumericOptions(double correctVal, int[] correctIdx, int i) {
|
|
|
|
|
String correctStr = formatNumber(correctVal);
|
|
|
|
|
boolean isInt = Math.abs(correctVal - Math.round(correctVal)) < 1e-6;
|
|
|
|
|
Set<String> optsSet = new LinkedHashSet<>();
|
|
|
|
|
optsSet.add(correctStr);
|
|
|
|
|
Random r = new Random();
|
|
|
|
|
|
|
|
|
|
int attempts = 0;
|
|
|
|
|
while (optsSet.size() < 4 && attempts < 50) {
|
|
|
|
|
attempts++;
|
|
|
|
|
double delta = isInt ? r.nextInt(10) + 1 : (r.nextGaussian() * Math.max(1, Math.abs(correctVal) * 0.15));
|
|
|
|
|
if (isInt && r.nextBoolean()) delta = -delta;
|
|
|
|
|
optsSet.add(formatNumber(correctVal + delta));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保存试卷原文本(题干)到文件(按你的 Save 实现)
|
|
|
|
|
while (optsSet.size() < 4) {
|
|
|
|
|
double delta = isInt ? r.nextInt(10) - 5 : r.nextDouble() * 2 - 1;
|
|
|
|
|
optsSet.add(formatNumber(correctVal + delta));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String[] optsArr = optsSet.toArray(new String[0]);
|
|
|
|
|
shuffleArray(optsArr);
|
|
|
|
|
correctIdx[i] = findIndex(optsArr, correctStr);
|
|
|
|
|
return optsArr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void savePaper(String username, List<String> paper) {
|
|
|
|
|
try {
|
|
|
|
|
// 为保存,我们保存原题干列表(文本)
|
|
|
|
|
java.util.List<String> raw = new ArrayList<>();
|
|
|
|
|
for (String s : paper) {
|
|
|
|
|
raw.add(s);
|
|
|
|
|
}
|
|
|
|
|
Save.savePaper(user.username, raw);
|
|
|
|
|
Save.savePaper(username, new ArrayList<>(paper));
|
|
|
|
|
} catch (RuntimeException re) {
|
|
|
|
|
// 抛给 UI 处理(但仍返回 Paper)
|
|
|
|
|
System.err.println("保存试卷时出错: " + re.getMessage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new Paper(paper, optionsList, correctIdx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void shuffleArray(String[] arr) {
|
|
|
|
|
@ -125,19 +102,13 @@ public class Create {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int findIndex(String[] arr, String target) {
|
|
|
|
|
for (int i = 0; i < arr.length; i++) {
|
|
|
|
|
if (arr[i].equals(target)) {
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < arr.length; i++) if (arr[i].equals(target)) return i;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static String formatNumber(double v) {
|
|
|
|
|
if (Math.abs(v - Math.round(v)) < 1e-6) {
|
|
|
|
|
return String.valueOf((long) Math.round(v));
|
|
|
|
|
} else {
|
|
|
|
|
return String.format("%.3f", v);
|
|
|
|
|
}
|
|
|
|
|
if (Math.abs(v - Math.round(v)) < 1e-6) return String.valueOf((long) Math.round(v));
|
|
|
|
|
return String.format("%.3f", v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|