parent
2a068523af
commit
41ecae7f5d
@ -0,0 +1,20 @@
|
||||
1. 37 + 35
|
||||
|
||||
2. 82 * 28
|
||||
|
||||
3. (16 / 56) + 42
|
||||
|
||||
4. 6 * 52 + 35
|
||||
|
||||
5. (99 + 26) + 37
|
||||
|
||||
6. 40 * 63
|
||||
|
||||
7. 52 - 61 - 16 - 70 * 51
|
||||
|
||||
8. 65 + 83
|
||||
|
||||
9. 84 / 64
|
||||
|
||||
10. 24 - 47 + 21 + 1 - 33
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
王五3 123 HIGH wangwu3@example.com
|
||||
王五2 123 HIGH wangwu2@example.com
|
||||
王五1 123 HIGH wangwu1@example.com
|
||||
张三3 123 PRIMARY zhangsan3@example.com
|
||||
张三1 123 PRIMARY zhangsan1@example.com
|
||||
张三2 123 PRIMARY zhangsan2@example.com
|
||||
李四3 123 MIDDLE lishi3@example.com
|
||||
sqf Aa123456 PRIMARY sqf090815@hnu.edu.cn
|
||||
李四1 123 MIDDLE lishi1@example.com
|
||||
李四2 123 MIDDLE lishi2@example.com
|
||||
@ -0,0 +1,27 @@
|
||||
# mathStudyAPP
|
||||
## 📂 项目结构
|
||||
src/
|
||||
├─ Main.java
|
||||
├─ controller/
|
||||
│ ├─ AssignController.java
|
||||
│ └─ FunctionController.java
|
||||
├─ model/
|
||||
│ ├─ Login.java
|
||||
│ ├─ LanguageSwitch.java
|
||||
│ ├─ QuestionGenerator.java
|
||||
│ ├─ Generator.java
|
||||
│ ├─ LoadFile.java
|
||||
│ ├─ Save.java
|
||||
│ ├─ Create.java
|
||||
│ └─ Paper.java
|
||||
└─ view/
|
||||
├─ LoginFrame.java
|
||||
├─ RegisterFrame.java
|
||||
├─ MainMenuFrame.java
|
||||
├─ ExamSetupFrame.java
|
||||
├─ ExamFrame.java
|
||||
└─ ResultFrame.java
|
||||
|
||||
---
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="lib" level="project" />
|
||||
<orderEntry type="library" name="jakarta.activation-2.0.1 (2)" level="project" />
|
||||
<orderEntry type="library" name="jakarta.mail-2.0.1 (2)" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,12 @@
|
||||
import javax.swing.SwingUtilities;
|
||||
import view.LoginFrame;
|
||||
|
||||
/**
|
||||
* 程序入口
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SwingUtilities.invokeLater(() -> new LoginFrame());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,142 @@
|
||||
package model;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 具体题目生成器
|
||||
*/
|
||||
public class Generator extends QuestionGenerator {
|
||||
|
||||
public static final Random RAND = new Random();
|
||||
public static final int MAX_ATTEMPTS = 2000;
|
||||
|
||||
public final Login.Level level;
|
||||
public final Set<String> existing;
|
||||
|
||||
public Generator(Login.Level level, List<String> existingQuestions) {
|
||||
this.level = level;
|
||||
this.existing = existingQuestions.stream().map(String::trim).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public List<String> generatePaper(int n) {
|
||||
Set<String> generated = new LinkedHashSet<>();
|
||||
int attempts = 0;
|
||||
while (generated.size() < n && attempts < MAX_ATTEMPTS) {
|
||||
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);
|
||||
}
|
||||
|
||||
public String normalize(String s) {
|
||||
return s.replaceAll("\\s+", "").toLowerCase();
|
||||
}
|
||||
|
||||
public String generateOneQuestion() {
|
||||
int operands = RAND.nextInt(5) + 1; // 1..5
|
||||
int operands_p = RAND.nextInt(4) + 2; // 2..5
|
||||
return switch (level) {
|
||||
case PRIMARY -> genPrimary(operands_p);
|
||||
case MIDDLE -> genMiddle(operands);
|
||||
case HIGH -> genHigh(operands);
|
||||
};
|
||||
}
|
||||
|
||||
public 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()) {
|
||||
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();
|
||||
}
|
||||
|
||||
public String genMiddle(int operands) {
|
||||
String expr = genPrimary(operands);
|
||||
if (RAND.nextBoolean()) expr = applySquare(expr);
|
||||
else expr = applySqrt(expr);
|
||||
return expr;
|
||||
}
|
||||
|
||||
public String genHigh(int operands) {
|
||||
String expr = genPrimary(operands);
|
||||
expr = applyTrig(expr);
|
||||
return expr;
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
public String applySqrt(String expr) {
|
||||
List<int[]> spans = findNumberSpans(expr);
|
||||
if (spans.isEmpty()) 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;
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
public int randInt(int a, int b) {
|
||||
return RAND.nextInt(b - a + 1) + a;
|
||||
}
|
||||
|
||||
public <T> T randomChoice(List<T> list) {
|
||||
return list.get(RAND.nextInt(list.size()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package model;
|
||||
|
||||
public class LanguageSwitch {
|
||||
public static String levelToChinese(Login.Level l) {
|
||||
return switch (l) {
|
||||
case PRIMARY -> "小学";
|
||||
case MIDDLE -> "初中";
|
||||
case HIGH -> "高中";
|
||||
default -> "未知";
|
||||
};
|
||||
}
|
||||
|
||||
public static Login.Level chineseToLevel(String s) {
|
||||
s = s.trim();
|
||||
return switch (s) {
|
||||
case "小学" -> Login.Level.PRIMARY;
|
||||
case "初中" -> Login.Level.MIDDLE;
|
||||
case "高中" -> Login.Level.HIGH;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class QuestionGenerator {
|
||||
|
||||
protected QuestionGenerator() {
|
||||
}
|
||||
|
||||
public abstract List<String> generatePaper(int n);
|
||||
|
||||
public abstract String normalize(String s);
|
||||
|
||||
public abstract String generateOneQuestion();
|
||||
|
||||
public abstract String genPrimary(int operands);
|
||||
|
||||
public abstract String genMiddle(int operands);
|
||||
|
||||
public abstract String genHigh(int operands);
|
||||
|
||||
public abstract String applySquare(String expr);
|
||||
|
||||
public abstract String applySqrt(String expr);
|
||||
|
||||
public abstract String applyTrig(String expr);
|
||||
|
||||
public abstract java.util.List<int[]> findNumberSpans(String expr);
|
||||
|
||||
public abstract int randInt(int a, int b);
|
||||
|
||||
public abstract <T> T randomChoice(java.util.List<T> list);
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package model;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class Save {
|
||||
public 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) {
|
||||
throw new RuntimeException("无法创建用户文件夹:" + e.getMessage(), e);
|
||||
}
|
||||
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) {
|
||||
throw new RuntimeException("保存文件失败:" + e.getMessage(), e);
|
||||
}
|
||||
return file.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package view;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import model.Login;
|
||||
import controller.AssignController;
|
||||
|
||||
/**
|
||||
* 登录窗口
|
||||
*/
|
||||
public class LoginFrame extends JFrame {
|
||||
|
||||
private JTextField usernameField;
|
||||
private JPasswordField passwordField;
|
||||
|
||||
public LoginFrame() {
|
||||
setTitle("数学卷子生成器 - 登录");
|
||||
setSize(380, 220);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setLocationRelativeTo(null);
|
||||
|
||||
JPanel p = new JPanel(new GridLayout(4, 2, 8, 8));
|
||||
p.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
|
||||
p.add(new JLabel("用户名:"));
|
||||
usernameField = new JTextField();
|
||||
p.add(usernameField);
|
||||
|
||||
p.add(new JLabel("密码:"));
|
||||
passwordField = new JPasswordField();
|
||||
p.add(passwordField);
|
||||
|
||||
JButton loginBtn = new JButton("登录");
|
||||
JButton regBtn = new JButton("注册");
|
||||
JButton exitBtn = new JButton("退出");
|
||||
|
||||
p.add(loginBtn);
|
||||
p.add(regBtn);
|
||||
p.add(new JLabel());
|
||||
p.add(exitBtn);
|
||||
|
||||
add(p);
|
||||
|
||||
loginBtn.addActionListener(e -> {
|
||||
String u = usernameField.getText().trim();
|
||||
String pwd = new String(passwordField.getPassword()).trim();
|
||||
if (u.isEmpty() || pwd.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(this, "请输入用户名和密码", "提示", JOptionPane.INFORMATION_MESSAGE);
|
||||
return;
|
||||
}
|
||||
Login.Account acc = Login.login(u, pwd);
|
||||
if (acc != null) {
|
||||
dispose();
|
||||
new MainMenuFrame(acc); // 登录后先进入主菜单
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(this, "用户名或密码错误", "登录失败", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
regBtn.addActionListener(e -> {
|
||||
new RegisterFrame(this);
|
||||
});
|
||||
|
||||
exitBtn.addActionListener(e -> System.exit(0));
|
||||
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package view;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import model.Login;
|
||||
|
||||
/**
|
||||
* 显示成绩并提供“退出或继续做题”的选择
|
||||
*/
|
||||
public class ResultFrame extends JFrame {
|
||||
public ResultFrame(Login.Account user, int score, int total, double percent) {
|
||||
setTitle("成绩 - " + user.username);
|
||||
setSize(360,220);
|
||||
setLocationRelativeTo(null);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
JPanel p = new JPanel(new GridLayout(5,1,6,6));
|
||||
p.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
|
||||
p.add(new JLabel("用户名: " + user.username));
|
||||
p.add(new JLabel("得分: " + score + " / " + total));
|
||||
p.add(new JLabel(String.format("百分比: %.2f%%", percent)));
|
||||
|
||||
JPanel btns = new JPanel(new FlowLayout());
|
||||
JButton exit = new JButton("退出");
|
||||
JButton again = new JButton("继续做题");
|
||||
btns.add(again);
|
||||
btns.add(exit);
|
||||
p.add(btns);
|
||||
|
||||
add(p);
|
||||
exit.addActionListener(e -> System.exit(0));
|
||||
again.addActionListener(e -> {
|
||||
dispose();
|
||||
new ExamSetupFrame(user);
|
||||
});
|
||||
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue