|
|
|
|
@ -0,0 +1,747 @@
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <set>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <ctime>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <dirent.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
#include <direct.h>
|
|
|
|
|
#define MKDIR(path) _mkdir(path)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 题目生成器接口:用于生成不同类型的数学题目
|
|
|
|
|
class IQuestionGenerator {
|
|
|
|
|
public:
|
|
|
|
|
virtual ~IQuestionGenerator() = default;
|
|
|
|
|
virtual string generateQuestion() = 0;
|
|
|
|
|
virtual string getType() const = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 用户管理器接口:用于管理用户登录、切换类型、记录题目等
|
|
|
|
|
class IUserManager {
|
|
|
|
|
public:
|
|
|
|
|
virtual ~IUserManager() = default;
|
|
|
|
|
virtual bool login(const string& username, const string& password) = 0;
|
|
|
|
|
virtual void logout() = 0;
|
|
|
|
|
virtual bool switchType(const string& type) = 0;
|
|
|
|
|
virtual bool isQuestionDuplicate(const string& question) = 0;
|
|
|
|
|
virtual void addQuestionToUserRecord(const string& question) = 0;
|
|
|
|
|
virtual string getCurrentUser() const = 0;
|
|
|
|
|
virtual string getCurrentType() const = 0;
|
|
|
|
|
virtual bool getIsLoggedIn() const = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 文件操作接口:用于处理文件和目录相关操作
|
|
|
|
|
class IFileManager {
|
|
|
|
|
public:
|
|
|
|
|
virtual ~IFileManager() = default;
|
|
|
|
|
virtual bool createDirectory(const string& path) = 0;
|
|
|
|
|
virtual bool directoryExists(const string& path) = 0;
|
|
|
|
|
virtual vector<string> getFilesInDirectory(const string& path) = 0;
|
|
|
|
|
virtual string trim(const string& str) = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 账户结构体:存储用户的用户名、密码和题目类型
|
|
|
|
|
struct Account {
|
|
|
|
|
string username;
|
|
|
|
|
string password;
|
|
|
|
|
string type;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 文件操作具体实现:实现文件和目录的创建、判断、遍历等功能
|
|
|
|
|
class FileManager : public IFileManager {
|
|
|
|
|
public:
|
|
|
|
|
bool createDirectory(const string& path) override {
|
|
|
|
|
return MKDIR(path.c_str()) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool directoryExists(const string& path) override {
|
|
|
|
|
DIR* dir = opendir(path.c_str());
|
|
|
|
|
if (dir) {
|
|
|
|
|
closedir(dir);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<string> getFilesInDirectory(const string& path) override {
|
|
|
|
|
vector<string> files;
|
|
|
|
|
DIR* dir = opendir(path.c_str());
|
|
|
|
|
if (dir) {
|
|
|
|
|
struct dirent* entry;
|
|
|
|
|
while ((entry = readdir(dir)) != nullptr) {
|
|
|
|
|
string filename = entry->d_name;
|
|
|
|
|
if (filename != "." && filename != "..") {
|
|
|
|
|
files.push_back(filename);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
closedir(dir);
|
|
|
|
|
}
|
|
|
|
|
return files;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string trim(const string& str) override {
|
|
|
|
|
size_t start = str.find_first_not_of(" \t\n\r");
|
|
|
|
|
size_t end = str.find_last_not_of(" \t\n\r");
|
|
|
|
|
if (start == string::npos || end == string::npos) {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
return str.substr(start, end - start + 1);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 题目生成器基类:为题目生成器提供公共的随机数和表达式构建方法
|
|
|
|
|
class BaseQuestionGenerator : public IQuestionGenerator {
|
|
|
|
|
protected:
|
|
|
|
|
int randomNumber(int min, int max) {
|
|
|
|
|
return rand() % (max - min + 1) + min;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool shouldAddParentheses(int operandCount) {
|
|
|
|
|
if (operandCount <= 2) return false;
|
|
|
|
|
return randomNumber(0, 1) == 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string addParenthesesToExpression(const vector<int>& operands, const vector<char>& operators) {
|
|
|
|
|
if (operands.size() <= 2) {
|
|
|
|
|
return buildExpression(operands, operators);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int parenthesesPos = randomNumber(0, operands.size() - 2);
|
|
|
|
|
|
|
|
|
|
string expression;
|
|
|
|
|
for (size_t i = 0; i < operands.size(); i++) {
|
|
|
|
|
if (i == parenthesesPos) {
|
|
|
|
|
expression += "(";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expression += to_string(operands[i]);
|
|
|
|
|
|
|
|
|
|
if (i == parenthesesPos + 1) {
|
|
|
|
|
expression += ")";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i < operators.size()) {
|
|
|
|
|
expression += " " + string(1, operators[i]) + " ";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return expression;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string buildExpression(const vector<int>& operands, const vector<char>& operators) {
|
|
|
|
|
string expression = to_string(operands[0]);
|
|
|
|
|
for (size_t i = 0; i < operators.size(); i++) {
|
|
|
|
|
expression += " " + string(1, operators[i]) + " " + to_string(operands[i + 1]);
|
|
|
|
|
}
|
|
|
|
|
return expression;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 小学题目生成器:生成小学难度的数学题目
|
|
|
|
|
class PrimaryQuestionGenerator : public BaseQuestionGenerator {
|
|
|
|
|
public:
|
|
|
|
|
string generateQuestion() override {
|
|
|
|
|
int operandCount = randomNumber(2, 5);
|
|
|
|
|
vector<int> operands;
|
|
|
|
|
vector<char> operators;
|
|
|
|
|
|
|
|
|
|
operands.push_back(randomNumber(1, 100));
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < operandCount - 1; i++) {
|
|
|
|
|
int opType = randomNumber(0, 3);
|
|
|
|
|
int prev = operands.back();
|
|
|
|
|
int next;
|
|
|
|
|
switch (opType) {
|
|
|
|
|
case 0:
|
|
|
|
|
next = randomNumber(1, 100);
|
|
|
|
|
operators.push_back('+');
|
|
|
|
|
operands.push_back(next);
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
next = randomNumber(1, prev);
|
|
|
|
|
operators.push_back('-');
|
|
|
|
|
operands.push_back(next);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
next = randomNumber(1, 100);
|
|
|
|
|
operators.push_back('*');
|
|
|
|
|
operands.push_back(next);
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
{
|
|
|
|
|
next = randomNumber(1, 10);
|
|
|
|
|
int result = randomNumber(1, 10);
|
|
|
|
|
int dividend = next * result;
|
|
|
|
|
operands.back() = dividend;
|
|
|
|
|
operators.push_back('/');
|
|
|
|
|
operands.push_back(next);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shouldAddParentheses(operandCount)) {
|
|
|
|
|
return addParenthesesToExpression(operands, operators);
|
|
|
|
|
} else {
|
|
|
|
|
return buildExpression(operands, operators);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string getType() const override {
|
|
|
|
|
return "小学";
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 初中题目生成器:生成初中难度的数学题目,包含平方和根号
|
|
|
|
|
class MiddleQuestionGenerator : public BaseQuestionGenerator {
|
|
|
|
|
private:
|
|
|
|
|
string addParenthesesToAdvancedExpression(const string& expression, const vector<string>& operators) {
|
|
|
|
|
if (operators.size() <= 1) return expression;
|
|
|
|
|
|
|
|
|
|
vector<size_t> opPositions;
|
|
|
|
|
size_t pos = 0;
|
|
|
|
|
for (const auto& op : operators) {
|
|
|
|
|
pos = expression.find(op, pos);
|
|
|
|
|
if (pos != string::npos) {
|
|
|
|
|
opPositions.push_back(pos);
|
|
|
|
|
pos += op.length();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opPositions.size() <= 1) return expression;
|
|
|
|
|
|
|
|
|
|
int parenthesesPos = randomNumber(0, opPositions.size() - 2);
|
|
|
|
|
|
|
|
|
|
string result = expression;
|
|
|
|
|
|
|
|
|
|
size_t leftPos = opPositions[parenthesesPos];
|
|
|
|
|
if (parenthesesPos > 0) {
|
|
|
|
|
size_t prevOpEnd = opPositions[parenthesesPos - 1] + operators[parenthesesPos - 1].length();
|
|
|
|
|
size_t numStart = result.find_first_not_of(" ", prevOpEnd);
|
|
|
|
|
leftPos = numStart;
|
|
|
|
|
} else {
|
|
|
|
|
leftPos = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.insert(leftPos, "(");
|
|
|
|
|
|
|
|
|
|
size_t rightPos = opPositions[parenthesesPos + 1];
|
|
|
|
|
size_t numEnd = result.find_first_of("+-*/", rightPos + 1);
|
|
|
|
|
if (numEnd == string::npos) {
|
|
|
|
|
numEnd = result.length();
|
|
|
|
|
} else {
|
|
|
|
|
numEnd--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.insert(numEnd, ")");
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
string generateQuestion() override {
|
|
|
|
|
int operandCount = randomNumber(2, 5);
|
|
|
|
|
vector<int> operands;
|
|
|
|
|
vector<string> operators;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < operandCount; i++) {
|
|
|
|
|
operands.push_back(randomNumber(1, 100));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool hasSquareOrRoot = false;
|
|
|
|
|
|
|
|
|
|
string expression = "";
|
|
|
|
|
for (int i = 0; i < operandCount; i++) {
|
|
|
|
|
if (i > 0) {
|
|
|
|
|
int opType = randomNumber(0, 3);
|
|
|
|
|
switch (opType) {
|
|
|
|
|
case 0: operators.push_back("+"); break;
|
|
|
|
|
case 1: operators.push_back("-"); break;
|
|
|
|
|
case 2: operators.push_back("*"); break;
|
|
|
|
|
case 3: operators.push_back("/"); break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int specialOp = randomNumber(0, 3);
|
|
|
|
|
if ((!hasSquareOrRoot && i == operandCount - 1) || (specialOp == 0 && i < operandCount - 1)) {
|
|
|
|
|
if (randomNumber(0, 1) == 0) {
|
|
|
|
|
expression += to_string(operands[i]) + "²";
|
|
|
|
|
} else {
|
|
|
|
|
expression += "√" + to_string(operands[i]);
|
|
|
|
|
}
|
|
|
|
|
hasSquareOrRoot = true;
|
|
|
|
|
} else {
|
|
|
|
|
expression += to_string(operands[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!hasSquareOrRoot) {
|
|
|
|
|
int pos = randomNumber(0, operandCount - 1);
|
|
|
|
|
size_t numStart = 0;
|
|
|
|
|
for (int i = 0; i < pos; i++) {
|
|
|
|
|
size_t opPos = expression.find_first_of("+-*/", numStart);
|
|
|
|
|
if (opPos != string::npos) {
|
|
|
|
|
numStart = opPos + 3;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (randomNumber(0, 1) == 0) {
|
|
|
|
|
expression.insert(numStart, "(");
|
|
|
|
|
expression.insert(expression.find(' ', numStart), ")²");
|
|
|
|
|
} else {
|
|
|
|
|
expression.insert(numStart, "√(");
|
|
|
|
|
expression.insert(expression.find(' ', numStart), ")");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shouldAddParentheses(operandCount)) {
|
|
|
|
|
expression = addParenthesesToAdvancedExpression(expression, operators);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return expression;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string getType() const override {
|
|
|
|
|
return "初中";
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 高中题目生成器:生成高中难度的数学题目,包含三角函数等
|
|
|
|
|
class HighQuestionGenerator : public BaseQuestionGenerator {
|
|
|
|
|
private:
|
|
|
|
|
string addParenthesesToAdvancedExpression(const string& expression, const vector<string>& operators) {
|
|
|
|
|
if (operators.size() <= 1) return expression;
|
|
|
|
|
|
|
|
|
|
vector<size_t> opPositions;
|
|
|
|
|
size_t pos = 0;
|
|
|
|
|
for (const auto& op : operators) {
|
|
|
|
|
pos = expression.find(op, pos);
|
|
|
|
|
if (pos != string::npos) {
|
|
|
|
|
opPositions.push_back(pos);
|
|
|
|
|
pos += op.length();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opPositions.size() <= 1) return expression;
|
|
|
|
|
|
|
|
|
|
int parenthesesPos = randomNumber(0, opPositions.size() - 2);
|
|
|
|
|
|
|
|
|
|
string result = expression;
|
|
|
|
|
|
|
|
|
|
size_t leftPos = opPositions[parenthesesPos];
|
|
|
|
|
if (parenthesesPos > 0) {
|
|
|
|
|
size_t prevOpEnd = opPositions[parenthesesPos - 1] + operators[parenthesesPos - 1].length();
|
|
|
|
|
size_t numStart = result.find_first_not_of(" ", prevOpEnd);
|
|
|
|
|
leftPos = numStart;
|
|
|
|
|
} else {
|
|
|
|
|
leftPos = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.insert(leftPos, "(");
|
|
|
|
|
|
|
|
|
|
size_t rightPos = opPositions[parenthesesPos + 1];
|
|
|
|
|
size_t numEnd = result.find_first_of("+-*/", rightPos + 1);
|
|
|
|
|
if (numEnd == string::npos) {
|
|
|
|
|
numEnd = result.length();
|
|
|
|
|
} else {
|
|
|
|
|
numEnd--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.insert(numEnd, ")");
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
string generateQuestion() override {
|
|
|
|
|
int operandCount = randomNumber(2, 5);
|
|
|
|
|
vector<int> operands;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < operandCount; i++) {
|
|
|
|
|
operands.push_back(randomNumber(1, 100));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool hasTrigonometry = false;
|
|
|
|
|
vector<string> operators;
|
|
|
|
|
|
|
|
|
|
string expression = "";
|
|
|
|
|
for (int i = 0; i < operandCount; i++) {
|
|
|
|
|
if (i > 0) {
|
|
|
|
|
int opType = randomNumber(0, 3);
|
|
|
|
|
switch (opType) {
|
|
|
|
|
case 0: operators.push_back("+"); break;
|
|
|
|
|
case 1: operators.push_back("-"); break;
|
|
|
|
|
case 2: operators.push_back("*"); break;
|
|
|
|
|
case 3: operators.push_back("/"); break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int specialOp = randomNumber(0, 5);
|
|
|
|
|
if ((!hasTrigonometry && i == operandCount - 1) || specialOp >= 2) {
|
|
|
|
|
int opChoice = randomNumber(0, 4);
|
|
|
|
|
switch (opChoice) {
|
|
|
|
|
case 0:
|
|
|
|
|
expression += to_string(operands[i]) + "²";
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
expression += "√" + to_string(operands[i]);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
expression += "sin(" + to_string(operands[i]) + ")";
|
|
|
|
|
hasTrigonometry = true;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
expression += "cos(" + to_string(operands[i]) + ")";
|
|
|
|
|
hasTrigonometry = true;
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
expression += "tan(" + to_string(operands[i]) + ")";
|
|
|
|
|
hasTrigonometry = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
expression += to_string(operands[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!hasTrigonometry) {
|
|
|
|
|
int pos = randomNumber(0, operandCount - 1);
|
|
|
|
|
size_t numStart = 0;
|
|
|
|
|
for (int i = 0; i < pos; i++) {
|
|
|
|
|
size_t opPos = expression.find_first_of("+-*/", numStart);
|
|
|
|
|
if (opPos != string::npos) {
|
|
|
|
|
numStart = opPos + 3;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int trigChoice = randomNumber(0, 2);
|
|
|
|
|
switch (trigChoice) {
|
|
|
|
|
case 0:
|
|
|
|
|
expression.insert(numStart, "sin(");
|
|
|
|
|
expression.insert(expression.find(' ', numStart), ")");
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
expression.insert(numStart, "cos(");
|
|
|
|
|
expression.insert(expression.find(' ', numStart), ")");
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
expression.insert(numStart, "tan(");
|
|
|
|
|
expression.insert(expression.find(' ', numStart), ")");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shouldAddParentheses(operandCount)) {
|
|
|
|
|
expression = addParenthesesToAdvancedExpression(expression, operators);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return expression;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string getType() const override {
|
|
|
|
|
return "高中";
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 用户管理器具体实现:实现用户登录、切换类型、题目去重等功能
|
|
|
|
|
class UserManager : public IUserManager {
|
|
|
|
|
private:
|
|
|
|
|
vector<Account> accounts;
|
|
|
|
|
string currentUser;
|
|
|
|
|
string currentType;
|
|
|
|
|
bool isLoggedIn;
|
|
|
|
|
map<string, set<string>> userQuestions;
|
|
|
|
|
shared_ptr<IFileManager> fileManager;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
UserManager(shared_ptr<IFileManager> fm) : fileManager(fm) {
|
|
|
|
|
isLoggedIn = false;
|
|
|
|
|
currentUser = "";
|
|
|
|
|
currentType = "";
|
|
|
|
|
|
|
|
|
|
accounts = {
|
|
|
|
|
{"张三1", "123", "小学"},
|
|
|
|
|
{"张三2", "123", "小学"},
|
|
|
|
|
{"张三3", "123", "小学"},
|
|
|
|
|
{"李四1", "123", "初中"},
|
|
|
|
|
{"李四2", "123", "初中"},
|
|
|
|
|
{"李四3", "123", "初中"},
|
|
|
|
|
{"王五1", "123", "高中"},
|
|
|
|
|
{"王五2", "123", "高中"},
|
|
|
|
|
{"王五3", "123", "高中"}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (const auto& account : accounts) {
|
|
|
|
|
string folderPath = account.username;
|
|
|
|
|
if (!fileManager->directoryExists(folderPath)) {
|
|
|
|
|
fileManager->createDirectory(folderPath);
|
|
|
|
|
}
|
|
|
|
|
loadUserQuestions(account.username);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void loadUserQuestions(const string& username) {
|
|
|
|
|
userQuestions[username].clear();
|
|
|
|
|
string folderPath = username;
|
|
|
|
|
|
|
|
|
|
if (!fileManager->directoryExists(folderPath)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<string> files = fileManager->getFilesInDirectory(folderPath);
|
|
|
|
|
for (const string& filename : files) {
|
|
|
|
|
if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".txt") {
|
|
|
|
|
string filepath = folderPath + "/" + filename;
|
|
|
|
|
ifstream file(filepath);
|
|
|
|
|
string line;
|
|
|
|
|
while (getline(file, line)) {
|
|
|
|
|
if (!line.empty() && isdigit(line[0])) {
|
|
|
|
|
size_t dotPos = line.find('.');
|
|
|
|
|
if (dotPos != string::npos && dotPos + 2 < line.length()) {
|
|
|
|
|
string question = line.substr(dotPos + 2);
|
|
|
|
|
userQuestions[username].insert(question);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
file.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool login(const string& username, const string& password) override {
|
|
|
|
|
for (const auto& account : accounts) {
|
|
|
|
|
if (account.username == username && account.password == password) {
|
|
|
|
|
currentUser = username;
|
|
|
|
|
currentType = account.type;
|
|
|
|
|
isLoggedIn = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void logout() override {
|
|
|
|
|
isLoggedIn = false;
|
|
|
|
|
currentUser = "";
|
|
|
|
|
currentType = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool switchType(const string& type) override {
|
|
|
|
|
string trimmedType = fileManager->trim(type);
|
|
|
|
|
if (trimmedType == "小学" || trimmedType == "初中" || trimmedType == "高中") {
|
|
|
|
|
currentType = trimmedType;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isQuestionDuplicate(const string& question) override {
|
|
|
|
|
return userQuestions[currentUser].find(question) != userQuestions[currentUser].end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void addQuestionToUserRecord(const string& question) override {
|
|
|
|
|
userQuestions[currentUser].insert(question);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string getCurrentUser() const override { return currentUser; }
|
|
|
|
|
string getCurrentType() const override { return currentType; }
|
|
|
|
|
bool getIsLoggedIn() const override { return isLoggedIn; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 题目生成器工厂:根据类型创建对应的题目生成器
|
|
|
|
|
class QuestionGeneratorFactory {
|
|
|
|
|
public:
|
|
|
|
|
static unique_ptr<IQuestionGenerator> createGenerator(const string& type) {
|
|
|
|
|
if (type == "小学") {
|
|
|
|
|
return make_unique<PrimaryQuestionGenerator>();
|
|
|
|
|
} else if (type == "初中") {
|
|
|
|
|
return make_unique<MiddleQuestionGenerator>();
|
|
|
|
|
} else if (type == "高中") {
|
|
|
|
|
return make_unique<HighQuestionGenerator>();
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 数学卷子生成主类:负责题目生成、保存、用户交互等主流程
|
|
|
|
|
class MathPaperGenerator {
|
|
|
|
|
private:
|
|
|
|
|
shared_ptr<IUserManager> userManager;
|
|
|
|
|
unique_ptr<IQuestionGenerator> questionGenerator;
|
|
|
|
|
shared_ptr<IFileManager> fileManager;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
MathPaperGenerator(shared_ptr<IUserManager> um, shared_ptr<IFileManager> fm)
|
|
|
|
|
: userManager(um), fileManager(fm) {}
|
|
|
|
|
|
|
|
|
|
vector<string> generateQuestions(int count) {
|
|
|
|
|
vector<string> questions;
|
|
|
|
|
set<string> generatedSet;
|
|
|
|
|
int attempts = 0;
|
|
|
|
|
const int MAX_ATTEMPTS = count * 10;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
questionGenerator = QuestionGeneratorFactory::createGenerator(userManager->getCurrentType());
|
|
|
|
|
if (!questionGenerator) {
|
|
|
|
|
cout << "题目生成器创建失败!" << endl;
|
|
|
|
|
return questions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (questions.size() < count && attempts < MAX_ATTEMPTS) {
|
|
|
|
|
string question = questionGenerator->generateQuestion();
|
|
|
|
|
|
|
|
|
|
if (!userManager->isQuestionDuplicate(question) && generatedSet.find(question) == generatedSet.end()) {
|
|
|
|
|
questions.push_back(question);
|
|
|
|
|
generatedSet.insert(question);
|
|
|
|
|
userManager->addQuestionToUserRecord(question);
|
|
|
|
|
}
|
|
|
|
|
attempts++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (questions.size() < count) {
|
|
|
|
|
string question;
|
|
|
|
|
string currentType = userManager->getCurrentType();
|
|
|
|
|
|
|
|
|
|
if (currentType == "小学") {
|
|
|
|
|
question = to_string(rand() % 100 + 1) + " + " + to_string(rand() % 100 + 1);
|
|
|
|
|
} else if (currentType == "初中") {
|
|
|
|
|
question = to_string(rand() % 100 + 1) + "² + " + to_string(rand() % 100 + 1);
|
|
|
|
|
} else {
|
|
|
|
|
question = "sin(" + to_string(rand() % 100 + 1) + ") + " + to_string(rand() % 100 + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!userManager->isQuestionDuplicate(question)) {
|
|
|
|
|
questions.push_back(question);
|
|
|
|
|
userManager->addQuestionToUserRecord(question);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return questions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void saveQuestions(const vector<string>& questions) {
|
|
|
|
|
time_t now = time(nullptr);
|
|
|
|
|
tm* localTime = localtime(&now);
|
|
|
|
|
|
|
|
|
|
stringstream filename;
|
|
|
|
|
filename << setfill('0')
|
|
|
|
|
<< localTime->tm_year + 1900 << "-"
|
|
|
|
|
<< setw(2) << localTime->tm_mon + 1 << "-"
|
|
|
|
|
<< setw(2) << localTime->tm_mday << "-"
|
|
|
|
|
<< setw(2) << localTime->tm_hour << "-"
|
|
|
|
|
<< setw(2) << localTime->tm_min << "-"
|
|
|
|
|
<< setw(2) << localTime->tm_sec << ".txt";
|
|
|
|
|
|
|
|
|
|
string filepath = userManager->getCurrentUser() + "/" + filename.str();
|
|
|
|
|
ofstream file(filepath);
|
|
|
|
|
|
|
|
|
|
if (file.is_open()) {
|
|
|
|
|
for (size_t i = 0; i < questions.size(); i++) {
|
|
|
|
|
file << i + 1 << ". " << questions[i] << "\n";
|
|
|
|
|
if (i < questions.size() - 1) {
|
|
|
|
|
file << "\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
file.close();
|
|
|
|
|
cout << "题目已保存到: " << filepath << endl;
|
|
|
|
|
} else {
|
|
|
|
|
cout << "文件保存失败!" << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void processInput() {
|
|
|
|
|
string input;
|
|
|
|
|
getline(cin, input);
|
|
|
|
|
|
|
|
|
|
input = fileManager->trim(input);
|
|
|
|
|
|
|
|
|
|
if (input == "-1") {
|
|
|
|
|
userManager->logout();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (input.find("切换为") == 0) {
|
|
|
|
|
string type = input.substr(3);
|
|
|
|
|
type = fileManager->trim(type);
|
|
|
|
|
|
|
|
|
|
if (userManager->switchType(type)) {
|
|
|
|
|
cout << "切换成功!当前类型为:" << userManager->getCurrentType() << endl;
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
cout << "请输入小学、初中和高中三个选项中的一个" << endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
processCountInput(input);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void processCountInput(const string& input) {
|
|
|
|
|
try {
|
|
|
|
|
int count = stoi(input);
|
|
|
|
|
if (count >= 10 && count <= 30) {
|
|
|
|
|
vector<string> questions = generateQuestions(count);
|
|
|
|
|
saveQuestions(questions);
|
|
|
|
|
} else {
|
|
|
|
|
cout << "题目数量应在10-30之间" << endl;
|
|
|
|
|
}
|
|
|
|
|
} catch (const exception& e) {
|
|
|
|
|
cout << "请输入有效的数字" << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void run() {
|
|
|
|
|
srand(time(nullptr));
|
|
|
|
|
cout << "=== 中小学数学卷子自动生成程序 ===" << endl;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
if (!userManager->getIsLoggedIn()) {
|
|
|
|
|
cout << "请输入用户名和密码(用空格隔开): ";
|
|
|
|
|
string username, password;
|
|
|
|
|
cin >> username >> password;
|
|
|
|
|
cin.ignore();
|
|
|
|
|
|
|
|
|
|
if (userManager->login(username, password)) {
|
|
|
|
|
cout << "登录成功!当前选择为" << userManager->getCurrentType() << "出题" << endl;
|
|
|
|
|
} else {
|
|
|
|
|
cout << "请输入正确的用户名、密码" << endl;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (userManager->getIsLoggedIn()) {
|
|
|
|
|
cout << endl << "准备生成" << userManager->getCurrentType() << "数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录)" << endl;
|
|
|
|
|
cout << "或者输入\"切换为小学/初中/高中\"来切换题目类型: ";
|
|
|
|
|
|
|
|
|
|
cin.clear();
|
|
|
|
|
processInput();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
|
|
|
|
|
auto fileManager = make_shared<FileManager>();
|
|
|
|
|
auto userManager = make_shared<UserManager>(fileManager);
|
|
|
|
|
MathPaperGenerator generator(userManager, fileManager);
|
|
|
|
|
|
|
|
|
|
generator.run();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|