You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

747 lines
25 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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