fix: 完善
This commit is contained in:
48
data/nucleon/test2.toml
Normal file
48
data/nucleon/test2.toml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Nucleon 是 HeurAMS 软件项目使用的基于 TOML 的专有源文件格式, 版本 4
|
||||||
|
# 建议使用的 MIME 类型: application/vnd.xyz.imwangzhiyu.heurams-nucleon.v4+toml
|
||||||
|
|
||||||
|
["__metadata__"]
|
||||||
|
["__metadata__.attribution"] # 版权元信息
|
||||||
|
author = "__heurams__"
|
||||||
|
group = "高考古诗文"
|
||||||
|
name = "过秦论"
|
||||||
|
license = "CC-BY-SA 4.0"
|
||||||
|
desc = "高考古诗文 - 过秦论"
|
||||||
|
|
||||||
|
["__metadata__.annotation"] # 键批注
|
||||||
|
note = "笔记"
|
||||||
|
keyword_note = "关键词翻译"
|
||||||
|
translation = "语句翻译"
|
||||||
|
|
||||||
|
["__metadata__.formation"] # 文件配置
|
||||||
|
delimiter = "/"
|
||||||
|
tts_text = "eval:nucleon['content'].replace('/', '')"
|
||||||
|
|
||||||
|
["__metadata__.orbital.puzzles"] # 谜题定义
|
||||||
|
# 我们称 "Recognition" 为 recognition 谜题的 alia
|
||||||
|
"Recognition" = { __origin__ = "recognition", __hint__ = "", primary = "eval:nucleon['content']", secondary = ["eval:nucleon['keyword_note']", "eval:nucleon['note']"], top_dim = ["eval:nucleon['translation']"] }
|
||||||
|
"SelectMeaning" = { __origin__ = "mcq", __hint__ = "eval:nucleon['content']", primary = "eval:nucleon['content']", mapping = "eval:nucleon['keyword_note']", jammer = "eval:nucleon['keyword_note']", max_riddles_num = "eval:default['mcq']['max_riddles_num']", prefix = "选择正确项: " }
|
||||||
|
"FillBlank" = { __origin__ = "cloze", __hint__ = "", text = "eval:nucleon['content']", delimiter = "eval:metadata['formation']['delimiter']", min_denominator = "eval:default['cloze']['min_denominator']"}
|
||||||
|
|
||||||
|
["__metadata__.orbital.schedule"] # 内置的推荐学习方案
|
||||||
|
quick_review = [["FillBlank", "1.0"], ["SelectMeaning", "0.5"], ["recognition", "1.0"]]
|
||||||
|
recognition = [["Recognition", "1.0"]]
|
||||||
|
final_review = [["FillBlank", "0.7"], ["SelectMeaning", "0.7"], ["recognition", "1.0"]]
|
||||||
|
|
||||||
|
["秦孝公据崤函之固, 拥雍州之地,"]
|
||||||
|
note = []
|
||||||
|
content = "秦孝公/据/崤函/之固/, 拥/雍州/之地,/"
|
||||||
|
translation = "秦孝公占据着崤山和函谷关的险固地势,拥有雍州的土地,"
|
||||||
|
keyword_note = {"据"="占据", "崤函"="崤山和函谷关", "雍州"="古代九州之一"}
|
||||||
|
|
||||||
|
["君臣固守以窥周室,"]
|
||||||
|
note = []
|
||||||
|
content = "君臣/固守/以窥/周室,/"
|
||||||
|
translation = "君臣牢固地守卫着,借以窥视周王室的权力,"
|
||||||
|
keyword_note = {"窥"="窥视"}
|
||||||
|
|
||||||
|
["有席卷天下, 包举宇内, 囊括四海之意, 并吞八荒之心."]
|
||||||
|
note = []
|
||||||
|
content = "有/席卷/天下/, 包举/宇内/, 囊括/四海/之意/, 并吞/八荒/之心./"
|
||||||
|
translation = "有席卷天下,包办天宇之间,囊括四海的意图,并统天下的雄心。"
|
||||||
|
keyword_note = {"席卷"="像卷席子一样全部卷进去", "包举"="像打包一样全部拿走", "囊括"="像装口袋一样全部装进去", "八荒"="八方荒远之地"}
|
||||||
@@ -51,11 +51,11 @@ class MemScreen(Screen):
|
|||||||
|
|
||||||
def puzzle_widget(self):
|
def puzzle_widget(self):
|
||||||
try:
|
try:
|
||||||
print(self.phaser.state)
|
#print(self.phaser.state)
|
||||||
self.fission = Fission(self.procession.current_atom, self.phaser.state)
|
self.fission = Fission(self.procession.current_atom, self.phaser.state)
|
||||||
# print(1)
|
# print(1)
|
||||||
puzzle_info = next(self.fission.generate())
|
puzzle_info = next(self.fission.generate())
|
||||||
print(puzzle_info)
|
#print(puzzle_info)
|
||||||
return shim.puzzle2widget[puzzle_info["puzzle"]](
|
return shim.puzzle2widget[puzzle_info["puzzle"]](
|
||||||
atom=self.procession.current_atom, alia=puzzle_info["alia"]
|
atom=self.procession.current_atom, alia=puzzle_info["alia"]
|
||||||
)
|
)
|
||||||
@@ -90,7 +90,9 @@ class MemScreen(Screen):
|
|||||||
forwards = 1 if new_rating >= 4 else 0
|
forwards = 1 if new_rating >= 4 else 0
|
||||||
self.rating = -1
|
self.rating = -1
|
||||||
if forwards:
|
if forwards:
|
||||||
self.procession.forward(1)
|
ret = self.procession.forward(1)
|
||||||
|
if ret == 0:
|
||||||
|
self.procession = self.phaser.current_procession() # type: ignore
|
||||||
self.load_puzzle()
|
self.load_puzzle()
|
||||||
|
|
||||||
def action_play_voice(self):
|
def action_play_voice(self):
|
||||||
|
|||||||
@@ -4,14 +4,11 @@ from textual.widgets import (
|
|||||||
Header,
|
Header,
|
||||||
Footer,
|
Footer,
|
||||||
Label,
|
Label,
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
Button,
|
Button,
|
||||||
Markdown,
|
|
||||||
Static,
|
Static,
|
||||||
ProgressBar,
|
ProgressBar,
|
||||||
)
|
)
|
||||||
from textual.containers import Container, Horizontal, Center
|
from textual.containers import Container, Horizontal
|
||||||
from textual.containers import Container
|
from textual.containers import Container
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
import pathlib
|
import pathlib
|
||||||
@@ -19,7 +16,7 @@ import pathlib
|
|||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
import heurams.services.hasher as hasher
|
import heurams.services.hasher as hasher
|
||||||
from heurams.context import *
|
from heurams.context import *
|
||||||
from textual.worker import Worker, get_current_worker
|
from textual.worker import get_current_worker
|
||||||
|
|
||||||
|
|
||||||
class PrecachingScreen(Screen):
|
class PrecachingScreen(Screen):
|
||||||
|
|||||||
48
src/heurams/interface/screens/synctool.py
Normal file
48
src/heurams/interface/screens/synctool.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.widgets import (
|
||||||
|
Header,
|
||||||
|
Footer,
|
||||||
|
Label,
|
||||||
|
Button,
|
||||||
|
Static,
|
||||||
|
ProgressBar,
|
||||||
|
)
|
||||||
|
from textual.containers import Container, Horizontal
|
||||||
|
from textual.containers import Container
|
||||||
|
from textual.screen import Screen
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
import heurams.kernel.particles as pt
|
||||||
|
import heurams.services.hasher as hasher
|
||||||
|
from heurams.context import *
|
||||||
|
from textual.worker import get_current_worker
|
||||||
|
|
||||||
|
|
||||||
|
class SyncScreen(Screen):
|
||||||
|
|
||||||
|
BINDINGS = [("q", "go_back", "返回")]
|
||||||
|
|
||||||
|
def __init__(self, nucleons: list = [], desc: str = ""):
|
||||||
|
super().__init__(name=None, id=None, classes=None)
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Header(show_clock=True)
|
||||||
|
with Container(id="sync_container"):
|
||||||
|
pass
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
def on_mount(self):
|
||||||
|
"""挂载时初始化状态"""
|
||||||
|
|
||||||
|
def update_status(self, status, current_item="", progress=None):
|
||||||
|
"""更新状态显示"""
|
||||||
|
|
||||||
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
|
event.stop()
|
||||||
|
|
||||||
|
def action_go_back(self):
|
||||||
|
self.app.pop_screen()
|
||||||
|
|
||||||
|
def action_quit_app(self):
|
||||||
|
self.app.exit()
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
|
# 单项选择题
|
||||||
from textual.widgets import (
|
from textual.widgets import (
|
||||||
Label,
|
Label,
|
||||||
Button,
|
Button,
|
||||||
)
|
)
|
||||||
|
from textual.containers import (
|
||||||
|
Container
|
||||||
|
)
|
||||||
from textual.widget import Widget
|
from textual.widget import Widget
|
||||||
import heurams.kernel.particles as pt
|
import heurams.kernel.particles as pt
|
||||||
import heurams.kernel.puzzles as pz
|
import heurams.kernel.puzzles as pz
|
||||||
from .base_puzzle_widget import BasePuzzleWidget
|
from .base_puzzle_widget import BasePuzzleWidget
|
||||||
from typing import TypedDict
|
from typing import TypedDict
|
||||||
|
from bidict import bidict
|
||||||
|
from heurams.services.hasher import hash
|
||||||
|
|
||||||
class Setting(TypedDict):
|
class Setting(TypedDict):
|
||||||
__origin__: str
|
__origin__: str
|
||||||
@@ -42,7 +47,7 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
)
|
)
|
||||||
self.inputlist = []
|
self.inputlist = []
|
||||||
self.alia = alia
|
self.alia = alia
|
||||||
self.hashtable = {}
|
self.hashmap = bidict()
|
||||||
self._load()
|
self._load()
|
||||||
|
|
||||||
def _load(self):
|
def _load(self):
|
||||||
@@ -53,9 +58,7 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
self.puzzle.refresh()
|
self.puzzle.refresh()
|
||||||
|
|
||||||
def compose(self):
|
def compose(self):
|
||||||
setting: Setting = self.atom.registry["nucleon"].metadata["orbital"]["puzzle"][
|
setting: Setting = self.atom.registry["nucleon"].metadata["orbital"]["puzzle"][self.alia]
|
||||||
self.alia
|
|
||||||
]
|
|
||||||
yield Label(setting["primary"], id="sentence")
|
yield Label(setting["primary"], id="sentence")
|
||||||
yield Label(self.puzzle.wording[len(self.inputlist)], id="puzzle")
|
yield Label(self.puzzle.wording[len(self.inputlist)], id="puzzle")
|
||||||
yield Label(f"当前输入: {self.inputlist}", id="inputpreview")
|
yield Label(f"当前输入: {self.inputlist}", id="inputpreview")
|
||||||
@@ -63,8 +66,8 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
# 渲染当前问题的选项
|
# 渲染当前问题的选项
|
||||||
current_options = self.puzzle.options[len(self.inputlist)]
|
current_options = self.puzzle.options[len(self.inputlist)]
|
||||||
for i in current_options:
|
for i in current_options:
|
||||||
self.hashtable[str(hash(i))] = i
|
self.hashmap[str(hash(i))] = i
|
||||||
yield Button(i, id=f"select{hash(i)}")
|
yield Button(i, id=f"select-{hash(i)}")
|
||||||
|
|
||||||
yield Button("退格", id="delete")
|
yield Button("退格", id="delete")
|
||||||
|
|
||||||
@@ -79,12 +82,9 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
if current_question_index < len(self.puzzle.wording):
|
if current_question_index < len(self.puzzle.wording):
|
||||||
puzzle_label.update(self.puzzle.wording[current_question_index]) # type: ignore
|
puzzle_label.update(self.puzzle.wording[current_question_index]) # type: ignore
|
||||||
|
|
||||||
# 发送输入变化消息
|
|
||||||
|
|
||||||
# 如果还有下一题,发送题目切换消息
|
|
||||||
|
|
||||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
"""处理按钮点击事件"""
|
"""处理按钮点击事件"""
|
||||||
|
event.stop()
|
||||||
button_id = event.button.id
|
button_id = event.button.id
|
||||||
|
|
||||||
if button_id == "delete":
|
if button_id == "delete":
|
||||||
@@ -93,9 +93,10 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
self.inputlist.pop()
|
self.inputlist.pop()
|
||||||
self.refresh_buttons()
|
self.refresh_buttons()
|
||||||
self.update_display()
|
self.update_display()
|
||||||
|
|
||||||
elif button_id.startswith("select"): # type: ignore
|
elif button_id.startswith("select"): # type: ignore
|
||||||
# 选项选择处理
|
# 选项选择处理
|
||||||
answer_text = self.hashtable[button_id[6:]] # type: ignore
|
answer_text = self.hashmap[button_id[7:]] # type: ignore
|
||||||
self.inputlist.append(answer_text)
|
self.inputlist.append(answer_text)
|
||||||
|
|
||||||
# 检查是否完成所有题目
|
# 检查是否完成所有题目
|
||||||
@@ -103,16 +104,7 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
is_correct = self.inputlist == self.puzzle.answer
|
is_correct = self.inputlist == self.puzzle.answer
|
||||||
rating = 4 if is_correct else 2
|
rating = 4 if is_correct else 2
|
||||||
|
|
||||||
# 发送完成消息
|
self.screen.rating = rating # type: ignore
|
||||||
self.post_message(
|
|
||||||
self.PuzzleCompleted(
|
|
||||||
atom=self.atom,
|
|
||||||
rating=rating,
|
|
||||||
is_correct=is_correct,
|
|
||||||
user_answers=self.inputlist.copy(),
|
|
||||||
correct_answers=self.puzzle.answer.copy(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# 重置输入(如果回答错误)
|
# 重置输入(如果回答错误)
|
||||||
if not is_correct:
|
if not is_correct:
|
||||||
@@ -132,6 +124,7 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
for child in self.children
|
for child in self.children
|
||||||
if hasattr(child, "id") and child.id and child.id.startswith("select")
|
if hasattr(child, "id") and child.id and child.id.startswith("select")
|
||||||
]
|
]
|
||||||
|
|
||||||
for button in buttons_to_remove:
|
for button in buttons_to_remove:
|
||||||
self.remove_child(button) # type: ignore
|
self.remove_child(button) # type: ignore
|
||||||
|
|
||||||
@@ -140,8 +133,8 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
if current_question_index < len(self.puzzle.options):
|
if current_question_index < len(self.puzzle.options):
|
||||||
current_options = self.puzzle.options[current_question_index]
|
current_options = self.puzzle.options[current_question_index]
|
||||||
for option in current_options:
|
for option in current_options:
|
||||||
button_id = f"select{hash(option)}"
|
button_id = f"select-{hash(option)}"
|
||||||
if button_id not in self.hashtable:
|
if button_id not in self.hashmap:
|
||||||
self.hashtable[button_id] = option
|
self.hashmap[button_id] = option
|
||||||
new_button = Button(option, id=button_id)
|
new_button = Button(option, id=button_id)
|
||||||
self.mount(new_button)
|
self.mount(new_button)
|
||||||
|
|||||||
@@ -1,58 +1,228 @@
|
|||||||
# mcq.py
|
# mcq.py
|
||||||
from .base import BasePuzzle
|
from .base import BasePuzzle
|
||||||
import random
|
import random
|
||||||
|
from typing import List, Dict, Optional, Union
|
||||||
|
|
||||||
|
|
||||||
class MCQPuzzle(BasePuzzle):
|
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__(
|
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.prefix = prefix
|
||||||
self.mapping = mapping
|
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:
|
while len(self.jammer) < 4:
|
||||||
self.jammer.append(" ")
|
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:
|
if not self.mapping:
|
||||||
self.wording = "无可用题目"
|
self._set_empty_puzzle()
|
||||||
self.answer = ["无答案"]
|
|
||||||
self.options = []
|
|
||||||
return
|
return
|
||||||
|
|
||||||
num_questions = min(self.max_riddles_num, len(self.mapping))
|
num_questions = min(self.max_riddles_num, len(self.mapping))
|
||||||
questions = random.sample(list(self.mapping.items()), num_questions)
|
selected_questions = random.sample(list(self.mapping.items()), num_questions)
|
||||||
puzzles = []
|
|
||||||
answers = []
|
|
||||||
all_options = []
|
|
||||||
|
|
||||||
for question, correct_answer in questions:
|
puzzles: List[str] = []
|
||||||
options = [correct_answer]
|
answers: List[str] = []
|
||||||
available_jammers = [j for j in self.jammer if j != correct_answer]
|
all_options: List[List[str]] = []
|
||||||
if len(available_jammers) >= 3:
|
|
||||||
selected_jammers = random.sample(available_jammers, 3)
|
for question, correct_answer in selected_questions:
|
||||||
else:
|
options = self._generate_options(correct_answer)
|
||||||
selected_jammers = random.choices(available_jammers, k=3)
|
|
||||||
options.extend(selected_jammers)
|
|
||||||
random.shuffle(options)
|
|
||||||
puzzles.append(question)
|
puzzles.append(question)
|
||||||
answers.append(correct_answer)
|
answers.append(correct_answer)
|
||||||
all_options.append(options)
|
all_options.append(options)
|
||||||
|
|
||||||
question_texts = []
|
self.wording = self._format_questions(puzzles)
|
||||||
for i, puzzle in enumerate(puzzles):
|
|
||||||
question_texts.append(f"{self.prefix}:\n {i+1}. {puzzle}")
|
|
||||||
|
|
||||||
self.wording = question_texts
|
|
||||||
self.answer = answers
|
self.answer = answers
|
||||||
self.options = all_options
|
self.options = all_options
|
||||||
|
|
||||||
def __str__(self):
|
def _set_empty_puzzle(self) -> None:
|
||||||
return f"{self.wording}\n正确答案: {', '.join(self.answer)}"
|
"""设置为空谜题状态
|
||||||
|
|
||||||
|
当没有可用的题目时, 设置相应的提示信息.
|
||||||
|
"""
|
||||||
|
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 # 成功
|
return 1 # 成功
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
print(f"{e}")
|
print(f"{e}")
|
||||||
|
self.state = ProcessionState.FINISHED
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def append(self, atom=None):
|
def append(self, atom=None):
|
||||||
|
|||||||
Reference in New Issue
Block a user