|
|
|
@ -14,15 +14,8 @@ import java.util.List;
|
|
|
|
import java.util.Random;
|
|
|
|
import java.util.Random;
|
|
|
|
import java.util.Set;
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 题目生成器,负责生成不同难度的数学题目。
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public class QuestionMaker {
|
|
|
|
public class QuestionMaker {
|
|
|
|
private static final int MAX_ATTEMPTS = 100;
|
|
|
|
private static final int max_attempts=100;
|
|
|
|
private static final String QUESTIONS_DIR = "questions";
|
|
|
|
|
|
|
|
private static final String FILE_SEPARATOR = File.separator;
|
|
|
|
|
|
|
|
private static final int OPTION_COUNT = 4;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Student student;
|
|
|
|
private Student student;
|
|
|
|
private final Random random;
|
|
|
|
private final Random random;
|
|
|
|
|
|
|
|
|
|
|
|
@ -30,377 +23,265 @@ public class QuestionMaker {
|
|
|
|
this.random = new Random();
|
|
|
|
this.random = new Random();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
public List<Question> makeQuestions(Grade grade,
|
|
|
|
* 生成指定数量和难度的题目。
|
|
|
|
int sum, String email) {
|
|
|
|
*
|
|
|
|
startStudent(grade,email);
|
|
|
|
* @param grade 题目难度等级
|
|
|
|
|
|
|
|
* @param sum 题目数量
|
|
|
|
|
|
|
|
* @param email 用户邮箱
|
|
|
|
|
|
|
|
* @return 题目列表
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public List<Question> makeQuestions(Grade grade, int sum, String email) {
|
|
|
|
|
|
|
|
initializeStudent(grade, email);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<Question> questions = new ArrayList<>();
|
|
|
|
List<Question> questions = new ArrayList<>();
|
|
|
|
Set<String> existingQuestions = loadQuestions(email);
|
|
|
|
Set<String> exQuestions=loadQuestions(email);
|
|
|
|
Set<String> currentQuestions = new HashSet<>();
|
|
|
|
Set<String> currQuestions=new HashSet<>();
|
|
|
|
int attempt = 0;
|
|
|
|
int attempt=0;
|
|
|
|
|
|
|
|
|
|
|
|
while (questions.size() < sum && attempt < MAX_ATTEMPTS) {
|
|
|
|
while(questions.size()<sum && attempt<max_attempts){
|
|
|
|
String expression = student.makeOneQuestion();
|
|
|
|
String expression=student.makeOneQuestion();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(!exQuestions.contains(expression) &&
|
|
|
|
|
|
|
|
!currQuestions.contains(expression)) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
double result = checkExpression(expression,grade);
|
|
|
|
|
|
|
|
Question question = createQuestion(expression, result);
|
|
|
|
|
|
|
|
questions.add(question);
|
|
|
|
|
|
|
|
exQuestions.add(expression);
|
|
|
|
|
|
|
|
currQuestions.add(expression);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
|
|
|
|
if (isUniqueExpression(expression, existingQuestions, currentQuestions)) {
|
|
|
|
}
|
|
|
|
tryAddQuestion(expression, grade, questions, existingQuestions,
|
|
|
|
|
|
|
|
currentQuestions);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
attempt++;
|
|
|
|
attempt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
saveQuestions(email,new ArrayList<>(currQuestions));
|
|
|
|
saveQuestions(email, new ArrayList<>(currentQuestions));
|
|
|
|
|
|
|
|
return questions;
|
|
|
|
return questions;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void initializeStudent(Grade grade, String email) {
|
|
|
|
private void startStudent(Grade grade, String email) {
|
|
|
|
String path = QUESTIONS_DIR + FILE_SEPARATOR + email;
|
|
|
|
String path="questions"+File.separator+email;
|
|
|
|
|
|
|
|
|
|
|
|
switch (grade) {
|
|
|
|
switch(grade){
|
|
|
|
case primary:
|
|
|
|
case primary:
|
|
|
|
student = new PrimaryMaker("primary", "", path);
|
|
|
|
student=new PrimaryMaker("primary","",path);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case middle:
|
|
|
|
case middle:
|
|
|
|
student = new MiddleMaker("middle", "", path);
|
|
|
|
student=new MiddleMaker("middle","",path);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case high:
|
|
|
|
case high:
|
|
|
|
student = new HighMaker("high", "", path);
|
|
|
|
student=new HighMaker("high","",path);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
throw new IllegalArgumentException("Unknown grade: " + grade);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private boolean isUniqueExpression(String expression,
|
|
|
|
private double checkExpression(String expression, Grade grade) {
|
|
|
|
Set<String> existingQuestions, Set<String> currentQuestions) {
|
|
|
|
String expressions=expression.replace(" ","");
|
|
|
|
return !existingQuestions.contains(expression)
|
|
|
|
|
|
|
|
&& !currentQuestions.contains(expression);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void tryAddQuestion(String expression, Grade grade,
|
|
|
|
|
|
|
|
List<Question> questions, Set<String> existingQuestions,
|
|
|
|
|
|
|
|
Set<String> currentQuestions) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
double result = evaluateExpression(expression, grade);
|
|
|
|
|
|
|
|
Question question = createQuestion(expression, result);
|
|
|
|
|
|
|
|
questions.add(question);
|
|
|
|
|
|
|
|
existingQuestions.add(expression);
|
|
|
|
|
|
|
|
currentQuestions.add(expression);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
// Skip invalid expressions
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private double evaluateExpression(String expression, Grade grade) {
|
|
|
|
|
|
|
|
String processedExpression = preprocessExpression(expression, grade);
|
|
|
|
|
|
|
|
return parseAndEvaluate(processedExpression);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private String preprocessExpression(String expression, Grade grade) {
|
|
|
|
|
|
|
|
String result = expression.replace(" ", "");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (grade == Grade.middle) {
|
|
|
|
if (grade == Grade.middle) {
|
|
|
|
result = preprocessMiddleSchoolExpression(result);
|
|
|
|
// 初中:处理平方和平方根
|
|
|
|
|
|
|
|
// 先处理括号内的平方:(5)^2 -> (5*5)
|
|
|
|
|
|
|
|
expressions = expressions.replaceAll("\\((\\d+)\\)\\^2", "($1*$1)");
|
|
|
|
|
|
|
|
// 处理普通数字的平方:5^2 -> (5*5)
|
|
|
|
|
|
|
|
expressions = expressions.replaceAll("(\\d+)\\^2", "($1*$1)");
|
|
|
|
|
|
|
|
// 处理平方根:√16 -> Math.sqrt(16)
|
|
|
|
|
|
|
|
expressions = expressions.replaceAll("√(\\d+)", "Math.sqrt($1)");
|
|
|
|
} else if (grade == Grade.high) {
|
|
|
|
} else if (grade == Grade.high) {
|
|
|
|
result = preprocessHighSchoolExpression(result);
|
|
|
|
// 高中:处理三角函数
|
|
|
|
|
|
|
|
expressions = expressions.replaceAll("sin\\((\\d+)\\)", "Math.sin(Math.toRadians($1))");
|
|
|
|
|
|
|
|
expressions = expressions.replaceAll("cos\\((\\d+)\\)", "Math.cos(Math.toRadians($1))");
|
|
|
|
|
|
|
|
expressions = expressions.replaceAll("tan\\((\\d+)\\)", "Math.tan(Math.toRadians($1))");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return evaluation(expressions);
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private String preprocessMiddleSchoolExpression(String expression) {
|
|
|
|
private double evaluation(String expressions) {
|
|
|
|
// Handle squares: (5)^2 -> (5*5) and 5^2 -> (5*5)
|
|
|
|
return new Object(){
|
|
|
|
String result = expression.replaceAll("\\((\\d+)\\)\\^2", "($1*$1)");
|
|
|
|
int pos=-1;
|
|
|
|
result = result.replaceAll("(\\d+)\\^2", "($1*$1)");
|
|
|
|
int ch;
|
|
|
|
// Handle square roots: √16 -> Math.sqrt(16)
|
|
|
|
void nextChar() {
|
|
|
|
result = result.replaceAll("√(\\d+)", "Math.sqrt($1)");
|
|
|
|
ch = (++pos < expressions.length()) ? expressions.charAt(pos) : -1;
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private String preprocessHighSchoolExpression(String expression) {
|
|
|
|
|
|
|
|
String result = expression.replaceAll("sin\\((\\d+)\\)",
|
|
|
|
|
|
|
|
"Math.sin(Math.toRadians($1))");
|
|
|
|
|
|
|
|
result = result.replaceAll("cos\\((\\d+)\\)",
|
|
|
|
|
|
|
|
"Math.cos(Math.toRadians($1))");
|
|
|
|
|
|
|
|
result = result.replaceAll("tan\\((\\d+)\\)",
|
|
|
|
|
|
|
|
"Math.tan(Math.toRadians($1))");
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private double parseAndEvaluate(String expression) {
|
|
|
|
|
|
|
|
return new ExpressionParser(expression).parse();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static class ExpressionParser {
|
|
|
|
|
|
|
|
private final String expression;
|
|
|
|
|
|
|
|
private int pos = -1;
|
|
|
|
|
|
|
|
private int ch;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ExpressionParser(String expression) {
|
|
|
|
|
|
|
|
this.expression = expression;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double parse() {
|
|
|
|
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
double result = parseExpression();
|
|
|
|
|
|
|
|
if (pos < expression.length()) {
|
|
|
|
|
|
|
|
throw new RuntimeException("Unexpected: " + (char) ch);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void nextChar() {
|
|
|
|
|
|
|
|
ch = (++pos < expression.length()) ? expression.charAt(pos) : -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean eat(int charToEat) {
|
|
|
|
boolean eat(int charToEat) {
|
|
|
|
while (ch == ' ') {
|
|
|
|
while (ch == ' ') {
|
|
|
|
nextChar();
|
|
|
|
nextChar();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ch == charToEat) {
|
|
|
|
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private double parseExpression() {
|
|
|
|
|
|
|
|
double x = parseTerm();
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
if (eat('+')) {
|
|
|
|
|
|
|
|
x += parseTerm();
|
|
|
|
|
|
|
|
} else if (eat('-')) {
|
|
|
|
|
|
|
|
x -= parseTerm();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return x;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ch == charToEat) {
|
|
|
|
}
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
private double parseTerm() {
|
|
|
|
|
|
|
|
double x = parseFactor();
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
if (eat('*')) {
|
|
|
|
|
|
|
|
x *= parseFactor();
|
|
|
|
|
|
|
|
} else if (eat('/')) {
|
|
|
|
|
|
|
|
x /= parseFactor();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return x;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private double parseFactor() {
|
|
|
|
|
|
|
|
if (eat('+')) {
|
|
|
|
|
|
|
|
return parseFactor();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eat('-')) {
|
|
|
|
|
|
|
|
return -parseFactor();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int startPos = pos;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (eat('(')) {
|
|
|
|
|
|
|
|
return parseParenthesizedExpression();
|
|
|
|
|
|
|
|
} else if (isDigit()) {
|
|
|
|
|
|
|
|
return parseNumber(startPos);
|
|
|
|
|
|
|
|
} else if (isUpperCaseLetter()) {
|
|
|
|
|
|
|
|
return parseMathFunction(startPos);
|
|
|
|
|
|
|
|
} else if (isLowerCaseLetter()) {
|
|
|
|
|
|
|
|
return parseCustomFunction(startPos);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
throw new RuntimeException("Unexpected: " + (char) ch);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean isDigit() {
|
|
|
|
|
|
|
|
return (ch >= '0' && ch <= '9') || ch == '.';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean isUpperCaseLetter() {
|
|
|
|
double parse() {
|
|
|
|
return ch >= 'A' && ch <= 'Z';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean isLowerCaseLetter() {
|
|
|
|
|
|
|
|
return ch >= 'a' && ch <= 'z';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private double parseParenthesizedExpression() {
|
|
|
|
|
|
|
|
double x = parseExpression();
|
|
|
|
|
|
|
|
eat(')');
|
|
|
|
|
|
|
|
return x;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private double parseNumber(int startPos) {
|
|
|
|
|
|
|
|
while (isDigit()) {
|
|
|
|
|
|
|
|
nextChar();
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
double x = parseExpression();
|
|
|
|
|
|
|
|
if (pos < expressions.length()) {
|
|
|
|
|
|
|
|
throw new RuntimeException("Unexpected: " + (char) ch);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Double.parseDouble(expression.substring(startPos, pos));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private double parseMathFunction(int startPos) {
|
|
|
|
double parseExpression() {
|
|
|
|
while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '.') {
|
|
|
|
double x = parseTerm();
|
|
|
|
nextChar();
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
if (eat('+')) {
|
|
|
|
|
|
|
|
x += parseTerm();
|
|
|
|
|
|
|
|
} else if (eat('-')) {
|
|
|
|
|
|
|
|
x -= parseTerm();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return x;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
String func = expression.substring(startPos, pos);
|
|
|
|
|
|
|
|
eat('(');
|
|
|
|
|
|
|
|
double x = parseExpression();
|
|
|
|
|
|
|
|
eat(')');
|
|
|
|
|
|
|
|
return applyMathFunction(func, x);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private double parseCustomFunction(int startPos) {
|
|
|
|
double parseTerm() {
|
|
|
|
while (ch >= 'a' && ch <= 'z') {
|
|
|
|
double x = parseFactor();
|
|
|
|
nextChar();
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
if (eat('*')) {
|
|
|
|
|
|
|
|
x *= parseFactor();
|
|
|
|
|
|
|
|
} else if (eat('/')) {
|
|
|
|
|
|
|
|
x /= parseFactor();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return x;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
String function = expression.substring(startPos, pos);
|
|
|
|
|
|
|
|
double x = parseFactor();
|
|
|
|
|
|
|
|
return applyCustomFunction(function, x);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private double applyMathFunction(String func, double value) {
|
|
|
|
double parseFactor() {
|
|
|
|
switch (func) {
|
|
|
|
if (eat('+')) {
|
|
|
|
case "Math.sqrt":
|
|
|
|
return parseFactor();
|
|
|
|
return Math.sqrt(value);
|
|
|
|
}
|
|
|
|
case "Math.sin":
|
|
|
|
if (eat('-')) {
|
|
|
|
return Math.sin(value);
|
|
|
|
return -parseFactor();
|
|
|
|
case "Math.cos":
|
|
|
|
}
|
|
|
|
return Math.cos(value);
|
|
|
|
|
|
|
|
case "Math.tan":
|
|
|
|
double x;
|
|
|
|
return Math.tan(value);
|
|
|
|
int startPos = this.pos;
|
|
|
|
case "Math.toRadians":
|
|
|
|
if (eat('(')) {
|
|
|
|
return Math.toRadians(value);
|
|
|
|
x = parseExpression();
|
|
|
|
default:
|
|
|
|
eat(')');
|
|
|
|
throw new RuntimeException("Unknown function: " + func);
|
|
|
|
} else if ((ch >= '0' && ch <= '9') || ch == '.') {
|
|
|
|
}
|
|
|
|
while ((ch >= '0' && ch <= '9') || ch == '.') {
|
|
|
|
}
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
x = Double.parseDouble(expressions.substring(startPos, this.pos));
|
|
|
|
|
|
|
|
} else if (ch >= 'A' && ch <= 'Z') {
|
|
|
|
|
|
|
|
// 处理 Math.xxx 函数
|
|
|
|
|
|
|
|
while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '.') {
|
|
|
|
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
String func = expressions.substring(startPos, this.pos);
|
|
|
|
|
|
|
|
eat('(');
|
|
|
|
|
|
|
|
x = parseExpression();
|
|
|
|
|
|
|
|
eat(')');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (func.equals("Math.sqrt")) {
|
|
|
|
|
|
|
|
x = Math.sqrt(x);
|
|
|
|
|
|
|
|
} else if (func.equals("Math.sin")) {
|
|
|
|
|
|
|
|
x = Math.sin(x);
|
|
|
|
|
|
|
|
} else if (func.equals("Math.cos")) {
|
|
|
|
|
|
|
|
x = Math.cos(x);
|
|
|
|
|
|
|
|
} else if (func.equals("Math.tan")) {
|
|
|
|
|
|
|
|
x = Math.tan(x);
|
|
|
|
|
|
|
|
} else if (func.equals("Math.toRadians")) {
|
|
|
|
|
|
|
|
x = Math.toRadians(x);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
throw new RuntimeException("Unknown function: " + func);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (ch >= 'a' && ch <= 'z') {
|
|
|
|
|
|
|
|
while (ch >= 'a' && ch <= 'z') {
|
|
|
|
|
|
|
|
nextChar();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
String function = expressions.substring(startPos, this.pos);
|
|
|
|
|
|
|
|
x = parseFactor();
|
|
|
|
|
|
|
|
if (function.equals("sqrt")) {
|
|
|
|
|
|
|
|
x = Math.sqrt(x);
|
|
|
|
|
|
|
|
} else if (function.equals("sin")) {
|
|
|
|
|
|
|
|
x = Math.sin(x);
|
|
|
|
|
|
|
|
} else if (function.equals("cos")) {
|
|
|
|
|
|
|
|
x = Math.cos(x);
|
|
|
|
|
|
|
|
} else if (function.equals("tan")) {
|
|
|
|
|
|
|
|
x = Math.tan(x);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
throw new RuntimeException("Unknown function: " + function);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
throw new RuntimeException("Unexpected: " + (char) ch);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private double applyCustomFunction(String function, double value) {
|
|
|
|
return x;
|
|
|
|
switch (function) {
|
|
|
|
|
|
|
|
case "sqrt":
|
|
|
|
|
|
|
|
return Math.sqrt(value);
|
|
|
|
|
|
|
|
case "sin":
|
|
|
|
|
|
|
|
return Math.sin(value);
|
|
|
|
|
|
|
|
case "cos":
|
|
|
|
|
|
|
|
return Math.cos(value);
|
|
|
|
|
|
|
|
case "tan":
|
|
|
|
|
|
|
|
return Math.tan(value);
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
throw new RuntimeException("Unknown function: " + function);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}.parse();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private Question createQuestion(String expression, double result) {
|
|
|
|
private Question createQuestion(String expression, double result) {
|
|
|
|
String text = "计算: " + expression + " = ? ";
|
|
|
|
String text="计算: "+expression+" = ? ";
|
|
|
|
double[] options = generateOptions(result);
|
|
|
|
double[] options=new double[4];
|
|
|
|
int correctIndex = random.nextInt(OPTION_COUNT);
|
|
|
|
int currIndex=random.nextInt(4);
|
|
|
|
options[correctIndex] = result;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String[] optionStrings = formatOptions(options);
|
|
|
|
|
|
|
|
return new Question(expression, text, optionStrings, correctIndex);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private double[] generateOptions(double correctAnswer) {
|
|
|
|
options[currIndex]=result;
|
|
|
|
double[] options = new double[OPTION_COUNT];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < OPTION_COUNT; i++) {
|
|
|
|
for(int i=0;i<4;i++) {
|
|
|
|
double offset = random.nextDouble() * 20 - 10;
|
|
|
|
if(i!=currIndex) {
|
|
|
|
if (Math.abs(offset) < 1) {
|
|
|
|
double offset=(random.nextDouble()*20-10);
|
|
|
|
offset = random.nextBoolean() ? 5 : -5;
|
|
|
|
if(Math.abs(offset)<1) {
|
|
|
|
|
|
|
|
offset=random.nextBoolean() ? 5 : -5 ;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
options[i]=offset+result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
options[i] = offset + correctAnswer;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return options;
|
|
|
|
String[] optionStrings=new String[4];
|
|
|
|
}
|
|
|
|
for(int i=0;i<4;i++) {
|
|
|
|
|
|
|
|
optionStrings[i]=String.format("%c. %.2f",
|
|
|
|
private String[] formatOptions(double[] options) {
|
|
|
|
|
|
|
|
String[] optionStrings = new String[OPTION_COUNT];
|
|
|
|
|
|
|
|
for (int i = 0; i < OPTION_COUNT; i++) {
|
|
|
|
|
|
|
|
optionStrings[i] = String.format("%c. %.2f",
|
|
|
|
|
|
|
|
(char) ('A' + i), options[i]);
|
|
|
|
(char) ('A' + i), options[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return optionStrings;
|
|
|
|
|
|
|
|
|
|
|
|
return new Question(expression, text, optionStrings, currIndex);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private Set<String> loadQuestions(String email) {
|
|
|
|
private Set<String> loadQuestions(String email) {
|
|
|
|
Set<String> questions = new HashSet<>();
|
|
|
|
Set<String> questions=new HashSet<>();
|
|
|
|
File userDir = getUserDirectory(email);
|
|
|
|
File userDir = new File("questions" + File.separator + email);
|
|
|
|
|
|
|
|
|
|
|
|
if (!userDir.exists() || !userDir.isDirectory()) {
|
|
|
|
if(!userDir.exists()||!userDir.isDirectory()) {
|
|
|
|
return questions;
|
|
|
|
return questions;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
File[] files = userDir.listFiles((dir, name) -> name.endsWith(".txt"));
|
|
|
|
File[] files = userDir.listFiles((dir, name) -> name.endsWith(".txt"));
|
|
|
|
if (files == null) {
|
|
|
|
if(files==null) {
|
|
|
|
return questions;
|
|
|
|
return questions;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (File file : files) {
|
|
|
|
for(File file:files) {
|
|
|
|
loadQuestionsFromFile(file, questions);
|
|
|
|
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
|
|
|
|
}
|
|
|
|
String line;
|
|
|
|
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
return questions;
|
|
|
|
line = line.trim();
|
|
|
|
}
|
|
|
|
if (line.matches("\\d+\\.\\s+.*")) {
|
|
|
|
|
|
|
|
String question = line.substring(line.indexOf('.') + 1).trim();
|
|
|
|
private void loadQuestionsFromFile(File file, Set<String> questions) {
|
|
|
|
questions.add(question);
|
|
|
|
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
|
|
|
|
}
|
|
|
|
String line;
|
|
|
|
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
|
|
|
|
String question = extractQuestion(line);
|
|
|
|
|
|
|
|
if (question != null) {
|
|
|
|
|
|
|
|
questions.add(question);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
|
|
System.err.println("读取文件失败: " + e.getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
|
|
System.err.println("读取文件失败: " + e.getMessage());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private String extractQuestion(String line) {
|
|
|
|
|
|
|
|
line = line.trim();
|
|
|
|
|
|
|
|
if (line.matches("\\d+\\.\\s+.*")) {
|
|
|
|
|
|
|
|
return line.substring(line.indexOf('.') + 1).trim();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
return questions;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void saveQuestions(String email, List<String> questions) {
|
|
|
|
private void saveQuestions(String email, List<String> questions) {
|
|
|
|
File userDir = getUserDirectory(email);
|
|
|
|
File userDir = new File("questions" + File.separator + email);
|
|
|
|
ensureDirectoryExists(userDir);
|
|
|
|
if(!userDir.exists()) {
|
|
|
|
|
|
|
|
if(!userDir.mkdirs()) {
|
|
|
|
String fileName = generateFileName();
|
|
|
|
|
|
|
|
File file = new File(userDir, fileName);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
writeQuestionsToFile(file, questions);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private File getUserDirectory(String email) {
|
|
|
|
|
|
|
|
return new File(QUESTIONS_DIR + FILE_SEPARATOR + email);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void ensureDirectoryExists(File directory) {
|
|
|
|
|
|
|
|
if (!directory.exists()) {
|
|
|
|
|
|
|
|
if (!directory.mkdirs()) {
|
|
|
|
|
|
|
|
System.err.println("创建目录失败");
|
|
|
|
System.err.println("创建目录失败");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private String generateFileName() {
|
|
|
|
|
|
|
|
LocalDateTime now = LocalDateTime.now();
|
|
|
|
LocalDateTime now = LocalDateTime.now();
|
|
|
|
return now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss")) + ".txt";
|
|
|
|
String fileName = now.format(
|
|
|
|
}
|
|
|
|
DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss")) + ".txt";
|
|
|
|
|
|
|
|
File file = new File(userDir, fileName);
|
|
|
|
|
|
|
|
|
|
|
|
private void writeQuestionsToFile(File file, List<String> questions) {
|
|
|
|
|
|
|
|
try (PrintWriter writer = new PrintWriter(new FileWriter(file))) {
|
|
|
|
try (PrintWriter writer = new PrintWriter(new FileWriter(file))) {
|
|
|
|
for (int i = 0; i < questions.size(); i++) {
|
|
|
|
for (int i = 0; i < questions.size(); i++) {
|
|
|
|
writer.println((i + 1) + ". " + questions.get(i));
|
|
|
|
writer.println((i + 1) + ". " + questions.get(i));
|
|
|
|
|