1 #1

Closed
hnu202326010315 wants to merge 0 commits from master into develop

29
.gitignore vendored

@ -0,0 +1,29 @@
### IntelliJ IDEA ###
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GoogleJavaFormatSettings">
<option name="enabled" value="true" />
</component>
</project>

@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/relentless1.iml" filepath="$PROJECT_DIR$/relentless1.iml" />
<module fileurl="file://$PROJECT_DIR$/quesproject.iml" filepath="$PROJECT_DIR$/quesproject.iml" />
</modules>
</component>
</project>

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/relentless" vcs="Git" />
</component>
</project>

@ -9,35 +9,31 @@
- 支持小学、初中和高中三个级别的用户账号
- 每个级别预设3个账号密码统一为123
- 登录成功后显示当前选择的出题级别
- 支持用户退出功能,可重新登录
### 2. 题目生成功能
- 根据账号类型自动选择对应的难度级别生成题目
- 支持自定义生成题目数量范围10-30题
- 操作数范围1-100
- 操作数个数1-5个
- 生成题目时实时显示题目内容
- 操作数个数2-5个
### 3. 多难度级别支持
- **小学**:仅包含加减乘除四则运算和括号,支持随机括号嵌套
- **初中**:在小学基础上增加平方和开根号运算,题目包含1-5个操作数若只有1个操作数则必须是平方或根号表达式确保每道题至少包含一个平方或开根号
- **高中**在初中基础上增加三角函数sin、cos、tan题目包含1-5个操作数但若只有1个操作数则强制为三角函数表达式确保每道题至少包含一个三角函数
- **小学**:仅包含加减乘除四则运算和括号
- **初中**:在小学基础上增加平方和开根号运算,每道题至少包含一个平方或开根号
- **高中**在初中基础上增加三角函数sin、cos、tan每道题至少包含一个三角函数
### 4. 题目类型切换
- 支持在登录状态下切换不同难度级别的题目生成
- 输入格式:`切换为 小学/初中/高中`
- 切换成功后系统清屏并提示新的难度级别
### 5. 题目查重功能
- 自动检测新生成的题目是否与当前用户历史生成的题目重复
- 设置最大尝试次数,防止在题库不足时陷入死循环
- 确保每个用户生成的题目都是唯一的
### 6. 文件保存功能
- 题目以文本文件形式保存
- 文件名格式:`yyyy-MM-dd-HH-mm-ss.txt`(基于生成时间)
- 每个用户有独立的文件夹存储题目(文件夹名为用户名)
- 题目包含题号,每题之间空一行,便于阅读
- 文件名格式:`年-月-日-时-分-秒.txt`
- 每个用户有独立的文件夹存储题目
- 题目包含题号,每题之间空一行
## 使用说明
@ -49,13 +45,11 @@
### 生成题目
1. 登录成功后,系统提示输入题目数量
2. 输入10-30之间的数字生成对应数量的题目
3. 系统将显示生成的题目并保存到用户文件夹
4. 输入-1退出当前用户返回登录界面
3. 输入-1退出当前用户返回登录界面
### 切换难度
- 在任何提示输入题目数量的界面,输入`切换为 小学/初中/高中`即可切换题目难度
- 切换成功后,系统会提示新的难度级别
- 输入无效时,系统会提示错误信息并保持当前难度
## 账号列表
@ -73,7 +67,7 @@
## 项目结构
```
relentless1/
quesproject/
├── src/ # 源代码目录
│ ├── Main.java # 程序入口类
│ ├── User.java # 用户类
@ -87,88 +81,29 @@ relentless1/
│ └── FileUtils.java # 文件操作工具类
├── doc/ # 文档目录
│ └── README.md # 项目说明文档
── [用户名]/ # 用户题目文件夹(运行时自动创建)
── [用户名]/ # 用户题目文件夹(运行时自动创建)
```
## 技术实现
### 设计模式
- **策略模式**:通过`QuestionStrategy`接口定义题目生成策略,不同难度级别(小学、初中、高中)实现各自的题目生成算法
- **工厂模式**:通过`QuestionStrategyFactory`根据用户角色动态创建对应的题目生成策略对象
### 核心类功能
#### Main.java
- 程序入口,处理用户交互逻辑
- 实现登录流程、题目数量输入和难度切换功能
- 包含清屏方法,提升用户体验
#### User.java
- 表示用户实体,包含用户名、密码和角色属性
- 提供静态登录方法,验证用户凭据
- 用户信息硬编码在程序中每个角色有3个预设账号
#### QuestionGenerator.java
- 根据用户角色使用对应策略生成题目
- 实现题目查重逻辑,确保题目唯一性
- 协调策略选择、题目生成和文件保存
#### QuestionStrategy.java及其实现类
- `QuestionStrategy`:定义题目生成的接口
- `ElementaryQuestionStrategy`:实现小学题目的生成,包含加减乘除和括号
- `MiddleSchoolQuestionStrategy`实现初中题目生成增加平方和开根号运算题目包含1-5个操作数单操作数题目强制为平方或根号表达式确保每道题至少包含一个平方或开根号
- `HighSchoolQuestionStrategy`实现高中题目生成增加三角函数sin、cos、tan运算题目包含1-5个操作数单操作数题目强制为三角函数表达式确保每道题至少包含一个三角函数
#### MathQuestion.java
- 表示单道数学题,包含题号和题目内容
#### FileUtils.java
- 提供文件操作工具方法
- 生成时间戳格式的文件名
- 保存题目到用户文件夹
- 加载用户历史题目进行查重
### 算法特点
1. **小学题目**:支持随机括号位置,确保数学表达式的多样性
2. **初中题目**
- 题目包含1-5个操作数
- 若只有1个操作数则必须是平方或根号表达式
- 有50%概率生成平方或根号操作数
- 确保每道题至少有一个平方或开根号运算
3. **高中题目**
- 题目包含1-5个操作数
- 若只有1个操作数则强制生成三角函数表达式
- 支持sin、cos、tan三种三角函数
- 确保每道题至少有一个三角函数
4. **查重机制**使用HashSet高效检测重复题目设置最大尝试次数避免死循环
- **工厂模式**通过QuestionStrategyFactory创建不同级别的题目生成策略
- **策略模式**通过QuestionStrategy接口及其实现类实现不同难度级别的题目生成
### 核心功能实现
1. **用户验证**:通过预设的账号列表进行验证
2. **题目生成**:根据不同难度级别使用不同的策略生成题目
3. **查重功能**:读取用户历史题目文件,确保新生成的题目不重复
4. **文件管理**:自动创建用户文件夹,按指定格式保存题目文件
## 运行环境
- Java 8及以上版本
- 支持Windows和Linux操作系统清屏功能自适应
## 扩展建议
1. 添加自定义操作数范围和运算符的功能
2. 实现题目答案自动计算功能
3. 支持更多类型的数学表达式和运算符号
4. 添加图形用户界面,提升用户体验
5. 实现用户注册和密码修改功能
6. 支持题目导入导出功能
## 编译与运行
- 支持Windows、Linux、macOS等操作系统
### Windows PowerShell编码设置 (重要)
> **注意**在Windows PowerShell控制台运行程序前请先执行以下命令设置编码否则可能导致中文显示错误或运行的异常
> ```powershell
> [Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
> ```
### 通过源代码编译运行
## 编译运行
1. 编译:`javac -d out src/*.java`
2. 运行:`java -cp out Main`
### 通过JAR包运行
1. 直接运行JAR包`java -jar out/artifacts/relentless1_jar/relentless1.jar`
## 注意事项
1. 题目数量必须在10-30之间否则会提示重新输入
2. 切换难度时,请确保输入格式正确:`切换为 级别名称`

@ -1,230 +1,151 @@
import java.util.Random;
/**
*
*
* <p>
*/
public class ElementaryQuestionStrategy implements QuestionStrategy {
private static final String[] OPERATORS = {"+", "-", "*", "/"};
private final Random random = new Random();
@Override
public String generateQuestion() {
int operandsCount = random.nextInt(4) + 2; // 操作数数量2~5
StringBuilder sb = new StringBuilder();
// 生成运算符序列
String[] chosenOps = generateOperators(operandsCount);
// 随机决定括号位置
int[] parentheses = decideParentheses(operandsCount, chosenOps);
// 拼接表达式
buildExpression(sb, operandsCount, chosenOps, parentheses);
return sb.toString();
}
/**
*
*
* @param operandsCount
* @return - 1
*/
private String[] generateOperators(int operandsCount) {
String[] chosenOps = new String[operandsCount - 1];
for (int i = 0; i < operandsCount - 1; i++) {
chosenOps[i] = OPERATORS[random.nextInt(OPERATORS.length)];
private final Random random = new Random();
private final String[] operators = {"+", "-", "*", "/"};
@Override
public String generateQuestion() {
int operandsCount = random.nextInt(4) + 2; // 操作数 2~5
StringBuilder sb = new StringBuilder();
// 生成运算符序列
String[] chosenOps = generateOperators(operandsCount);
// 随机决定括号
int[] parentheses = decideParentheses(operandsCount, chosenOps);
// 拼接表达式
buildExpression(sb, operandsCount, chosenOps, parentheses);
return sb.toString();
}
/**
*
*
*
* @param operandsCount
* @return -1
*/
private String[] generateOperators(int operandsCount) {
String[] chosenOps = new String[operandsCount - 1];
for (int i = 0; i < operandsCount - 1; i++) {
chosenOps[i] = operators[random.nextInt(operators.length)];
}
return chosenOps;
}
/**
*
* 3
*
* @param operandsCount
* @param chosenOps
* @return [openParenIndex, closeParenIndex]-1
*/
private int[] decideParentheses(int operandsCount, String[] chosenOps) {
int openParenIndex = -1;
int closeParenIndex = -1;
if (operandsCount > 2 && random.nextBoolean()) {
openParenIndex = random.nextInt(operandsCount - 1);
closeParenIndex = random.nextInt(operandsCount - openParenIndex - 1) + openParenIndex + 1;
// 如果括号包裹整个表达式,则不需要括号
if (openParenIndex == 0 && closeParenIndex == operandsCount - 1) {
openParenIndex = -1;
closeParenIndex = -1;
} else {
// 检查括号内的运算符优先级是否相同,如果相同则不需要括号
boolean samePrecedence = checkPrecedenceEquality(chosenOps, openParenIndex, closeParenIndex);
if (samePrecedence) {
openParenIndex = -1;
closeParenIndex = -1;
}
}
}
return new int[]{openParenIndex, closeParenIndex};
}
/**
*
*
*
* @param chosenOps
* @param openParenIndex
* @param closeParenIndex
* @return truefalse
*/
private boolean checkPrecedenceEquality(String[] chosenOps, int openParenIndex, int closeParenIndex) {
int precedence = getPrecedence(chosenOps[openParenIndex]);
for (int i = openParenIndex; i < closeParenIndex; i++) {
if (getPrecedence(chosenOps[i]) != precedence) {
return false;
}
}
return true;
}
/**
*
*
*
* @param sb StringBuilder
* @param operandsCount
* @param chosenOps
* @param parentheses [openParenIndex, closeParenIndex]
*/
private void buildExpression(StringBuilder sb, int operandsCount, String[] chosenOps, int[] parentheses) {
int prevNum = random.nextInt(100) + 1;
int openParenIndex = parentheses[0];
int closeParenIndex = parentheses[1];
// 处理第一个操作数可能的开括号
if (openParenIndex == 0) sb.append("(");
sb.append(prevNum);
// 处理最后一个操作数可能的闭括号
if (closeParenIndex == operandsCount - 1) sb.append(")");
// 构建剩余的操作数和运算符
for (int i = 1; i < operandsCount; i++) {
String op = chosenOps[i - 1];
sb.append(" ").append(op).append(" ");
// 根据运算符生成下一个操作数
int num = generateOperand(op, prevNum);
// 添加括号(如果需要)
if (i == openParenIndex) sb.append("(");
sb.append(num);
if (i == closeParenIndex) sb.append(")");
prevNum = num; // 更新上一个数字,供下一轮计算使用
}
}
/**
*
*
*
* @param op
* @param prevNum
* @return
*/
private int generateOperand(String op, int prevNum) {
int num = random.nextInt(100) + 1; // 默认生成1-100之间的随机数
// 如果是减法运算符,确保不会出现负数结果
if (op.equals("-") && num > prevNum) {
num = random.nextInt(prevNum) + 1; // 保证 num <= prevNum避免负数结果
}
return num;
}
private int getPrecedence(String op) {
if (op.equals("+") || op.equals("-")) return 1;
if (op.equals("*") || op.equals("/")) return 2;
return 0;
}
return chosenOps;
}
/**
*
*
* @param sb StringBuilder
* @param operandsCount
* @param chosenOps
* @param parentheses [openParenIndex, closeParenIndex]
*/
private void buildExpression(
StringBuilder sb, int operandsCount, String[] chosenOps, int[] parentheses) {
int openParenIndex = parentheses[0];
int closeParenIndex = parentheses[1];
// 生成第一个操作数
int prevNum = random.nextInt(100) + 1;
// 如果开括号在第一个操作数之前
if (openParenIndex == 0) {
sb.append("(");
}
sb.append(prevNum);
// 如果闭括号在第一个操作数之后(即只包含第一个操作数)
if (closeParenIndex == 0) {
sb.append(")");
}
for (int i = 1; i < operandsCount; i++) {
String op = chosenOps[i - 1];
sb.append(" ").append(op).append(" ");
int num = generateOperand(op, prevNum);
// 如果开括号在当前操作数之前
if (i == openParenIndex) {
sb.append("(");
}
sb.append(num);
// 如果闭括号在当前操作数之后
if (i == closeParenIndex) {
sb.append(")");
}
prevNum = num;
}
// 确保括号正确闭合(安全措施)
String expression = sb.toString();
int openCount = countChar(expression, '(');
int closeCount = countChar(expression, ')');
// 如果括号不匹配,重新生成表达式
if (openCount != closeCount) {
sb.setLength(0); // 清空 StringBuilder
// 递归调用生成新的表达式(或者去掉所有括号)
buildExpressionWithoutParentheses(sb, operandsCount, chosenOps);
}
}
/**
*
*/
private void buildExpressionWithoutParentheses(
StringBuilder sb, int operandsCount, String[] chosenOps) {
int prevNum = random.nextInt(100) + 1;
sb.append(prevNum);
for (int i = 1; i < operandsCount; i++) {
String op = chosenOps[i - 1];
sb.append(" ").append(op).append(" ");
int num = generateOperand(op, prevNum);
sb.append(num);
prevNum = num;
}
}
/**
*
*
* @param str
* @param ch
* @return
*/
private int countChar(String str, char ch) {
int count = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == ch) {
count++;
}
}
return count;
}
/**
*
*
* @param operandsCount
* @param chosenOps
* @return [openParenIndex, closeParenIndex]
*/
private int[] decideParentheses(int operandsCount, String[] chosenOps) {
if (operandsCount <= 2) {
return new int[] {-1, -1}; // 操作数太少,不需要括号
}
if (!random.nextBoolean()) {
return new int[] {-1, -1}; // 随机决定不加括号
}
// 生成有效的括号位置
int openParenIndex = random.nextInt(operandsCount - 1);
int closeParenIndex =
random.nextInt(operandsCount - openParenIndex - 1) + openParenIndex + 1;
// 确保至少包含 2 个操作数
if (closeParenIndex - openParenIndex < 2) {
closeParenIndex = openParenIndex + 1;
}
// 如果括号包裹整个表达式,则不需要括号
if (openParenIndex == 0 && closeParenIndex == operandsCount - 1) {
return new int[] {-1, -1};
}
// 检查括号内运算符是否同一优先级,如果是则不需要括号
boolean samePrecedence = checkPrecedenceEquality(chosenOps, openParenIndex, closeParenIndex);
if (samePrecedence) {
return new int[] {-1, -1};
}
return new int[] {openParenIndex, closeParenIndex};
}
/**
*
*
* @param chosenOps
* @param openParenIndex
* @param closeParenIndex
* @return true
*/
private boolean checkPrecedenceEquality(
String[] chosenOps, int openParenIndex, int closeParenIndex) {
int precedence = getPrecedence(chosenOps[openParenIndex]);
for (int i = openParenIndex; i < closeParenIndex; i++) {
if (getPrecedence(chosenOps[i]) != precedence) {
return false;
}
}
return true;
}
/**
*
*
* <p>
*
* @param op
* @param prevNum
* @return
*/
private int generateOperand(String op, int prevNum) {
int num = random.nextInt(100) + 1;
if (op.equals("-") && num > prevNum) {
num = random.nextInt(prevNum) + 1;
}
return num;
}
/**
*
*
* @param op
* @return
*/
private int getPrecedence(String op) {
if (op.equals("+") || op.equals("-")) {
return 1;
}
if (op.equals("*") || op.equals("/")) {
return 2;
}
return 0;
}
}

@ -1,115 +1,64 @@
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
/**
*
*
*/
public final class FileUtils {
public class FileUtils {
/** 文件后缀名常量。 */
private static final String FILE_SUFFIX = ".txt";
private FileUtils() {
// 工具类不允许实例化
}
/**
*
*
* @return {@code yyyy-MM-dd-HH-mm-ss.txt}
*/
public static String generateFileName() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
return sdf.format(new Date()) + FILE_SUFFIX;
}
/**
*
*
* @param userId ID
* @param fileName
* @param questions
* @throws IOException
*/
public static void saveQuestionsToFile(
String userId, String fileName, List<MathQuestion> questions) throws IOException {
File userFolder = new File(userId);
if (!userFolder.exists() && !userFolder.mkdirs()) {
throw new IOException(
String.format("无法创建用户目录: %s", userFolder.getAbsolutePath()));
}
File file = new File(userFolder, fileName);
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
for (MathQuestion question : questions) {
writer.write(
String.format("题号 %d: %s",
question.getQuestionNumber(),
question.getQuestionText()));
writer.newLine();
writer.newLine();
}
} catch (IOException e) {
throw new IOException(
String.format("写文件失败: %s", file.getAbsolutePath()), e);
// 生成文件名
public static String generateFileName() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
return sdf.format(new Date()) + ".txt";
}
System.out.printf("题目已生成并保存到: %s%n%n", file.getAbsolutePath());
}
/**
* txt
*
* @param userId ID
* @return
* @throws IOException
*/
public static Set<String> loadExistingQuestions(String userId) throws IOException {
Set<String> existingQuestions = new HashSet<>();
File userFolder = new File(userId);
// 保存题目到用户文件夹
public static void saveQuestionsToFile(String userId, String fileName, List<MathQuestion> questions) {
File userFolder = new File(userId);
if (!userFolder.exists()) {
userFolder.mkdirs();
}
if (!userFolder.exists() || !userFolder.isDirectory()) {
return existingQuestions;
File file = new File(userFolder, fileName);
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
for (MathQuestion question : questions) {
writer.write("题号 " + question.getQuestionNumber() + ": " + question.getQuestionText());
writer.newLine();
writer.newLine();
}
writer.flush();
} catch (IOException e) {
throw new RuntimeException("写文件失败: " + file.getAbsolutePath(), e);
}
}
File[] files =
userFolder.listFiles(
(dir, name) -> name != null && name.endsWith(FILE_SUFFIX));
// 读取用户文件夹下所有 txt 文件中已存在的题目
public static Set<String> loadExistingQuestions(String userId) {
Set<String> existingQuestions = new HashSet<>();
File userFolder = new File(userId);
if (!userFolder.exists() || !userFolder.isDirectory()) return existingQuestions;
if (files == null) {
return existingQuestions;
}
File[] files = userFolder.listFiles((dir, name) -> name != null && name.endsWith(".txt"));
if (files == null) return existingQuestions;
for (File file : files) {
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.isEmpty()) {
continue;
}
if (line.startsWith("题号")) {
int colonIndex = line.indexOf(":");
if (colonIndex != -1 && colonIndex + 1 < line.length()) {
existingQuestions.add(line.substring(colonIndex + 1).trim());
for (File f : files) {
try (BufferedReader reader = new BufferedReader(new FileReader(f))) {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.isEmpty()) continue;
if (line.startsWith("题号")) {
int idx = line.indexOf(":");
if (idx != -1 && idx + 1 < line.length()) {
existingQuestions.add(line.substring(idx + 1).trim());
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
throw new IOException(
String.format("读取文件失败: %s", file.getAbsolutePath()), e);
}
return existingQuestions;
}
return existingQuestions;
}
}

@ -1,90 +1,42 @@
import java.util.Random;
/**
*
*
* <p>
* 15 1 </p>
*/
public class HighSchoolQuestionStrategy implements QuestionStrategy {
/** 基本四则运算符。 */
private static final String[] BASIC_OPS = {"+", "-", "*", "/"};
/** 支持的三角函数。 */
private static final String[] TRIG_FUNCS = {"sin", "cos", "tan"};
/** 随机数生成器。 */
private final Random random = new Random();
/**
*
*
* @return
*/
@Override
public String generateQuestion() {
// 操作数个数1~5
int operandsCount = random.nextInt(5) + 1;
StringBuilder expression = new StringBuilder();
boolean hasTrig = false;
// 仅一个操作数时,强制生成一个三角函数表达式
if (operandsCount == 1) {
return buildTrigOperand();
}
// 生成多个操作数与运算符
for (int i = 0; i < operandsCount; i++) {
String operand = buildOperand();
expression.append(operand);
if (operand.matches(".*(sin|cos|tan).*")) {
hasTrig = true;
}
// 添加运算符(除最后一个操作数外)
if (i != operandsCount - 1) {
expression.append(' ')
.append(BASIC_OPS[random.nextInt(BASIC_OPS.length)])
.append(' ');
}
private final Random random = new Random();
private final String[] basicOps = {"+", "-", "*", "/"};
private final String[] trigFuncs = {"sin", "cos", "tan"};
@Override
public String generateQuestion() {
// 随机操作数个数 2-5
int operandsCount = random.nextInt(4) + 2;
StringBuilder sb = new StringBuilder();
boolean hasTrig = false;
for (int i = 0; i < operandsCount; i++) {
int num = random.nextInt(100) + 1;
// 每个操作数有概率加三角函数
if (random.nextBoolean()) {
String func = trigFuncs[random.nextInt(trigFuncs.length)];
sb.append(func).append("(").append(num).append(")");
hasTrig = true;
} else {
sb.append(num);
}
if (i != operandsCount - 1) {
String op = basicOps[random.nextInt(basicOps.length)];
sb.append(" ").append(op).append(" ");
}
}
// 确保至少一个三角函数
if (!hasTrig) {
String func = trigFuncs[random.nextInt(trigFuncs.length)];
int num = random.nextInt(100) + 1;
return func + "(" + num + ") + " + sb.toString();
}
return sb.toString();
}
// 确保至少包含一个三角函数
if (!hasTrig) {
expression.append(" + ").append(buildTrigOperand());
}
return expression.toString();
}
/**
*
*/
private String buildOperand() {
int num = random.nextInt(100) + 1;
if (random.nextBoolean()) {
return buildTrigOperandWithNumber(num);
}
return String.valueOf(num);
}
/**
*
*/
private String buildTrigOperand() {
int num = random.nextInt(100) + 1;
return buildTrigOperandWithNumber(num);
}
/**
*
*
* @param number 1~100
* @return sin(45)
*/
private String buildTrigOperandWithNumber(int number) {
String func = TRIG_FUNCS[random.nextInt(TRIG_FUNCS.length)];
return func + "(" + number + ")";
}
}

@ -1,3 +0,0 @@
Manifest-Version: 1.0
Main-Class: Main

@ -1,191 +1,100 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
/**
*
*
*/
public class Main {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
User currentUser = null;
private static final String ROLE_PRIMARY = "小学";
private static final String ROLE_JUNIOR = "初中";
private static final String ROLE_SENIOR = "高中";
/**
*
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
clearScreen();
System.out.println(
"-----------------欢迎来到中小学数学卷子自动生成程序-----------------");
try (Scanner scanner = new Scanner(System.in)) {
while (true) {
// 登录流程
User currentUser = loginProcess(scanner);
// 题目生成流程
generateProcess(scanner, currentUser);
}
}
}
/**
*
*
* @param scanner
* @return
*/
private static User loginProcess(Scanner scanner) {
User currentUser = null;
while (currentUser == null) {
System.out.print("请输入用户名和密码,用空格分隔:");
String username = scanner.next();
String password = scanner.next();
currentUser = User.login(username, password);
if (currentUser == null) {
System.out.println("请输入正确的用户名、密码");
} else {
clearScreen();
System.out.println("当前选择为 " + currentUser.getRole() + " 出题");
}
}
return currentUser;
}
/**
*
*
* @param scanner
* @param currentUser
* @throws IOException
*/
private static void generateProcess(Scanner scanner, User currentUser)
throws IOException {
String currentRole = currentUser.getRole();
while (true) {
System.out.println(
"准备生成 " + currentRole
+ " 数学题目,请输入生成题目数量(输入 -1 将退出当前用户,重新登录):");
String input = scanner.next();
if (handleExit(input)) {
return; // 返回上一级,重新登录
}
if (handleRoleSwitch(input, currentRole)) {
currentRole = input.substring(3).trim();
continue;
}
Integer questionCount = parseQuestionCount(input, currentRole);
if (questionCount == null) {
continue;
}
clearScreen();
QuestionGenerator generator =
new QuestionGenerator(currentUser, currentRole);
generator.generateQuestions(questionCount);
}
}
/**
* 退
*
* @param input
* @return 退 true
*/
private static boolean handleExit(String input) {
if (input.equals("-1")) {
clearScreen();
System.out.println("已退出当前用户,重新登录...");
return true;
}
return false;
}
/**
*
*
* @param input
* @param currentRole
* @return true false
*/
private static boolean handleRoleSwitch(String input, String currentRole) {
if (input.startsWith("切换为")) {
String newRole = input.substring(3).trim();
if (!isValidRole(newRole)) {
System.out.println(
"请输入小学、初中和高中三个选项中的一个 (当前类型为 " + currentRole + ")");
return false;
}
clearScreen();
System.out.println(
"系统提示:准备生成 " + newRole + " 数学题目,请输入生成题目数量");
return true;
}
return false;
}
/**
*
*/
private static boolean isValidRole(String role) {
return role.equals(ROLE_PRIMARY)
|| role.equals(ROLE_JUNIOR)
|| role.equals(ROLE_SENIOR);
}
/**
*
*
* @param input
* @param currentRole
* @return null
*/
private static Integer parseQuestionCount(String input, String currentRole) {
int questionCount;
try {
questionCount = Integer.parseInt(input);
} catch (NumberFormatException e) {
System.out.println(
"请输入有效的数字或使用“切换为小学/初中/高中”命令 (当前类型为 " + currentRole + ")");
return null;
}
if (questionCount < 10 || questionCount > 30) {
System.out.println("请输入有效的题目数量 (10-30) 或 -1 退出");
return null;
while (true) {
while (currentUser == null) {
System.out.print("请输入用户名和密码,用空格分隔:");
String username = scanner.next();
String password = scanner.next();
currentUser = User.login(username, password);
if (currentUser == null) {
System.out.println("请输入正确的用户名、密码");
} else {
clearScreen(); //登录成功清屏
System.out.println("当前选择为 " + currentUser.getRole() + " 出题");
}
}
// 每次登录后初始化当前出题类型(账号默认类型)
String currentRole = currentUser.getRole();
// 题目生成流程
while (true) {
System.out.println("准备生成 " + currentRole
+ " 数学题目,请输入生成题目数量(输入-1将退出当前用户重新登录");
String input = scanner.next();
// 退出登录
if (input.equals("-1")) {
currentUser = null;
clearScreen();
System.out.println("已退出当前用户,重新登录...");
break;
}
// 检测切换命令
if (input.startsWith("切换为")) {
String newRole = input.substring(3).trim();
if (!newRole.equals("小学") && !newRole.equals("初中") && !newRole.equals("高中")) {
System.out.println("请输入小学、初中和高中三个选项中的一个"+"(当前类型为"+currentRole+")");
continue;
}
currentRole = newRole;
clearScreen();
System.out.println("系统提示:准备生成 " + currentRole + " 数学题目,请输入生成题目数量");
continue;
}
// 输入题目数量
int questionCount;
try {
questionCount = Integer.parseInt(input);
} catch (NumberFormatException e) {
System.out.println("请输入有效的数字或使用“切换为小学/初中/高中”命令"+"(当前类型为"+currentRole+")");
continue;
}
if (questionCount < 10 || questionCount > 30) {
System.out.println("请输入有效的题目数量 (10-30) 或 -1 退出");
continue;
}
clearScreen();
// 把 currentRole 传给 QuestionGenerator
QuestionGenerator generator = new QuestionGenerator(currentUser, currentRole);
generator.generateQuestions(questionCount);
System.out.println("题目已生成并保存!\n");
}
}
}
return questionCount;
}
/**
*
*/
public static void clearScreen() {
try {
if (System.getProperty("os.name").contains("Windows")) {
new ProcessBuilder("cmd", "/c", "cls")
.inheritIO()
.start()
.waitFor();
} else {
new ProcessBuilder("clear")
.inheritIO()
.start()
.waitFor();
}
} catch (Exception e) {
for (int i = 0; i < 50; i++) {
System.out.println();
}
/**
*
*/
public static void clearScreen() {
try {
if (System.getProperty("os.name").contains("Windows")) {
new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor();
} else {
new ProcessBuilder("clear").inheritIO().start().waitFor();
}
} catch (Exception e) {
for (int i = 0; i < 50; i++) {
System.out.println();
}
}
}
}
}

@ -1,36 +1,21 @@
/**
*
*
*/
public class MathQuestion {
private final int questionNumber;
private final String questionText;
private int questionNumber;
private String questionText;
/**
*
*
* @param questionNumber
* @param questionText
*/
public MathQuestion(int questionNumber, String questionText) {
this.questionNumber = questionNumber;
this.questionText = questionText;
}
// 构造方法
public MathQuestion(int questionNumber, String questionText) {
this.questionNumber = questionNumber;
this.questionText = questionText;
}
/**
*
*
* @return
*/
public int getQuestionNumber() {
return questionNumber;
}
public int getQuestionNumber() {
return questionNumber;
}
/**
*
*
* @return
*/
public String getQuestionText() {
return questionText;
}
public String getQuestionText() {
return questionText;
}
}

@ -1,78 +1,47 @@
import java.util.Random;
/**
*
* <p></p>
*/
public class MiddleSchoolQuestionStrategy implements QuestionStrategy {
/** 随机数生成器,生成操作数和运算符索引 */
private static final Random RANDOM = new Random();
/** 基本运算符集合 */
private static final String[] BASIC_OPS = {"+", "-", "*", "/"};
/**
*
* <p> 15 1 </p>
*
* @return
*/
@Override
public String generateQuestion() {
int operandsCount = RANDOM.nextInt(5) + 1; // 操作数个数 15
StringBuilder sb = new StringBuilder();
boolean hasSquareOrRoot = false;
// 若只有一个操作数,则直接生成平方或根号表达式
if (operandsCount == 1) {
sb.append(generateSquareOrRoot());
} else {
// 多个操作数:依次生成操作数和运算符
for (int i = 0; i < operandsCount; i++) {
String operand = generateOperand();
sb.append(operand);
if (operand.contains("^2") || operand.contains("√")) {
hasSquareOrRoot = true;
private final Random random = new Random();
private final String[] basicOps = {"+", "-", "*", "/"}; // 基本运算符
@Override
public String generateQuestion() {
// 随机操作数个数 2-5
int operandsCount = random.nextInt(4) + 2;
StringBuilder sb = new StringBuilder();
boolean hasSquareOrRoot = false;
// 生成运算符和操作数
for (int i = 0; i < operandsCount; i++) {
int num = random.nextInt(100) + 1; // 生成1-100之间的数字
// 每个操作数有概率平方或开根号
if (random.nextBoolean()) {
if (random.nextBoolean()) {
sb.append("(").append(num).append(")^2");
hasSquareOrRoot = true; // 标记是否已经使用了平方或根号
} else {
// 确保根号下的数为正
int rootNumber = random.nextInt(100) + 1; // 始终生成正整数
sb.append("√(").append(rootNumber).append(")");
hasSquareOrRoot = true; // 标记是否已经使用了平方或根号
}
} else {
sb.append(num); // 普通数字
}
// 添加运算符(除最后一个操作数外)
if (i != operandsCount - 1) {
String op = basicOps[random.nextInt(basicOps.length)];
sb.append(" ").append(op).append(" ");
}
}
if (i != operandsCount - 1) {
sb.append(" ").append(BASIC_OPS[RANDOM.nextInt(4)]).append(" ");
// 如果没有平方或根号,强制添加一个
if (!hasSquareOrRoot) {
// 确保根号下的数为正
int rootNumber = random.nextInt(100) + 1; // 始终生成正整数
sb.append(" + √(").append(rootNumber).append(")");
}
}
}
// 如果多操作数情况下没有出现平方或根号,强制追加一个根号项
if (!hasSquareOrRoot) {
sb.append(" + ").append(generateSquareOrRoot());
return sb.toString();
}
return sb.toString();
}
/**
*
* <p> 50% 50% </p>
*
* @return
*/
private String generateOperand() {
int num = RANDOM.nextInt(100) + 1;
if (RANDOM.nextBoolean()) {
return RANDOM.nextBoolean()
? "(" + num + ")^2"
: "√(" + (RANDOM.nextInt(100) + 1) + ")";
}
return String.valueOf(num);
}
/**
*
*
* @return
*/
private String generateSquareOrRoot() {
return RANDOM.nextBoolean()
? "(" + (RANDOM.nextInt(100) + 1) + ")^2"
: "√(" + (RANDOM.nextInt(100) + 1) + ")";
}
}

@ -1,74 +1,51 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
/**
* 使
*
*/
public class QuestionGenerator {
private static final int MAX_TRY_MULTIPLIER = 100;
private User user;
private String role;
/** 当前用户对象。 */
private final User user;
/** 当前用户角色(小学/初中/高中)。 */
private final String role;
/**
*
*
* @param user
* @param role
*/
public QuestionGenerator(User user, String role) {
this.user = user;
this.role = role;
}
/**
*
*
* @param questionCount
* @throws IOException
*/
public void generateQuestions(int questionCount) throws IOException {
// 获取策略
QuestionStrategy strategy = QuestionStrategyFactory.getStrategy(role);
// 读取该用户所有历史题目
Set<String> existingQuestions =
FileUtils.loadExistingQuestions(user.getUsername());
Set<String> newQuestionsSet = new HashSet<>();
List<MathQuestion> questions = new ArrayList<>();
System.out.println("以下为生成的题目列表:\n");
public QuestionGenerator(User user, String role) {
this.user = user;
this.role = role;
}
int i = 1;
int tryCount = 0;
while (questions.size() < questionCount) {
String questionText = strategy.generateQuestion();
tryCount++;
//这里是为了进行题目的查重
if (existingQuestions.contains(questionText)
|| newQuestionsSet.contains(questionText)) {
// 如果题库太小可能死循环,这里设置最大尝试次数
if (tryCount > questionCount * MAX_TRY_MULTIPLIER) {
System.out.println("题库不足,无法生成足够不重复的题目");
break;
public void generateQuestions(int questionCount) throws IOException {
// 获取策略
QuestionStrategy strategy = QuestionStrategyFactory.getStrategy(role);
// 读出该用户所有历史题目
Set<String> existingQuestions = FileUtils.loadExistingQuestions(user.getUsername());
Set<String> newQuestionsSet = new HashSet<>();
List<MathQuestion> questions = new ArrayList<>();
System.out.println("以下为生成的题目列表:\n");
int i = 1;
int tryCount = 0;
while (questions.size() < questionCount) {
String questionText = strategy.generateQuestion();
tryCount++;
if (existingQuestions.contains(questionText) || newQuestionsSet.contains(questionText)) {
// 如果题库太小可能死循环,这里设置最大尝试次数
if (tryCount > questionCount * 100) {
System.out.println("题库不足,无法生成足够不重复的题目");
break;
}
continue;
}
newQuestionsSet.add(questionText);
MathQuestion question = new MathQuestion(i, questionText);
questions.add(question);
System.out.println("题号 " + i + "" + questionText + "\n");
i++;
}
continue;
}
newQuestionsSet.add(questionText);
MathQuestion question = new MathQuestion(i, questionText);
questions.add(question);
System.out.println("题号 " + i + "" + questionText + "\n");
i++;
String fileName = FileUtils.generateFileName();
FileUtils.saveQuestionsToFile(user.getUsername(), fileName, questions);
}
String fileName = FileUtils.generateFileName();
FileUtils.saveQuestionsToFile(user.getUsername(), fileName, questions);
}
}

@ -0,0 +1,26 @@
import java.util.HashSet;
import java.util.Set;
/**
*
*/
public class QuestionRepository {
private Set<String> generatedQuestions = new HashSet<>();
/**
*
* @param question
* @return
*/
public boolean isDuplicate(String question) {
return generatedQuestions.contains(question);
}
/**
*
* @param question
*/
public void addQuestion(String question) {
generatedQuestions.add(question);
}
}

@ -1,12 +1,6 @@
/**
*
*
*/
public interface QuestionStrategy {
/**
*
*
* @return
*/
String generateQuestion();
String generateQuestion();
}

@ -1,21 +1,17 @@
/**
*
* role
*/
public class QuestionStrategyFactory {
/**
*
*
* @param role
* @return
* @throws IllegalArgumentException
*/
public static QuestionStrategy getStrategy(String role) {
return switch (role) {
case "小学" -> new ElementaryQuestionStrategy();
case "初中" -> new MiddleSchoolQuestionStrategy();
case "高中" -> new HighSchoolQuestionStrategy();
default -> throw new IllegalArgumentException("未知的角色: " + role);
};
}
public static QuestionStrategy getStrategy(String role) {
switch (role) {
case "小学":
return new ElementaryQuestionStrategy();
case "初中":
return new MiddleSchoolQuestionStrategy();
case "高中":
return new HighSchoolQuestionStrategy();
default:
throw new IllegalArgumentException("未知的角色: " + role);
}
}
}

@ -1,67 +1,52 @@
/**
*
*
*/
public class User {
private final String username;
private final String password;
private final String role;
private String username;
private String password;
private String role;
/**
*
*
* @param username
* @param password
* @param role
*/
public User(String username, String password, String role) {
this.username = username;
this.password = password;
this.role = role;
}
// 构造方法
public User(String username, String password, String role) {
this.username = username;
this.password = password;
this.role = role;
}
/**
*
*
* @param username
* @param password
* @return {@code null}
*/
public static User login(String username, String password) {
String[][] users = {
{"张三1", "123", "小学"},
{"张三2", "123", "小学"},
{"张三3", "123", "小学"},
{"李四1", "123", "初中"},
{"李四2", "123", "初中"},
{"李四3", "123", "初中"},
{"王五1", "123", "高中"},
{"王五2", "123", "高中"},
{"王五3", "123", "高中"}
};
/**
*
* @param username
* @param password
* @return null
*/
public static User login(String username, String password) {
String[][] users = {
{"张三1", "123", "小学"}, {"张三2", "123", "小学"}, {"张三3", "123", "小学"},
{"李四1", "123", "初中"}, {"李四2", "123", "初中"}, {"李四3", "123", "初中"},
{"王五1", "123", "高中"}, {"王五2", "123", "高中"}, {"王五3", "123", "高中"}
};
for (String[] user : users) {
if (user[0].equals(username) && user[1].equals(password)) {
return new User(username, password, user[2]);
}
for (String[] user : users) {
if (user[0].equals(username) && user[1].equals(password)) {
return new User(username, password, user[2]);
}
}
return null;
}
return null;
}
/**
*
*
* @return
*/
public String getRole() {
return this.role;
}
/**
*
* @return
*/
public String getRole() {
return this.role;
}
/**
*
*
* @return
*/
public String getUsername() {
return this.username;
}
/**
*
* @return
*/
public String getUsername() {
return this.username;
}
}

@ -0,0 +1,20 @@
题号 1: 20 + 66
题号 2: 50 * 87
题号 3: 68 - 11
题号 4: 69 + 56
题号 5: 74 + 30
题号 6: 86 - 48
题号 7: 68 / 41
题号 8: 71 / 49
题号 9: 62 / 41
题号 10: 62 - 51

@ -0,0 +1,20 @@
题号 1: 10 - 50
题号 2: 61 * 30
题号 3: 93 * 5
题号 4: 80 / 2
题号 5: 16 - 52
题号 6: 38 + 8
题号 7: 38 - 69
题号 8: 9 * 6
题号 9: 31 - 47
题号 10: 28 * 26

@ -0,0 +1,24 @@
题号 1: 43 - 38
题号 2: 59 - 92
题号 3: 61 - 21
题号 4: 11 / 39
题号 5: 56 * 2
题号 6: 65 - 44
题号 7: 41 + 67
题号 8: 71 / 65
题号 9: 76 - 73
题号 10: 36 - 55
题号 11: 12 + 10
题号 12: 77 / 85

@ -0,0 +1,20 @@
题号 1: 100 - 61
题号 2: 78 - 41
题号 3: 85 - 72
题号 4: 13 - 52
题号 5: 17 / 38
题号 6: 34 / 8
题号 7: 96 * 45
题号 8: 9 - 89
题号 9: 98 - 50
题号 10: 11 / 65

@ -0,0 +1,20 @@
题号 1: 65 / 37
题号 2: 48 / 79
题号 3: 37 - 92
题号 4: 20 / 19
题号 5: 63 * 31
题号 6: 27 / 33
题号 7: 59 + 30
题号 8: 34 + 94
题号 9: 96 * 91
题号 10: 26 * 45

@ -0,0 +1,20 @@
题号 1: tan(45)
题号 2: cos(36)
题号 3: tan(17)
题号 4: cos(49)
题号 5: cos(83)
题号 6: cos(82)
题号 7: cos(82)
题号 8: cos(21)
题号 9: tan(38)
题号 10: tan(84)

@ -0,0 +1,26 @@
题号 1: cos(59)
题号 2: tan(75)
题号 3: cos(87)
题号 4: cos(67)
题号 5: cos(19)
题号 6: tan(9)
题号 7: cos(65)
题号 8: tan(84)
题号 9: tan(69)
题号 10: sin(83)
题号 11: tan(17)
题号 12: sin(35)
题号 13: cos(37)
Loading…
Cancel
Save