v1.0 #1

Merged
hnu202326010326 merged 1 commits from develop into main 4 months ago

@ -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;
}

Binary file not shown.
Loading…
Cancel
Save