@ -1,28 +0,0 @@
|
||||
# 编译生成的可执行文件
|
||||
*.exe
|
||||
*.out
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# 临时文件
|
||||
*.tmp
|
||||
*.temp
|
||||
*~
|
||||
|
||||
# 系统文件
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# IDE文件
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# 生成的题目文件夹(用户数据)
|
||||
xiaoxue*/
|
||||
chuzhong*/
|
||||
gaozhong*/
|
||||
|
||||
# 日志文件
|
||||
*.log
|
||||
@ -1,22 +1,28 @@
|
||||
# Makefile for Math Exam Generator
|
||||
CXX = g++
|
||||
CXXFLAGS = -std=c++17 -Wall -Wextra -O2
|
||||
TARGET = math_exam_generator
|
||||
SOURCE = main.cpp
|
||||
# Simple Makefile for Math Exam Generator
|
||||
|
||||
# Default target
|
||||
all: $(TARGET)
|
||||
CXX ?= g++
|
||||
CXXFLAGS ?= -std=c++17 -Wall -Wextra -O2
|
||||
|
||||
# Build target
|
||||
$(TARGET): $(SOURCE)
|
||||
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SOURCE)
|
||||
# Cross-platform remove command
|
||||
ifeq ($(OS),Windows_NT)
|
||||
RM := cmd /C del /Q
|
||||
else
|
||||
RM := rm -f
|
||||
endif
|
||||
|
||||
# Clean target
|
||||
clean:
|
||||
rm -f $(TARGET) $(TARGET).exe
|
||||
TARGET := math_exam_generator
|
||||
SRCS := main.cpp app.cpp auth.cpp exam.cpp login.cpp utils.cpp
|
||||
OBJS := $(SRCS:.cpp=.o)
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||
-$(RM) $(OBJS)
|
||||
|
||||
# Run target
|
||||
run: $(TARGET)
|
||||
./$(TARGET)
|
||||
%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
.PHONY: all clean run
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-$(RM) $(OBJS) $(TARGET) $(TARGET).exe
|
||||
@ -0,0 +1,29 @@
|
||||
#include "include/auth.hpp"
|
||||
|
||||
namespace meg {
|
||||
|
||||
Auth::Auth() {
|
||||
// 小学 张三1..3
|
||||
users_[L"张三1"] = {L"123", Level::Primary};
|
||||
users_[L"张三2"] = {L"123", Level::Primary};
|
||||
users_[L"张三3"] = {L"123", Level::Primary};
|
||||
|
||||
// 初中 李四1..3
|
||||
users_[L"李四1"] = {L"123", Level::Middle};
|
||||
users_[L"李四2"] = {L"123", Level::Middle};
|
||||
users_[L"李四3"] = {L"123", Level::Middle};
|
||||
|
||||
// 高中 王五1..3
|
||||
users_[L"王五1"] = {L"123", Level::High};
|
||||
users_[L"王五2"] = {L"123", Level::High};
|
||||
users_[L"王五3"] = {L"123", Level::High};
|
||||
}
|
||||
|
||||
std::optional<User> Auth::authenticate(const std::wstring& username, const std::wstring& password) const {
|
||||
auto it = users_.find(username);
|
||||
if (it == users_.end()) return std::nullopt;
|
||||
if (it->second.first != password) return std::nullopt;
|
||||
return User{username, password, it->second.second};
|
||||
}
|
||||
|
||||
} // namespace meg
|
||||
@ -0,0 +1,152 @@
|
||||
#include "include/exam.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace meg {
|
||||
|
||||
static int rand_int(std::mt19937& rng, int lo, int hi) {
|
||||
std::uniform_int_distribution<int> dist(lo, hi);
|
||||
return dist(rng);
|
||||
}
|
||||
|
||||
ExamGenerator::ExamGenerator() {
|
||||
std::random_device rd;
|
||||
rng_ = std::mt19937(rd());
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> ExamGenerator::load_history(const std::filesystem::path& history_path) {
|
||||
std::unordered_set<std::string> s;
|
||||
std::ifstream fin(history_path);
|
||||
std::string line;
|
||||
while (std::getline(fin, line)) {
|
||||
if (!line.empty()) s.insert(line);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void ExamGenerator::append_history(const std::filesystem::path& history_path, const std::vector<std::string>& qs) {
|
||||
std::ofstream fout(history_path, std::ios::app);
|
||||
for (auto& q : qs) {
|
||||
fout << q << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> ExamGenerator::generate_unique(Level level, int count, std::unordered_set<std::string>& history) {
|
||||
std::vector<std::string> out;
|
||||
int attempts = 0;
|
||||
while ((int)out.size() < count && attempts < count * 50) {
|
||||
std::string q;
|
||||
if (level == Level::Primary) q = gen_primary();
|
||||
else if (level == Level::Middle) q = gen_middle();
|
||||
else q = gen_high();
|
||||
|
||||
if (history.insert(q).second) {
|
||||
out.push_back(q);
|
||||
}
|
||||
++attempts;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::string join_ops(const std::vector<int>& nums, const std::vector<char>& ops) {
|
||||
std::string s;
|
||||
for (size_t i = 0; i < nums.size(); ++i) {
|
||||
s += std::to_string(nums[i]);
|
||||
if (i < ops.size()) {
|
||||
s += ' ';
|
||||
s += ops[i];
|
||||
s += ' ';
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string ExamGenerator::gen_primary() {
|
||||
int n_ops = rand_int(rng_, 1, 2); // 2-3个操作数 => 1-2个操作符
|
||||
std::vector<int> nums;
|
||||
for (int i = 0; i < n_ops + 1; ++i) nums.push_back(rand_int(rng_, 1, 50));
|
||||
static const char all_ops[] = {'+', '-', '*', '/'};
|
||||
std::vector<char> ops;
|
||||
for (int i = 0; i < n_ops; ++i) ops.push_back(all_ops[rand_int(rng_, 0, 3)]);
|
||||
std::string expr = join_ops(nums, ops);
|
||||
// 适度加括号
|
||||
if (nums.size() >= 3 && rand_int(rng_, 0, 1)) {
|
||||
size_t pos = expr.find(' ');
|
||||
if (pos != std::string::npos) {
|
||||
expr = "(" + expr.substr(0, pos) + ")" + expr.substr(pos);
|
||||
}
|
||||
}
|
||||
return expr + " = ?";
|
||||
}
|
||||
|
||||
std::string ExamGenerator::gen_middle() {
|
||||
int n_ops = rand_int(rng_, 2, 3); // 3-4 操作数
|
||||
std::vector<int> nums;
|
||||
for (int i = 0; i < n_ops + 1; ++i) nums.push_back(rand_int(rng_, 1, 100));
|
||||
static const char all_ops[] = {'+', '-', '*', '/'};
|
||||
std::vector<char> ops;
|
||||
for (int i = 0; i < n_ops; ++i) ops.push_back(all_ops[rand_int(rng_, 0, 3)]);
|
||||
|
||||
// 随机把一个数变成平方或开方
|
||||
int idx = rand_int(rng_, 0, (int)nums.size() - 1);
|
||||
bool use_sqrt = rand_int(rng_, 0, 1) == 1;
|
||||
std::string expr = join_ops(nums, ops);
|
||||
|
||||
// 找到替换位置(简单方式:直接重新拼接)
|
||||
std::string s;
|
||||
for (size_t i = 0; i < nums.size(); ++i) {
|
||||
std::string term = std::to_string(nums[i]);
|
||||
if ((int)i == idx) {
|
||||
if (use_sqrt) term = "sqrt(" + term + ")"; // 开根号
|
||||
else term = term + "^2"; // 平方
|
||||
}
|
||||
s += term;
|
||||
if (i < ops.size()) {
|
||||
s += ' '; s += ops[i]; s += ' ';
|
||||
}
|
||||
}
|
||||
return s + " = ?";
|
||||
}
|
||||
|
||||
std::string ExamGenerator::gen_high() {
|
||||
int n_ops = rand_int(rng_, 3, 4); // 4-5 操作数
|
||||
std::vector<int> nums;
|
||||
for (int i = 0; i < n_ops + 1; ++i) nums.push_back(rand_int(rng_, 1, 100));
|
||||
static const char all_ops[] = {'+', '-', '*', '/'};
|
||||
std::vector<char> ops;
|
||||
for (int i = 0; i < n_ops; ++i) ops.push_back(all_ops[rand_int(rng_, 0, 3)]);
|
||||
|
||||
// 先基础表达式
|
||||
std::string base;
|
||||
for (size_t i = 0; i < nums.size(); ++i) {
|
||||
base += std::to_string(nums[i]);
|
||||
if (i < ops.size()) { base += ' '; base += ops[i]; base += ' '; }
|
||||
}
|
||||
|
||||
// 至少一个三角函数,角度用 30/45/60/90 之一,使用 "deg" 标识角度
|
||||
static const char* funcs[] = {"sin", "cos", "tan"};
|
||||
static const int angles[] = {30, 45, 60, 90};
|
||||
std::string tri = std::string(funcs[rand_int(rng_, 0, 2)]) + "(" + std::to_string(angles[rand_int(rng_, 0, 3)]) + "deg)";
|
||||
|
||||
// 随机把一个数替换为三角项
|
||||
size_t replace_pos = rand_int(rng_, 0, (int)nums.size() - 1);
|
||||
// 重建字符串并替换
|
||||
std::string s;
|
||||
for (size_t i = 0; i < nums.size(); ++i) {
|
||||
std::string term = (i == replace_pos) ? tri : std::to_string(nums[i]);
|
||||
s += term;
|
||||
if (i < ops.size()) { s += ' '; s += ops[i]; s += ' '; }
|
||||
}
|
||||
|
||||
// 同时可能再加平方或开方
|
||||
if (rand_int(rng_, 0, 1)) {
|
||||
s = "(" + s + ")^2";
|
||||
} else if (rand_int(rng_, 0, 1)) {
|
||||
s = "sqrt(" + s + ")";
|
||||
}
|
||||
return s + " = ?";
|
||||
}
|
||||
|
||||
} // namespace meg
|
||||
@ -0,0 +1,30 @@
|
||||
#ifndef MEG_APP_HPP
|
||||
#define MEG_APP_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
#include "auth.hpp"
|
||||
#include "login.hpp"
|
||||
#include "exam.hpp"
|
||||
|
||||
namespace meg {
|
||||
|
||||
class App {
|
||||
public:
|
||||
App();
|
||||
void run();
|
||||
|
||||
private:
|
||||
Auth auth_;
|
||||
LoginManager login_{auth_};
|
||||
ExamGenerator exam_;
|
||||
|
||||
std::optional<User> current_user_;
|
||||
Level current_level_ = Level::Primary;
|
||||
|
||||
void handle_logged_in();
|
||||
};
|
||||
|
||||
} // namespace meg
|
||||
|
||||
#endif // MEG_APP_HPP
|
||||
@ -0,0 +1,27 @@
|
||||
#ifndef MEG_AUTH_HPP
|
||||
#define MEG_AUTH_HPP
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <map>
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace meg {
|
||||
|
||||
struct User {
|
||||
std::wstring username;
|
||||
std::wstring password;
|
||||
Level level;
|
||||
};
|
||||
|
||||
class Auth {
|
||||
public:
|
||||
Auth();
|
||||
std::optional<User> authenticate(const std::wstring& username, const std::wstring& password) const;
|
||||
private:
|
||||
std::map<std::wstring, std::pair<std::wstring, Level>> users_;
|
||||
};
|
||||
|
||||
} // namespace meg
|
||||
|
||||
#endif // MEG_AUTH_HPP
|
||||
@ -0,0 +1,33 @@
|
||||
#ifndef MEG_EXAM_HPP
|
||||
#define MEG_EXAM_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <filesystem>
|
||||
#include <random>
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace meg {
|
||||
|
||||
class ExamGenerator {
|
||||
public:
|
||||
ExamGenerator();
|
||||
// 加载/保存历史,防止同教师重复题目
|
||||
std::unordered_set<std::string> load_history(const std::filesystem::path& history_path);
|
||||
void append_history(const std::filesystem::path& history_path, const std::vector<std::string>& qs);
|
||||
|
||||
// 生成不重复题目
|
||||
std::vector<std::string> generate_unique(Level level, int count, std::unordered_set<std::string>& history);
|
||||
|
||||
private:
|
||||
std::mt19937 rng_;
|
||||
|
||||
std::string gen_primary();
|
||||
std::string gen_middle();
|
||||
std::string gen_high();
|
||||
};
|
||||
|
||||
} // namespace meg
|
||||
|
||||
#endif // MEG_EXAM_HPP
|
||||
@ -0,0 +1,20 @@
|
||||
#ifndef MEG_LOGIN_HPP
|
||||
#define MEG_LOGIN_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "auth.hpp"
|
||||
|
||||
namespace meg {
|
||||
|
||||
class LoginManager {
|
||||
public:
|
||||
explicit LoginManager(const Auth& auth) : auth_(auth) {}
|
||||
std::optional<User> prompt_login();
|
||||
private:
|
||||
const Auth& auth_;
|
||||
};
|
||||
|
||||
} // namespace meg
|
||||
|
||||
#endif // MEG_LOGIN_HPP
|
||||
Binary file not shown.
@ -0,0 +1,30 @@
|
||||
40 + 31 * 38 = ?
|
||||
46 * 6 = ?
|
||||
(8) - 32 / 31 = ?
|
||||
36 + 20 + 17 = ?
|
||||
(7) + 14 / 39 = ?
|
||||
(19) - 24 * 20 = ?
|
||||
(13) - 27 * 43 = ?
|
||||
43 / 17 - 39 = ?
|
||||
17 - 43 = ?
|
||||
9 / 30 = ?
|
||||
48 / 31 = ?
|
||||
17 - 24 = ?
|
||||
(3) - 23 * 30 = ?
|
||||
26 / 38 = ?
|
||||
(48) / 46 * 33 = ?
|
||||
11 * 7 = ?
|
||||
(47) / 17 * 31 = ?
|
||||
36 * 10 / 14 = ?
|
||||
47 * 23 / 32 = ?
|
||||
(29) - 14 * 33 = ?
|
||||
33 * 46 + 38 = ?
|
||||
(4) * 11 * 9 = ?
|
||||
(39) / 36 - 36 = ?
|
||||
25 - 4 = ?
|
||||
12 - 8 = ?
|
||||
39 - 32 = ?
|
||||
13 / 25 = ?
|
||||
40 - 43 = ?
|
||||
(48) + 44 + 28 = ?
|
||||
(12) / 9 + 44 = ?
|
||||
@ -0,0 +1,60 @@
|
||||
1. 40 + 31 * 38 = ?
|
||||
|
||||
2. 46 * 6 = ?
|
||||
|
||||
3. (8) - 32 / 31 = ?
|
||||
|
||||
4. 36 + 20 + 17 = ?
|
||||
|
||||
5. (7) + 14 / 39 = ?
|
||||
|
||||
6. (19) - 24 * 20 = ?
|
||||
|
||||
7. (13) - 27 * 43 = ?
|
||||
|
||||
8. 43 / 17 - 39 = ?
|
||||
|
||||
9. 17 - 43 = ?
|
||||
|
||||
10. 9 / 30 = ?
|
||||
|
||||
11. 48 / 31 = ?
|
||||
|
||||
12. 17 - 24 = ?
|
||||
|
||||
13. (3) - 23 * 30 = ?
|
||||
|
||||
14. 26 / 38 = ?
|
||||
|
||||
15. (48) / 46 * 33 = ?
|
||||
|
||||
16. 11 * 7 = ?
|
||||
|
||||
17. (47) / 17 * 31 = ?
|
||||
|
||||
18. 36 * 10 / 14 = ?
|
||||
|
||||
19. 47 * 23 / 32 = ?
|
||||
|
||||
20. (29) - 14 * 33 = ?
|
||||
|
||||
21. 33 * 46 + 38 = ?
|
||||
|
||||
22. (4) * 11 * 9 = ?
|
||||
|
||||
23. (39) / 36 - 36 = ?
|
||||
|
||||
24. 25 - 4 = ?
|
||||
|
||||
25. 12 - 8 = ?
|
||||
|
||||
26. 39 - 32 = ?
|
||||
|
||||
27. 13 / 25 = ?
|
||||
|
||||
28. 40 - 43 = ?
|
||||
|
||||
29. (48) + 44 + 28 = ?
|
||||
|
||||
30. (12) / 9 + 44 = ?
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
@echo off
|
||||
echo 正在编译程序...
|
||||
g++ -std=c++17 -Wall -Wextra -O2 -o math_exam_generator.exe main.cpp
|
||||
if %errorlevel% equ 0 (
|
||||
echo 编译成功!正在启动程序...
|
||||
echo.
|
||||
math_exam_generator.exe
|
||||
) else (
|
||||
echo 编译失败!
|
||||
pause
|
||||
)
|
||||
@ -1,12 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
echo 正在编译程序...
|
||||
g++ -std=c++17 -Wall -Wextra -O2 -o math_exam_generator.exe main.cpp
|
||||
if %errorlevel% equ 0 (
|
||||
echo 编译成功!正在启动程序...
|
||||
echo.
|
||||
math_exam_generator.exe
|
||||
) else (
|
||||
echo 编译失败!
|
||||
pause
|
||||
)
|
||||
@ -0,0 +1,74 @@
|
||||
#include "include/utils.hpp"
|
||||
|
||||
#include <locale>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <io.h>
|
||||
# include <fcntl.h>
|
||||
#endif
|
||||
|
||||
namespace meg {
|
||||
|
||||
std::wstring level_to_chinese(Level level) {
|
||||
switch (level) {
|
||||
case Level::Primary: return L"小学";
|
||||
case Level::Middle: return L"初中";
|
||||
case Level::High: return L"高中";
|
||||
}
|
||||
return L"小学";
|
||||
}
|
||||
|
||||
void init_console_locale() {
|
||||
#ifdef _WIN32
|
||||
_setmode(_fileno(stdout), _O_U16TEXT);
|
||||
_setmode(_fileno(stdin), _O_U16TEXT);
|
||||
_setmode(_fileno(stderr), _O_U16TEXT);
|
||||
#else
|
||||
try {
|
||||
std::locale loc("");
|
||||
std::locale::global(loc);
|
||||
std::wcout.imbue(loc);
|
||||
std::wcin.imbue(loc);
|
||||
std::wcerr.imbue(loc);
|
||||
} catch (...) {
|
||||
// 忽略本地化设置失败
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string now_timestamp_str() {
|
||||
using namespace std::chrono;
|
||||
auto now = system_clock::now();
|
||||
std::time_t t = system_clock::to_time_t(now);
|
||||
std::tm tm{};
|
||||
#ifdef _WIN32
|
||||
localtime_s(&tm, &t);
|
||||
#else
|
||||
localtime_r(&t, &tm);
|
||||
#endif
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(&tm, "%Y-%m-%d-%H-%M-%S");
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
bool starts_with(const std::wstring& s, const std::wstring& prefix) {
|
||||
if (prefix.size() > s.size()) return false;
|
||||
return std::equal(prefix.begin(), prefix.end(), s.begin());
|
||||
}
|
||||
|
||||
static inline bool is_space(wchar_t c) {
|
||||
return c == L' ' || c == L'\t' || c == L'\n' || c == L'\r' || c == L'\f' || c == L'\v';
|
||||
}
|
||||
|
||||
std::wstring trim(const std::wstring& s) {
|
||||
size_t b = 0, e = s.size();
|
||||
while (b < e && is_space(s[b])) ++b;
|
||||
while (e > b && is_space(s[e-1])) --e;
|
||||
return s.substr(b, e - b);
|
||||
}
|
||||
|
||||
} // namespace meg
|
||||
Loading…
Reference in new issue