diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..94a3dc4 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,747 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #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 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 getFilesInDirectory(const string& path) override { + vector 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& operands, const vector& 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& operands, const vector& 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 operands; + vector 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& operators) { + if (operators.size() <= 1) return expression; + + vector 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 operands; + vector 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& operators) { + if (operators.size() <= 1) return expression; + + vector 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 operands; + + for (int i = 0; i < operandCount; i++) { + operands.push_back(randomNumber(1, 100)); + } + + bool hasTrigonometry = false; + vector 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 accounts; + string currentUser; + string currentType; + bool isLoggedIn; + map> userQuestions; + shared_ptr fileManager; + +public: + UserManager(shared_ptr 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 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 createGenerator(const string& type) { + if (type == "小学") { + return make_unique(); + } else if (type == "初中") { + return make_unique(); + } else if (type == "高中") { + return make_unique(); + } + return nullptr; + } +}; + +// 数学卷子生成主类:负责题目生成、保存、用户交互等主流程 +class MathPaperGenerator { +private: + shared_ptr userManager; + unique_ptr questionGenerator; + shared_ptr fileManager; + +public: + MathPaperGenerator(shared_ptr um, shared_ptr fm) + : userManager(um), fileManager(fm) {} + + vector generateQuestions(int count) { + vector questions; + set 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& 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 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(); + auto userManager = make_shared(fileManager); + MathPaperGenerator generator(userManager, fileManager); + + generator.run(); + return 0; +} \ No newline at end of file diff --git a/src/main.exe b/src/main.exe new file mode 100644 index 0000000..76c17f3 Binary files /dev/null and b/src/main.exe differ