This commit is contained in:
2025-11-01 23:13:51 +08:00
parent bf79d9ef6f
commit f689e08a1d
7 changed files with 369 additions and 57 deletions

View File

@@ -0,0 +1,22 @@
from textual.app import App, ComposeResult
from textual.widgets import Button, Header, Label, Footer
class HeurAMSApp(App):
#CSS_PATH = "question02.tcss"
TITLE = "潜进"
SUB_TITLE = "启发式辅助记忆调度器"
def compose(self) -> ComposeResult:
yield Header(show_clock = True)
yield Footer(show_command_palette = True)
def on_mount(self) -> None:
def on_button_pressed(self, event: Button.Pressed) -> None:
self.exit(event.button.id)
if __name__ == "__main__":
app = HeurAMSApp()
app.run()

View File

@@ -0,0 +1,25 @@
from textual.widget import Widget
class FileSelector(Widget):
def __init__(self, *children: Widget, name: str | None = None, id: str | None = None, classes: str | None = None, disabled: bool = False, markup: bool = True) -> None:
super().__init__(*children, name=name, id=id, classes=classes, disabled=disabled, markup=markup)
def a(self):
file_list_widget = self.query_one("#file-list", ListView)
nucleon_path = pathlib.Path("./nucleon")
nucleon_files = sorted(
[f for f in nucleon_path.iterdir() if f.suffix == ".toml"],
key=lambda f: Glimpse(pt.NucleonUnion(f)).next_date,
reverse=True
)
if nucleon_files:
for file in nucleon_files:
text = self.item_desc_generator(pathlib.Path(file))
file_list_widget.append(ListItem(
Label(text[0] + '\n' + text[1]),
))
else:
file_list_widget.append(
ListItem(Static("在 ./nucleon/ 中未找到任何内容源数据文件.\n请放置文件后重启应用.\n或者新建空的单元集."))
)
file_list_widget.disabled = True

View File

@@ -0,0 +1,9 @@
from .sm2 import SM2Algorithm
__all__ = [
'SM2Algorithm',
]
registry = {
"SM-2": SM2Algorithm,
}

View File

@@ -1,66 +1,72 @@
import heurams.services.timer as timer
from typing import TypedDict
algo_name = "SM-2"
class SM2Algorithm:
algo_name = "SM-2"
class AlgodataDict(TypedDict):
efactor: float
real_rept: int
rept: int
interval: int
last_date: int
next_date: int
is_activated: int
last_modify: float
class AlgodataDict(TypedDict):
efactor: float
real_rept: int
rept: int
interval: int
last_date: int
next_date: int
is_activated: int
last_modify: float
defaults = {
'efactor': 2.5,
'real_rept': 0,
'rept': 0,
'interval': 0,
'last_date': 0,
'next_date': 0,
'is_activated': 0,
'last_modify': timer.get_timestamp()
}
defaults = {
'efactor': 2.5,
'real_rept': 0,
'rept': 0,
'interval': 0,
'last_date': 0,
'next_date': 0,
'is_activated': 0,
'last_modify': timer.get_timestamp()
}
@classmethod
def revisor(cls, algodata: dict, feedback: int = 5, is_new_activation: bool = False):
"""SM-2 算法迭代决策机制实现
根据 quality(0 ~ 5) 进行参数迭代最佳间隔
quality 由主程序评估
def revisor(algodata: dict, feedback: int = 5, is_new_activation: bool = False):
"""SM-2 算法迭代决策机制实现
根据 quality(0 ~ 5) 进行参数迭代最佳间隔
quality 由主程序评估
Args:
quality (int): 记忆保留率量化参数
"""
if feedback == -1:
return
Args:
quality (int): 记忆保留率量化参数
"""
if feedback == -1:
return
algodata[algo_name]['efactor'] = algodata[algo_name]['efactor'] + (
0.1 - (5 - feedback) * (0.08 + (5 - feedback) * 0.02)
)
algodata[algo_name]['efactor'] = max(1.3, algodata[algo_name]['efactor'])
if feedback < 3:
algodata[algo_name]['rept'] = 0
algodata[algo_name]['interval'] = 0
else:
algodata[algo_name]['rept'] += 1
algodata[algo_name]['real_rept'] += 1
if is_new_activation:
algodata[algo_name]['rept'] = 0
algodata[algo_name]['efactor'] = 2.5
if algodata[algo_name]['rept'] == 0:
algodata[algo_name]['interval'] = 1
elif algodata[algo_name]['rept'] == 1:
algodata[algo_name]['interval'] = 6
else:
algodata[algo_name]['interval'] = round(
algodata[algo_name]['interval'] * algodata[algo_name]['efactor']
algodata[cls.algo_name]['efactor'] = algodata[cls.algo_name]['efactor'] + (
0.1 - (5 - feedback) * (0.08 + (5 - feedback) * 0.02)
)
algodata[cls.algo_name]['efactor'] = max(1.3, algodata[cls.algo_name]['efactor'])
algodata[algo_name]['last_date'] = timer.get_daystamp()
algodata[algo_name]['next_date'] = timer.get_daystamp() + algodata[algo_name]['interval']
algodata[algo_name]['last_modify'] = timer.get_timestamp()
if feedback < 3:
algodata[cls.algo_name]['rept'] = 0
algodata[cls.algo_name]['interval'] = 0
else:
algodata[cls.algo_name]['rept'] += 1
algodata[cls.algo_name]['real_rept'] += 1
if is_new_activation:
algodata[cls.algo_name]['rept'] = 0
algodata[cls.algo_name]['efactor'] = 2.5
if algodata[cls.algo_name]['rept'] == 0:
algodata[cls.algo_name]['interval'] = 1
elif algodata[cls.algo_name]['rept'] == 1:
algodata[cls.algo_name]['interval'] = 6
else:
algodata[cls.algo_name]['interval'] = round(
algodata[cls.algo_name]['interval'] * algodata[cls.algo_name]['efactor']
)
algodata[cls.algo_name]['last_date'] = timer.get_daystamp()
algodata[cls.algo_name]['next_date'] = timer.get_daystamp() + algodata[cls.algo_name]['interval']
algodata[cls.algo_name]['last_modify'] = timer.get_timestamp()
@classmethod
def is_due(cls, algodata):
return (algodata[cls.algo_name]['next_date'] <= timer.get_daystamp())

View File

@@ -0,0 +1,19 @@
"""
Puzzle 模块 - 谜题生成系统
提供多种类型的谜题生成器,支持从字符串、字典等数据源导入题目
"""
from .base import BasePuzzle
from .cloze import ClozePuzzle
from .mcq import MCQPuzzle
from .factory import PuzzleFactory
from .loader import PuzzleLoader
__all__ = [
'BasePuzzle',
'ClozePuzzle',
'MCQPuzzle',
'PuzzleFactory',
'PuzzleLoader',
]

View File

@@ -0,0 +1,77 @@
"""
谜题工厂类 - 统一创建各种类型的谜题
"""
from typing import Dict, List, Union, Any
from .base import BasePuzzle
from .cloze import ClozePuzzle
from .mcq import MCQPuzzle
class PuzzleFactory:
"""谜题工厂,根据配置创建不同类型的谜题"""
@staticmethod
def create_cloze(text: str, min_denominator: int = 7) -> ClozePuzzle:
"""
创建填空题谜题
Args:
text: 原始字符串,使用 "/" 分割句子
min_denominator: 最小概率倒数,控制填空数量
Returns:
ClozePuzzle: 填空题实例
"""
return ClozePuzzle(text, min_denominator)
@staticmethod
def create_mcq(
mapping: Dict[str, str],
jammer: List[str],
max_riddles_num: int = 2,
prefix: str = ""
) -> MCQPuzzle:
"""
创建选择题谜题
Args:
mapping: 题目-答案映射字典
jammer: 干扰项列表
max_riddles_num: 最大题目数量
prefix: 题目前缀
Returns:
MCQPuzzle: 选择题实例
"""
return MCQPuzzle(mapping, jammer, max_riddles_num, prefix)
@staticmethod
def from_config(config: Dict[str, Any]) -> BasePuzzle:
"""
根据配置字典创建谜题
Args:
config: 配置字典,包含谜题类型和参数
Returns:
BasePuzzle: 谜题实例
Raises:
ValueError: 当配置无效时抛出
"""
puzzle_type = config.get('type')
if puzzle_type == 'cloze':
return PuzzleFactory.create_cloze(
text=config['text'],
min_denominator=config.get('min_denominator', 7)
)
elif puzzle_type == 'mcq':
return PuzzleFactory.create_mcq(
mapping=config['mapping'],
jammer=config.get('jammer', []),
max_riddles_num=config.get('max_riddles_num', 2),
prefix=config.get('prefix', '')
)
else:
raise ValueError(f"未知的谜题类型: {puzzle_type}")

View File

@@ -0,0 +1,154 @@
"""
谜题加载器 - 从多种数据源加载谜题配置
"""
import json
import yaml
import re
from typing import Dict, List, Union, Any
from pathlib import Path
from .factory import PuzzleFactory
class PuzzleLoader:
"""谜题加载器,支持从多种格式加载谜题配置"""
@staticmethod
def from_json(file_path: Union[str, Path]) -> Dict[str, Any]:
"""
从JSON文件加载谜题配置
Args:
file_path: JSON文件路径
Returns:
Dict: 谜题配置字典
"""
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
@staticmethod
def from_yaml(file_path: Union[str, Path]) -> Dict[str, Any]:
"""
从YAML文件加载谜题配置
Args:
file_path: YAML文件路径
Returns:
Dict: 谜题配置字典
"""
with open(file_path, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
@staticmethod
def from_text(text: str, puzzle_type: str = 'cloze', **kwargs) -> Dict[str, Any]:
"""
从纯文本字符串创建谜题配置
Args:
text: 原始文本
puzzle_type: 谜题类型 ('cloze''mcq')
**kwargs: 其他参数
Returns:
Dict: 谜题配置字典
"""
config = {'type': puzzle_type}
if puzzle_type == 'cloze':
config['text'] = text
config.update(kwargs)
elif puzzle_type == 'mcq':
# 从文本解析选择题配置
config.update(PuzzleLoader._parse_mcq_from_text(text, **kwargs))
else:
raise ValueError(f"不支持的谜题类型: {puzzle_type}")
return config
@staticmethod
def _parse_mcq_from_text(text: str, **kwargs) -> Dict[str, Any]:
"""
从文本解析选择题配置
Args:
text: 原始文本
**kwargs: 其他参数
Returns:
Dict: 选择题配置
"""
# 简单的解析逻辑:每行一个题目,格式为 "题目:答案"
mapping = {}
lines = text.strip().split('\n')
for line in lines:
line = line.strip()
if ':' in line:
question, answer = line.split(':', 1)
mapping[question.strip()] = answer.strip()
return {
'mapping': mapping,
'jammer': kwargs.get('jammer', []),
'max_riddles_num': kwargs.get('max_riddles_num', 2),
'prefix': kwargs.get('prefix', '')
}
@staticmethod
def from_csv(file_path: Union[str, Path], **kwargs) -> Dict[str, Any]:
"""
从CSV文件加载选择题配置
Args:
file_path: CSV文件路径
**kwargs: 其他参数
Returns:
Dict: 选择题配置
"""
import csv
mapping = {}
with open(file_path, 'r', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
if len(row) >= 2:
mapping[row[0].strip()] = row[1].strip()
return {
'type': 'mcq',
'mapping': mapping,
'jammer': kwargs.get('jammer', []),
'max_riddles_num': kwargs.get('max_riddles_num', 2),
'prefix': kwargs.get('prefix', '')
}
@staticmethod
def load_puzzles_from_directory(directory: Union[str, Path]) -> List[Dict[str, Any]]:
"""
从目录批量加载谜题配置
Args:
directory: 目录路径
Returns:
List[Dict]: 谜题配置列表
"""
puzzles = []
dir_path = Path(directory)
# 加载JSON文件
for json_file in dir_path.glob('*.json'):
puzzles.append(PuzzleLoader.from_json(json_file))
# 加载YAML文件
for yaml_file in dir_path.glob('*.yaml'):
puzzles.append(PuzzleLoader.from_yaml(yaml_file))
# 加载CSV文件
for csv_file in dir_path.glob('*.csv'):
puzzles.append(PuzzleLoader.from_csv(csv_file))
return puzzles