fix: 完善
This commit is contained in:
@@ -1,58 +1,228 @@
|
||||
# mcq.py
|
||||
from .base import BasePuzzle
|
||||
import random
|
||||
from typing import List, Dict, Optional, Union
|
||||
|
||||
|
||||
class MCQPuzzle(BasePuzzle):
|
||||
"""选择题谜题生成器"""
|
||||
"""选择题谜题生成器
|
||||
|
||||
该类用于生成和管理选择题谜题, 支持多个题目同时生成,
|
||||
每个题目包含问题, 正确答案和干扰项选项.
|
||||
|
||||
Attributes:
|
||||
prefix (str): 题目前缀文本
|
||||
mapping (Dict[str, str]): 问题和正确答案的映射字典
|
||||
jammer (List[str]): 干扰项列表
|
||||
max_riddles_num (int): 最大题目数量限制
|
||||
wording (Union[str, List[str]]): 题目文本内容
|
||||
answer (Union[str, List[str]]): 正确答案列表
|
||||
options (List[List[str]]): 每个题目的选项列表
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, mapping: dict, jammer: list, max_riddles_num: int = 2, prefix: str = ""
|
||||
):
|
||||
self,
|
||||
mapping: Dict[str, str],
|
||||
jammer: List[str],
|
||||
max_riddles_num: int = 2,
|
||||
prefix: str = "",
|
||||
) -> None:
|
||||
"""初始化选择题谜题生成器
|
||||
|
||||
Args:
|
||||
mapping: 问题和正确答案的映射字典, 键为问题, 值为正确答案
|
||||
jammer: 干扰项列表, 用于生成错误选项
|
||||
max_riddles_num: 每次生成的最大题目数量, 范围限制在1-5之间
|
||||
prefix: 题目前缀文本, 会显示在每个题目之前
|
||||
"""
|
||||
self.prefix = prefix
|
||||
self.mapping = mapping
|
||||
self.jammer = list(set(jammer + list(mapping.values())))
|
||||
self.max_riddles_num = max(1, min(max_riddles_num, 5))
|
||||
|
||||
# 初始化干扰项, 确保至少有4个选项
|
||||
self._init_jammer(jammer)
|
||||
|
||||
# 初始化题目状态
|
||||
self._reset_puzzle_state()
|
||||
|
||||
def _init_jammer(self, jammer: List[str]) -> None:
|
||||
"""初始化干扰项列表
|
||||
|
||||
合并传入的干扰项和所有正确答案, 确保去重后至少有4个干扰项.
|
||||
|
||||
Args:
|
||||
jammer: 传入的干扰项列表
|
||||
"""
|
||||
# 合并正确答案和传入的干扰项, 并去重
|
||||
unique_jammers = set(jammer + list(self.mapping.values()))
|
||||
self.jammer = list(unique_jammers)
|
||||
|
||||
# 确保至少有4个干扰项
|
||||
while len(self.jammer) < 4:
|
||||
self.jammer.append(" ")
|
||||
self.max_riddles_num = max(1, min(max_riddles_num, 5))
|
||||
self.wording = "选择题 - 尚未刷新谜题"
|
||||
self.answer = ["选择题 - 尚未刷新谜题"]
|
||||
self.options = []
|
||||
|
||||
def refresh(self):
|
||||
"""刷新谜题,根据题目数量生成适当数量的谜题"""
|
||||
def _reset_puzzle_state(self) -> None:
|
||||
"""重置谜题状态为初始值
|
||||
|
||||
将题目文本, 答案和选项重置为默认状态.
|
||||
"""
|
||||
self.wording: Union[str, List[str]] = "选择题 - 尚未刷新谜题"
|
||||
self.answer: Union[str, List[str]] = ["选择题 - 尚未刷新谜题"]
|
||||
self.options: List[List[str]] = []
|
||||
|
||||
def refresh(self) -> None:
|
||||
"""刷新谜题, 生成指定数量的选择题
|
||||
|
||||
从mapping中随机选择指定数量的问题, 为每个问题生成包含正确答案
|
||||
和干扰项的选项列表, 并更新谜题状态.
|
||||
|
||||
Raises:
|
||||
ValueError: 当mapping为空时不会抛出异常, 但会设置空谜题状态
|
||||
"""
|
||||
if not self.mapping:
|
||||
self.wording = "无可用题目"
|
||||
self.answer = ["无答案"]
|
||||
self.options = []
|
||||
self._set_empty_puzzle()
|
||||
return
|
||||
|
||||
num_questions = min(self.max_riddles_num, len(self.mapping))
|
||||
questions = random.sample(list(self.mapping.items()), num_questions)
|
||||
puzzles = []
|
||||
answers = []
|
||||
all_options = []
|
||||
selected_questions = random.sample(list(self.mapping.items()), num_questions)
|
||||
|
||||
for question, correct_answer in questions:
|
||||
options = [correct_answer]
|
||||
available_jammers = [j for j in self.jammer if j != correct_answer]
|
||||
if len(available_jammers) >= 3:
|
||||
selected_jammers = random.sample(available_jammers, 3)
|
||||
else:
|
||||
selected_jammers = random.choices(available_jammers, k=3)
|
||||
options.extend(selected_jammers)
|
||||
random.shuffle(options)
|
||||
puzzles: List[str] = []
|
||||
answers: List[str] = []
|
||||
all_options: List[List[str]] = []
|
||||
|
||||
for question, correct_answer in selected_questions:
|
||||
options = self._generate_options(correct_answer)
|
||||
puzzles.append(question)
|
||||
answers.append(correct_answer)
|
||||
all_options.append(options)
|
||||
|
||||
question_texts = []
|
||||
for i, puzzle in enumerate(puzzles):
|
||||
question_texts.append(f"{self.prefix}:\n {i+1}. {puzzle}")
|
||||
|
||||
self.wording = question_texts
|
||||
self.wording = self._format_questions(puzzles)
|
||||
self.answer = answers
|
||||
self.options = all_options
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.wording}\n正确答案: {', '.join(self.answer)}"
|
||||
def _set_empty_puzzle(self) -> None:
|
||||
"""设置为空谜题状态
|
||||
|
||||
当没有可用的题目时, 设置相应的提示信息.
|
||||
"""
|
||||
self.wording = "无可用题目"
|
||||
self.answer = ["无答案"]
|
||||
self.options = []
|
||||
|
||||
def _generate_options(self, correct_answer: str) -> List[str]:
|
||||
"""为单个问题生成选项列表(包含正确答案和干扰项)
|
||||
|
||||
Args:
|
||||
correct_answer: 当前问题的正确答案
|
||||
|
||||
Returns:
|
||||
包含4个选项的列表, 其中一个是正确答案, 三个是干扰项
|
||||
|
||||
Note:
|
||||
如果可用干扰项不足3个, 会使用重复的干扰项填充
|
||||
"""
|
||||
options = [correct_answer]
|
||||
|
||||
# 获取可用的干扰项(排除正确答案)
|
||||
available_jammers = [
|
||||
jammer for jammer in self.jammer if jammer != correct_answer
|
||||
]
|
||||
|
||||
# 选择3个干扰项
|
||||
if len(available_jammers) >= 3:
|
||||
selected_jammers = random.sample(available_jammers, 3)
|
||||
else:
|
||||
selected_jammers = random.choices(available_jammers, k=3)
|
||||
|
||||
options.extend(selected_jammers)
|
||||
random.shuffle(options)
|
||||
|
||||
return options
|
||||
|
||||
def _format_questions(self, puzzles: List[str]) -> List[str]:
|
||||
"""格式化问题列表为可读的文本
|
||||
|
||||
Args:
|
||||
puzzles: 原始问题文本列表
|
||||
|
||||
Returns:
|
||||
格式化后的问题文本列表, 包含编号和前缀
|
||||
|
||||
Example:
|
||||
输入: ["问题1", "问题2"]
|
||||
输出: ["前缀:\\n 1. 问题1", "前缀:\\n 2. 问题2"]
|
||||
"""
|
||||
if not puzzles:
|
||||
return []
|
||||
|
||||
formatted_questions = []
|
||||
for i, puzzle in enumerate(puzzles, 1):
|
||||
question_text = (
|
||||
f"{self.prefix}:\n {i}. {puzzle}" if self.prefix else f"{i}. {puzzle}"
|
||||
)
|
||||
formatted_questions.append(question_text)
|
||||
|
||||
return formatted_questions
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""返回谜题的字符串表示
|
||||
|
||||
Returns:
|
||||
包含所有问题和正确答案的格式化字符串
|
||||
|
||||
Example:
|
||||
选择题 - 尚未刷新谜题
|
||||
正确答案: 选择题 - 尚未刷新谜题
|
||||
"""
|
||||
if isinstance(self.wording, list):
|
||||
question_text = "\n".join(self.wording)
|
||||
else:
|
||||
question_text = self.wording
|
||||
|
||||
if isinstance(self.answer, list):
|
||||
answer_text = ", ".join(self.answer)
|
||||
else:
|
||||
answer_text = str(self.answer)
|
||||
|
||||
return f"{question_text}\n正确答案: {answer_text}"
|
||||
|
||||
def get_question_count(self) -> int:
|
||||
"""获取当前生成的题目数量
|
||||
|
||||
Returns:
|
||||
当前题目的数量, 如果尚未刷新则返回 0
|
||||
"""
|
||||
if isinstance(self.wording, list):
|
||||
return len(self.wording)
|
||||
elif self.wording == "选择题 - 尚未刷新谜题" or self.wording == "无可用题目":
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
def get_correct_answer_for_question(self, question_index: int) -> Optional[str]:
|
||||
"""获取指定题目的正确答案
|
||||
|
||||
Args:
|
||||
question_index: 题目索引(从0开始)
|
||||
|
||||
Returns:
|
||||
指定题目的正确答案, 如果索引无效则返回None
|
||||
"""
|
||||
if not isinstance(self.answer, list):
|
||||
return None
|
||||
if 0 <= question_index < len(self.answer):
|
||||
return self.answer[question_index]
|
||||
return None
|
||||
|
||||
def get_options_for_question(self, question_index: int) -> Optional[List[str]]:
|
||||
"""获取指定题目的选项列表
|
||||
|
||||
Args:
|
||||
question_index: 题目索引(从0开始)
|
||||
|
||||
Returns:
|
||||
指定题目的选项列表, 如果索引无效则返回None
|
||||
"""
|
||||
if 0 <= question_index < len(self.options):
|
||||
return self.options[question_index]
|
||||
return None
|
||||
|
||||
@@ -25,6 +25,7 @@ class Procession:
|
||||
return 1 # 成功
|
||||
except IndexError as e:
|
||||
print(f"{e}")
|
||||
self.state = ProcessionState.FINISHED
|
||||
return 0
|
||||
|
||||
def append(self, atom=None):
|
||||
|
||||
Reference in New Issue
Block a user