|
|
|
|
@ -0,0 +1,275 @@
|
|
|
|
|
import os
|
|
|
|
|
import random
|
|
|
|
|
import re
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from typing import List, Set, Dict, Optional
|
|
|
|
|
|
|
|
|
|
class MathPaperGenerator:
|
|
|
|
|
def __init__(self):
|
|
|
|
|
# 预设用户账号
|
|
|
|
|
self.users = {
|
|
|
|
|
'primary': [
|
|
|
|
|
{'username': '张三1', 'password': '123', 'type': '小学'},
|
|
|
|
|
{'username': '张三2', 'password': '123', 'type': '小学'},
|
|
|
|
|
{'username': '张三3', 'password': '123', 'type': '小学'}
|
|
|
|
|
],
|
|
|
|
|
'middle': [
|
|
|
|
|
{'username': '李四1', 'password': '123', 'type': '初中'},
|
|
|
|
|
{'username': '李四2', 'password': '123', 'type': '初中'},
|
|
|
|
|
{'username': '李四3', 'password': '123', 'type': '初中'}
|
|
|
|
|
],
|
|
|
|
|
'high': [
|
|
|
|
|
{'username': '王五1', 'password': '123', 'type': '高中'},
|
|
|
|
|
{'username': '王五2', 'password': '123', 'type': '高中'},
|
|
|
|
|
{'username': '王五3', 'password': '123', 'type': '高中'}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.current_user: Optional[Dict] = None
|
|
|
|
|
self.current_type: Optional[str] = None
|
|
|
|
|
self.base_dir = "数学试卷"
|
|
|
|
|
|
|
|
|
|
# 创建基础目录
|
|
|
|
|
if not os.path.exists(self.base_dir):
|
|
|
|
|
os.makedirs(self.base_dir)
|
|
|
|
|
|
|
|
|
|
def authenticate(self, username: str, password: str) -> Optional[Dict]:
|
|
|
|
|
"""用户认证"""
|
|
|
|
|
for user_type, user_list in self.users.items():
|
|
|
|
|
for user in user_list:
|
|
|
|
|
if user['username'] == username and user['password'] == password:
|
|
|
|
|
return user
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def get_user_folder(self, username: str) -> str:
|
|
|
|
|
"""获取用户专属文件夹路径"""
|
|
|
|
|
user_folder = os.path.join(self.base_dir, username)
|
|
|
|
|
if not os.path.exists(user_folder):
|
|
|
|
|
os.makedirs(user_folder)
|
|
|
|
|
return user_folder
|
|
|
|
|
|
|
|
|
|
def get_all_existing_questions(self, username: str) -> Set[str]:
|
|
|
|
|
"""获取该用户所有已生成的题目"""
|
|
|
|
|
user_folder = self.get_user_folder(username)
|
|
|
|
|
all_questions = set()
|
|
|
|
|
|
|
|
|
|
if not os.path.exists(user_folder):
|
|
|
|
|
return all_questions
|
|
|
|
|
|
|
|
|
|
for filename in os.listdir(user_folder):
|
|
|
|
|
if filename.endswith('.txt'):
|
|
|
|
|
filepath = os.path.join(user_folder, filename)
|
|
|
|
|
try:
|
|
|
|
|
with open(filepath, 'r', encoding='utf-8') as f:
|
|
|
|
|
content = f.read()
|
|
|
|
|
# 提取所有题目(去除题号)
|
|
|
|
|
questions = re.findall(r'\d+\.\s*(.+?)(?=\n\d+\.|\n\n|$)', content, re.DOTALL)
|
|
|
|
|
all_questions.update(question.strip() for question in questions)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"读取文件 {filename} 时出错: {e}")
|
|
|
|
|
|
|
|
|
|
return all_questions
|
|
|
|
|
|
|
|
|
|
def generate_primary_question(self) -> str:
|
|
|
|
|
"""生成小学题目:加减乘除"""
|
|
|
|
|
operators = ['+', '-', '×', '÷']
|
|
|
|
|
num_operands = random.randint(2, 5) # 2-5个操作数
|
|
|
|
|
|
|
|
|
|
operands = [random.randint(1, 100) for _ in range(num_operands)]
|
|
|
|
|
ops = [random.choice(operators) for _ in range(num_operands - 1)]
|
|
|
|
|
|
|
|
|
|
# 构建表达式
|
|
|
|
|
expression = str(operands[0])
|
|
|
|
|
for i in range(num_operands - 1):
|
|
|
|
|
expression += f" {ops[i]} {operands[i+1]}"
|
|
|
|
|
|
|
|
|
|
return expression
|
|
|
|
|
|
|
|
|
|
def generate_middle_question(self) -> str:
|
|
|
|
|
"""生成初中题目:包含平方、开方、括号"""
|
|
|
|
|
if random.random() < 0.5:
|
|
|
|
|
# 基础四则运算带括号
|
|
|
|
|
a, b, c = random.randint(1, 100), random.randint(1, 100), random.randint(1, 100)
|
|
|
|
|
ops = ['+', '-', '×']
|
|
|
|
|
op = random.choice(ops)
|
|
|
|
|
return f"({a} {op} {b}) × {c}"
|
|
|
|
|
else:
|
|
|
|
|
# 平方或开方运算
|
|
|
|
|
num = random.randint(1, 20)
|
|
|
|
|
if random.random() < 0.5:
|
|
|
|
|
return f"{num}² + {random.randint(1, 100)}"
|
|
|
|
|
else:
|
|
|
|
|
return f"√{num**2} × {random.randint(1, 50)}"
|
|
|
|
|
|
|
|
|
|
def generate_high_question(self) -> str:
|
|
|
|
|
"""生成高中题目:包含三角函数、乘方等"""
|
|
|
|
|
trig_functions = ['sin', 'cos', 'tan']
|
|
|
|
|
num = random.randint(1, 90)
|
|
|
|
|
|
|
|
|
|
choice = random.randint(0, 2)
|
|
|
|
|
if choice == 0:
|
|
|
|
|
# 三角函数
|
|
|
|
|
func = random.choice(trig_functions)
|
|
|
|
|
return f"{func}({num}°) × {random.randint(1, 50)}"
|
|
|
|
|
elif choice == 1:
|
|
|
|
|
# 乘方运算
|
|
|
|
|
base = random.randint(2, 10)
|
|
|
|
|
exp = random.randint(2, 4)
|
|
|
|
|
return f"{base}^{exp} + {random.randint(1, 100)}"
|
|
|
|
|
else:
|
|
|
|
|
# 复杂表达式
|
|
|
|
|
a, b = random.randint(1, 50), random.randint(1, 50)
|
|
|
|
|
return f"({a} + {b}) × √{random.randint(1, 100)}"
|
|
|
|
|
|
|
|
|
|
def generate_question(self, difficulty: str) -> str:
|
|
|
|
|
"""根据难度生成题目"""
|
|
|
|
|
if difficulty == '小学':
|
|
|
|
|
return self.generate_primary_question()
|
|
|
|
|
elif difficulty == '初中':
|
|
|
|
|
return self.generate_middle_question()
|
|
|
|
|
else: # 高中
|
|
|
|
|
return self.generate_high_question()
|
|
|
|
|
|
|
|
|
|
def generate_paper(self, username: str, difficulty: str, num_questions: int) -> bool:
|
|
|
|
|
"""生成试卷"""
|
|
|
|
|
user_folder = self.get_user_folder(username)
|
|
|
|
|
existing_questions = self.get_all_existing_questions(username)
|
|
|
|
|
generated_questions = []
|
|
|
|
|
|
|
|
|
|
attempts = 0
|
|
|
|
|
max_attempts = num_questions * 10 # 防止无限循环
|
|
|
|
|
|
|
|
|
|
while len(generated_questions) < num_questions and attempts < max_attempts:
|
|
|
|
|
question = self.generate_question(difficulty)
|
|
|
|
|
|
|
|
|
|
# 检查题目是否重复
|
|
|
|
|
if question not in existing_questions and question not in generated_questions:
|
|
|
|
|
generated_questions.append(question)
|
|
|
|
|
existing_questions.add(question)
|
|
|
|
|
|
|
|
|
|
attempts += 1
|
|
|
|
|
|
|
|
|
|
# 如果无法生成足够的不重复题目
|
|
|
|
|
if len(generated_questions) < num_questions:
|
|
|
|
|
print(f"警告:只能生成 {len(generated_questions)} 道不重复的题目")
|
|
|
|
|
|
|
|
|
|
# 生成文件名
|
|
|
|
|
timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
|
|
|
|
filename = f"{timestamp}.txt"
|
|
|
|
|
filepath = os.path.join(user_folder, filename)
|
|
|
|
|
|
|
|
|
|
# 写入文件
|
|
|
|
|
try:
|
|
|
|
|
with open(filepath, 'w', encoding='utf-8') as f:
|
|
|
|
|
for i, question in enumerate(generated_questions, 1):
|
|
|
|
|
f.write(f"{i}. {question}")
|
|
|
|
|
if i < len(generated_questions): # 题目之间空一行
|
|
|
|
|
f.write("\n\n")
|
|
|
|
|
|
|
|
|
|
print(f"试卷已生成:{filepath}")
|
|
|
|
|
print(f"成功生成 {len(generated_questions)} 道题目")
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"文件保存失败: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def switch_type(self, new_type: str) -> bool:
|
|
|
|
|
"""切换题目类型"""
|
|
|
|
|
valid_types = ['小学', '初中', '高中']
|
|
|
|
|
if new_type in valid_types:
|
|
|
|
|
self.current_type = new_type
|
|
|
|
|
print(f"当前选择为{new_type}出题")
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
print("请输入小学、初中和高中三个选项中的一个")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
"""主运行循环"""
|
|
|
|
|
print("=== 中小学数学卷子自动生成程序 ===")
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
# 登录阶段
|
|
|
|
|
while self.current_user is None:
|
|
|
|
|
try:
|
|
|
|
|
input_str = input("请输入用户名和密码(用空格隔开,输入quit退出):").strip()
|
|
|
|
|
if input_str.lower() == 'quit':
|
|
|
|
|
print("再见!")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
parts = input_str.split()
|
|
|
|
|
if len(parts) != 2:
|
|
|
|
|
print("请输入正确的用户名、密码")
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
username, password = parts
|
|
|
|
|
user = self.authenticate(username, password)
|
|
|
|
|
|
|
|
|
|
if user:
|
|
|
|
|
self.current_user = user
|
|
|
|
|
self.current_type = user['type']
|
|
|
|
|
print(f"当前选择为{self.current_type}出题")
|
|
|
|
|
else:
|
|
|
|
|
print("请输入正确的用户名、密码")
|
|
|
|
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
print("\n再见!")
|
|
|
|
|
return
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"输入错误:{e}")
|
|
|
|
|
|
|
|
|
|
# 登录后的操作阶段
|
|
|
|
|
while self.current_user is not None:
|
|
|
|
|
try:
|
|
|
|
|
# 确保 current_type 不为 None
|
|
|
|
|
if self.current_type is None:
|
|
|
|
|
self.current_type = self.current_user['type']
|
|
|
|
|
|
|
|
|
|
prompt = f"准备生成{self.current_type}数学题目,请输入生成题目数量(10-30,输入-1退出当前用户,输入'switch'切换类型):"
|
|
|
|
|
user_input = input(prompt).strip()
|
|
|
|
|
|
|
|
|
|
if user_input == '-1':
|
|
|
|
|
print("退出当前用户")
|
|
|
|
|
self.current_user = None
|
|
|
|
|
self.current_type = None
|
|
|
|
|
break
|
|
|
|
|
elif user_input.startswith('切换为'):
|
|
|
|
|
# 处理切换类型
|
|
|
|
|
new_type = user_input[3:].strip()
|
|
|
|
|
self.switch_type(new_type)
|
|
|
|
|
elif user_input.lower() == 'switch':
|
|
|
|
|
new_type = input("请输入要切换的类型(小学/初中/高中):").strip()
|
|
|
|
|
self.switch_type(new_type)
|
|
|
|
|
else:
|
|
|
|
|
# 处理生成题目
|
|
|
|
|
try:
|
|
|
|
|
num = int(user_input)
|
|
|
|
|
if 10 <= num <= 30:
|
|
|
|
|
# 确保 difficulty 参数是字符串类型
|
|
|
|
|
difficulty = self.current_type or self.current_user['type']
|
|
|
|
|
self.generate_paper(
|
|
|
|
|
self.current_user['username'],
|
|
|
|
|
difficulty,
|
|
|
|
|
num
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
print("题目数量应在10-30之间")
|
|
|
|
|
except ValueError:
|
|
|
|
|
print("请输入有效的数字")
|
|
|
|
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
print("\n再见!")
|
|
|
|
|
return
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"发生错误:{e}")
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
"""主函数"""
|
|
|
|
|
try:
|
|
|
|
|
generator = MathPaperGenerator()
|
|
|
|
|
generator.run()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"程序发生错误: {e}")
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|