Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
2eeb60c75b | |||
2c870377a6 | |||
36562323b7 | |||
b91176241b | |||
edfeb0b40b | |||
9d658ea646 | |||
5d3c354d8f | |||
d27d353374 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1 +1,5 @@
|
||||
.vscode
|
||||
.vscode
|
||||
.directory
|
||||
__pycache__/
|
||||
scripts/
|
||||
.idea
|
0
CONTRIBUTING.md
Normal file
0
CONTRIBUTING.md
Normal file
50
README.md
50
README.md
@@ -1,21 +1,26 @@
|
||||
# 潜进 (HeurAMS) - 开放源代码实验型辅助记忆程序
|
||||
# 潜进 (HeurAMS) - 实验型辅助记忆程序
|
||||
> 形人而我无形,**则我专而敌分**
|
||||
|
||||
## 概述
|
||||
|
||||
"潜进" (HeurAMS, 中文含义: 启发式辅助记忆软件) 是一款为古诗词设计的记忆辅助软件, 集成记忆拟合算法、自然语音技术与生成式人工智能, 提供科学的记忆训练解决方案
|
||||
"潜进" (HeurAMS, 中文含义: 启发式辅助记忆软件) 是为习题册, 古诗词, 及其他问答/记忆/理解型题目设计的记忆辅助软件, 提供优化记忆方案
|
||||
|
||||
## 核心特性
|
||||
|
||||
### 科学记忆拟合算法
|
||||
## 技术集成与特性
|
||||
|
||||
- 采用经实证的 SM-2 间隔重复算法
|
||||
### 间隔迭代算法
|
||||
> 许多出版物都广泛讨论了不同重复间隔对学习效果的影响。特别是,间隔效应被认为是一种普遍现象。间隔效应是指,如果重复的间隔是分散/稀疏的,而不是集中重复,那么学习任务的表现会更好。因此,有观点提出,学习中使用的最佳重复间隔是**最长的、但不会导致遗忘的间隔**。
|
||||
- 采用经实证的 SM-2 间隔迭代算法, 此算法亦用作 Anki 闪卡记忆软件的默认闪卡调度器
|
||||
> 计划: 将添加 FSRS 算法 (Anki 的新可选闪卡调度器) 与一种 SM-15 变体算法作为后续替代
|
||||
> 参考 https://github.com/slaypni/SM-15
|
||||
> 为什么使用 SM-15 的变体?
|
||||
> SM-2 后续算法仅有论文, 无具体方程, 故使用一种基于 SM-15 描述实现的变体算法
|
||||
- 动态优化每首诗词的记忆间隔时间表
|
||||
- 实时跟踪记忆曲线,最大化长期记忆保留率与稳定性
|
||||
- 实时跟踪记忆曲线,优化长期记忆保留率与稳定性
|
||||
|
||||
### 技术集成与优化
|
||||
- 逐字解析:支持点击查看每个字的详细释义
|
||||
- 语法分析:接入生成式人工智能, 支持古文结构**交互式**解析
|
||||
### 学习进程优化
|
||||
- 逐字解析:支持逐字详细释义解析
|
||||
- 语法分析:接入生成式人工智能, 支持古文结构交互式解析
|
||||
- 自然语音:集成微软神经网络文本转语音 (TTS) 技术
|
||||
|
||||
### 现代用户界面
|
||||
@@ -24,34 +29,43 @@
|
||||
- 支持触屏/鼠标/键盘多操作模式
|
||||
- 简洁直观的复习流程设计
|
||||
|
||||
## 技术架构
|
||||
## 屏幕截图
|
||||

|
||||

|
||||
|
||||
## 技术架构
|
||||
> 有关技术与实现的细节, 请参阅 CONTRIBUTING.md
|
||||
> 提交拉取请求以参与到此开放源代码项目
|
||||
``` mermaid
|
||||
graph TD
|
||||
subgraph 后端
|
||||
A[SM-2算法] --> B[间隔预测引擎]
|
||||
B --> C[个性化记忆模型]
|
||||
A[SM-2 算法] --> B[间隔迭代算法]
|
||||
B --> C[迭代记忆参数]
|
||||
end
|
||||
|
||||
subgraph 用户界面
|
||||
D[诗词展示模块] --> E[交互复习界面]
|
||||
D[展示模块] --> E[用户界面]
|
||||
E --> F[进度追踪面板]
|
||||
end
|
||||
|
||||
subgraph 外部服务
|
||||
G[词义解析]
|
||||
H[语法分析]
|
||||
I[语音合成]
|
||||
G[LLM]
|
||||
H[TTS]
|
||||
end
|
||||
|
||||
C --> D
|
||||
F -->|用户数据| C
|
||||
D --> G
|
||||
D --> H
|
||||
D --> I
|
||||
```
|
||||
|
||||
## 系统要求
|
||||
|
||||
- 平台支持:Windows / macOS / Linux / Android(需要 Termux) (终端或浏览器)
|
||||
- 平台支持:Windows / macOS / Linux / Android (需要 Termux 或 Linux) (终端或浏览器)
|
||||
- 网络连接:可预缓存语音文件, 需联网使用大模型服务功能
|
||||
|
||||
## 使用 Nuitka 静态编译
|
||||
运行
|
||||
```bash
|
||||
nuitka --clang --jobs=6 --standalone --onefile main.py
|
||||
```
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -27,7 +27,7 @@ def get_daystamp() -> int:
|
||||
time_override = config.get("time_override", -1)
|
||||
|
||||
if time_override is not None and time_override != -1:
|
||||
print(f"TIME OVERRIDEED TO {time_override}")
|
||||
#print(f"TIME OVERRIDEED TO {time_override}")
|
||||
return int(time_override)
|
||||
|
||||
return int(time.time() // (24 * 3600))
|
||||
|
BIN
cache/voice/臣密言:臣以险衅, 夙遭闵凶..wav
vendored
BIN
cache/voice/臣密言:臣以险衅, 夙遭闵凶..wav
vendored
Binary file not shown.
35
main.py
35
main.py
@@ -12,7 +12,7 @@ import particles as pt
|
||||
from reactor import Reactor
|
||||
import auxiliary as aux
|
||||
|
||||
ver = '0.2.6'
|
||||
ver = '0.2.9'
|
||||
|
||||
config = aux.ConfigFile("config.toml")
|
||||
|
||||
@@ -37,8 +37,8 @@ class MemScreen(Screen):
|
||||
btn = dict()
|
||||
def __init__(
|
||||
self,
|
||||
nucleon_file: pt.AtomicFile,
|
||||
electron_file: pt.AtomicFile,
|
||||
nucleon_file: pt.NucleonUnion,
|
||||
electron_file: pt.ElectronUnion,
|
||||
tasked_num
|
||||
):
|
||||
super().__init__(name=None, id=None, classes=None)
|
||||
@@ -57,12 +57,13 @@ class MemScreen(Screen):
|
||||
yield Static("", id="feedback") # 用于显示反馈
|
||||
yield Label(self._get_progress_text(), id="progress")
|
||||
with Container(id="button_container"):
|
||||
self.btn['5'] = Button("完美回想", variant="success", id="q5", classes="choice")
|
||||
self.btn['4'] = Button("犹豫后正确", variant="success", id="q4", classes="choice")
|
||||
self.btn['3'] = Button("困难地正确", variant="warning", id="q3", classes="choice")
|
||||
self.btn['2'] = Button("错误但熟悉", variant="warning", id="q2", classes="choice")
|
||||
self.btn['1'] = Button("错误且不熟", variant="error", id="q1", classes="choice")
|
||||
self.btn['0'] = Button("完全空白", variant="error", id="q0", classes="choice")
|
||||
if 1:
|
||||
self.btn['5'] = Button("完美回想", variant="success", id="q5", classes="choice")
|
||||
self.btn['4'] = Button("犹豫后正确", variant="success", id="q4", classes="choice")
|
||||
self.btn['3'] = Button("困难地正确", variant="warning", id="q3", classes="choice")
|
||||
self.btn['2'] = Button("错误但熟悉", variant="warning", id="q2", classes="choice")
|
||||
self.btn['1'] = Button("错误且不熟", variant="error", id="q1", classes="choice")
|
||||
self.btn['0'] = Button("完全空白", variant="error", id="q0", classes="choice")
|
||||
yield Horizontal(self.btn['5'], self.btn['4'])
|
||||
yield Horizontal(self.btn['3'], self.btn['2'])
|
||||
yield Horizontal(self.btn['1'], self.btn['0'])
|
||||
@@ -146,7 +147,7 @@ class PreparationScreen(Screen):
|
||||
("escape", "quit_app", "退出")
|
||||
]
|
||||
|
||||
def __init__(self, nucleon_file: pt.AtomicFile, electron_file: pt.AtomicFile) -> None:
|
||||
def __init__(self, nucleon_file: pt.NucleonUnion, electron_file: pt.ElectronUnion) -> None:
|
||||
super().__init__(name=None, id=None, classes=None)
|
||||
self.nucleon_file = nucleon_file
|
||||
self.electron_file = electron_file
|
||||
@@ -157,12 +158,18 @@ class PreparationScreen(Screen):
|
||||
yield Label(f"记忆项目: [b]{self.nucleon_file.name}[/b]\n")
|
||||
yield Label(f"核子文件对象: ./nucleon/[b]{self.nucleon_file.name}[/b].toml")
|
||||
yield Label(f"电子文件对象: ./electron/[b]{self.electron_file.name}[/b].toml")
|
||||
yield Label(f"核子数量:{self.nucleon_file.get_len()}")
|
||||
yield Label(f"核子数量:{len(self.nucleon_file)}")
|
||||
yield Button("开始记忆", id="start_memorizing_button", variant="primary", classes="start-button")
|
||||
yield Static(f"\n全文如下:\n")
|
||||
yield Static(self.nucleon_file.get_full_content(), classes="full")
|
||||
yield Static(self._get_full_content(), classes="full")
|
||||
yield Footer()
|
||||
|
||||
def _get_full_content(self):
|
||||
content = ""
|
||||
for i in self.nucleon_file.nucleons:
|
||||
content += i['content']
|
||||
return content
|
||||
|
||||
def action_go_back(self):
|
||||
self.app.pop_screen()
|
||||
|
||||
@@ -215,13 +222,13 @@ class FileSelectorScreen(Screen):
|
||||
return
|
||||
|
||||
selected_filename = str(selected_label.renderable)
|
||||
nucleon_file = pt.AtomicFile(pathlib.Path("./nucleon") / selected_filename, "nucleon")
|
||||
nucleon_file = pt.NucleonUnion(pathlib.Path("./nucleon") / selected_filename)
|
||||
electron_file_path = pathlib.Path("./electron") / selected_filename
|
||||
if electron_file_path.exists():
|
||||
pass
|
||||
else:
|
||||
electron_file_path.touch()
|
||||
electron_file = pt.AtomicFile(pathlib.Path("./electron") / selected_filename, "electron")
|
||||
electron_file = pt.ElectronUnion(pathlib.Path("./electron") / selected_filename)
|
||||
# self.notify(f"已选择: {selected_filename}", timeout=2)
|
||||
self.app.push_screen(PreparationScreen(nucleon_file, electron_file))
|
||||
|
||||
|
@@ -1,3 +1,18 @@
|
||||
# 散列表的键翻译
|
||||
["keydata"]
|
||||
note = "笔记"
|
||||
keyword_note = "关键词翻译"
|
||||
translation = "语句翻译"
|
||||
|
||||
# 测试项目元数据
|
||||
["testdata"]
|
||||
# 记忆时显示的额外信息
|
||||
additional_inf = ["translation", "note"]
|
||||
# 填空测试
|
||||
fill_blank = ["translation"]
|
||||
# 选择题测试
|
||||
draw_card = ["keyword_note"]
|
||||
|
||||
["臣密言:臣以险衅, 夙遭闵凶."]
|
||||
note = []
|
||||
translation = "臣子李密陈言:我因命运不好,小时候遭遇到了不幸"
|
||||
|
246
particles.py
246
particles.py
@@ -3,49 +3,43 @@ import toml
|
||||
import time
|
||||
import auxiliary as aux
|
||||
|
||||
import time
|
||||
|
||||
class Electron():
|
||||
"""电子: 记忆分析元数据及算法"""
|
||||
algorithm = "SM-2" # 暂时使用 SM-2 算法进行记忆拟合, 考虑 SM-15 替代
|
||||
"""
|
||||
content = "" # 内容
|
||||
efactor = 2.5 # 易度系数, 越大越简单, 最大为5
|
||||
real_rept = 0 # (实际)重复次数
|
||||
rept = 0 # (有效)重复次数
|
||||
interval = 0 # 最佳间隔
|
||||
last_date = 0 # 上一次复习的时间戳
|
||||
next_date = 0 # 将要复习的时间戳
|
||||
is_activated = 0 # 激活状态
|
||||
# *NOTE: 此处"时间戳"是以天为单位的整数, 即 UNIX 时间戳除以一天的秒数取整
|
||||
last_modify = 0 # 最后修改时间戳(此处是UNIX时间戳)
|
||||
"""
|
||||
def __init__(self, content: str, data: dict):
|
||||
|
||||
def __init__(self, content: str, metadata: dict):
|
||||
self.content = content
|
||||
self.efactor = data.get('efactor', 2.5)
|
||||
self.real_rept = data.get('real_rept', 0)
|
||||
self.rept = data.get('rept', 0)
|
||||
self.interval = data.get('interval', 0)
|
||||
self.last_date = data.get('last_date', 0)
|
||||
self.next_date = data.get('next_date', 0)
|
||||
self.is_activated = data.get('is_activated', 0)
|
||||
self.last_modify = time.time()
|
||||
self.metadata = metadata
|
||||
if metadata == {}:
|
||||
#print("NULL")
|
||||
self._default_init()
|
||||
|
||||
def _default_init(self):
|
||||
defaults = {
|
||||
'efactor': 2.5, # 易度系数, 越大越简单, 最大为5
|
||||
'real_rept': 0, # (实际)重复次数
|
||||
'rept': 0, # (有效)重复次数
|
||||
'interval': 0, # 最佳间隔
|
||||
'last_date': 0, # 上一次复习的时间戳
|
||||
'next_date': 0, # 将要复习的时间戳
|
||||
'is_activated': 0, # 激活状态
|
||||
# *NOTE: 此处"时间戳"是以天为单位的整数, 即 UNIX 时间戳除以一天的秒数取整
|
||||
'last_modify': time.time() # 最后修改时间戳(此处是UNIX时间戳)
|
||||
}
|
||||
self.metadata = defaults
|
||||
|
||||
def activate(self):
|
||||
self.is_activated = 1
|
||||
self.metadata['is_activated'] = 1
|
||||
self.metadata['last_modify'] = time.time()
|
||||
|
||||
def modify(self, var: str, value):
|
||||
setattr(self, var, value)
|
||||
self.last_modify = time.time()
|
||||
|
||||
def export_data(self):
|
||||
return {
|
||||
'efactor': self.efactor,
|
||||
'real_rept': self.real_rept,
|
||||
'rept': self.rept,
|
||||
'interval': self.interval,
|
||||
'last_date': self.last_date,
|
||||
'next_date': self.next_date,
|
||||
'is_activated': self.is_activated
|
||||
}
|
||||
if var in self.metadata:
|
||||
self.metadata[var] = value
|
||||
self.metadata['last_modify'] = time.time()
|
||||
else:
|
||||
print(f"警告: '{var}' 非已知元数据字段")
|
||||
|
||||
def revisor(self, quality: int = 5, is_new_activation: bool = False):
|
||||
"""SM-2 算法迭代决策机制实现
|
||||
@@ -58,91 +52,168 @@ class Electron():
|
||||
if quality == -1:
|
||||
return -1
|
||||
|
||||
self.efactor = self.efactor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02))
|
||||
self.efactor = max(1.3, self.efactor)
|
||||
self.metadata['efactor'] = self.metadata['efactor'] + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02))
|
||||
self.metadata['efactor'] = max(1.3, self.metadata['efactor'])
|
||||
|
||||
if quality < 3:
|
||||
# 若保留率低于 3,重置重复次数
|
||||
self.rept = 0
|
||||
self.interval = 0 # 设为0,以便下面重新计算 I(1)
|
||||
self.metadata['rept'] = 0
|
||||
self.metadata['interval'] = 0 # 设为0,以便下面重新计算 I(1)
|
||||
else:
|
||||
self.rept += 1
|
||||
self.real_rept += 1
|
||||
self.metadata['rept'] += 1
|
||||
|
||||
self.metadata['real_rept'] += 1
|
||||
|
||||
if is_new_activation: # 初次激活
|
||||
self.rept = 0
|
||||
self.efactor = 2.5
|
||||
self.metadata['rept'] = 0
|
||||
self.metadata['efactor'] = 2.5
|
||||
|
||||
if self.rept == 0: # 刚被重置或初次激活后复习
|
||||
self.interval = 1 # I(1)
|
||||
elif self.rept == 1:
|
||||
self.interval = 6 # I(2) 经验公式
|
||||
if self.metadata['rept'] == 0: # 刚被重置或初次激活后复习
|
||||
self.metadata['interval'] = 1 # I(1)
|
||||
elif self.metadata['rept'] == 1:
|
||||
self.metadata['interval'] = 6 # I(2) 经验公式
|
||||
else:
|
||||
self.interval = round(self.interval * self.efactor)
|
||||
self.metadata['interval'] = round(self.metadata['interval'] * self.metadata['efactor'])
|
||||
|
||||
self.last_date = aux.get_daystamp()
|
||||
self.next_date = aux.get_daystamp() + self.interval
|
||||
self.metadata['last_date'] = aux.get_daystamp()
|
||||
self.metadata['next_date'] = aux.get_daystamp() + self.metadata['interval']
|
||||
self.metadata['last_modify'] = time.time()
|
||||
|
||||
def __str__(self):
|
||||
return (f"记忆单元预览 \n"
|
||||
f"内容: '{self.content}' \n"
|
||||
f"易度系数: {self.efactor:.2f} \n"
|
||||
f"已经重复的次数: {self.rept} \n"
|
||||
f"下次间隔: {self.interval} 天 \n"
|
||||
f"下次复习日期时间戳: {self.next_date}")
|
||||
f"易度系数: {self.metadata['efactor']:.2f} \n"
|
||||
f"已经重复的次数: {self.metadata['rept']} \n"
|
||||
f"下次间隔: {self.metadata['interval']} 天 \n"
|
||||
f"下次复习日期时间戳: {self.metadata['next_date']}")
|
||||
|
||||
def __eq__(self, other):
|
||||
if self.content == other.content:
|
||||
return 1
|
||||
return 0
|
||||
return True
|
||||
return False
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.content)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == "content":
|
||||
return self.content
|
||||
if key in self.metadata:
|
||||
return self.metadata[key]
|
||||
else:
|
||||
raise KeyError(f"Key '{key}' not found in metadata.")
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if key == "content":
|
||||
raise AttributeError("content 应为只读")
|
||||
|
||||
# 可以在此处添加更复杂的验证逻辑,例如只允许修改预定义的 metadata 键
|
||||
# 或者根据键进行类型检查等。
|
||||
|
||||
self.metadata[key] = value
|
||||
self.metadata['last_modify'] = time.time()
|
||||
|
||||
def __iter__(self):
|
||||
yield from self.metadata.keys()
|
||||
|
||||
def __len__(self):
|
||||
return len(self.metadata)
|
||||
|
||||
@staticmethod
|
||||
def placeholder():
|
||||
return Electron("电子对象样例内容", {})
|
||||
|
||||
@staticmethod
|
||||
def import_from_file(path: pathlib.Path):
|
||||
name = path.name.replace(path.suffix, "")
|
||||
with open(path, 'r') as f:
|
||||
all = toml.load(f)
|
||||
lst = list()
|
||||
for i in all.keys():
|
||||
lst.append(Electron(i, all[i]))
|
||||
return (name, lst)
|
||||
@staticmethod
|
||||
def save_to_file(electron_dictized, path: pathlib.Path):
|
||||
with open(path, 'w') as f:
|
||||
toml.dump(electron_dictized, f)
|
||||
|
||||
class Nucleon():
|
||||
class Nucleon:
|
||||
"""核子: 材料元数据"""
|
||||
|
||||
def __init__(self, content: str, data: dict):
|
||||
self.metadata = data
|
||||
self.content = content
|
||||
|
||||
@staticmethod
|
||||
def import_from_file(path: pathlib.Path):
|
||||
name = path.name.replace(path.suffix, "")
|
||||
with open(path, 'r') as f:
|
||||
all = toml.load(f)
|
||||
lst = list()
|
||||
for i in all.keys():
|
||||
lst.append(Nucleon(i, all[i]))
|
||||
return (name, lst)
|
||||
|
||||
@staticmethod
|
||||
def save_to_file(nucleon_dictized, path: pathlib.Path):
|
||||
with open(path, 'w') as f:
|
||||
toml.dump(nucleon_dictized, f)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == "content":
|
||||
return self.content
|
||||
if key in self.metadata:
|
||||
return self.metadata[key]
|
||||
else:
|
||||
raise KeyError(f"Key '{key}' not found in metadata.")
|
||||
|
||||
def __iter__(self):
|
||||
yield from self.metadata.keys()
|
||||
|
||||
def __len__(self):
|
||||
return len(self.metadata)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.content)
|
||||
|
||||
@staticmethod
|
||||
def placeholder():
|
||||
return Nucleon("核子对象样例内容", {})
|
||||
|
||||
class AtomicFile():
|
||||
class NucleonUnion():
|
||||
"""
|
||||
替代原有 NucleonFile 类, 支持复杂逻辑
|
||||
|
||||
Attributes:
|
||||
path (Path): 对应于 NucleonUnion 实例的文件路径。
|
||||
name (str): 核联对象的显示名称,从文件名中派生。
|
||||
nucleons (list): 内部核子对象的列表。
|
||||
nucleons_dict (dict): 内部核子对象的字典,以核子内容作为键。
|
||||
keydata (dict): 核子对象字典键名的翻译。
|
||||
testdata (dict): 记忆测试项目的元数据。
|
||||
|
||||
Parameters:
|
||||
path (Path): 包含核子数据的文件路径。
|
||||
"""
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.name = path.name.replace(path.suffix, "")
|
||||
with open(path, 'r') as f:
|
||||
all = toml.load(f)
|
||||
lst = list()
|
||||
for i in all.keys():
|
||||
if "data" in i:
|
||||
continue
|
||||
lst.append(Nucleon(i, all[i]))
|
||||
self.keydata = all["keydata"]
|
||||
self.testdata = all["testdata"]
|
||||
self.nucleons = lst
|
||||
self.nucleons_dict = {i.content: i for i in lst}
|
||||
|
||||
def __len__(self):
|
||||
return len(self.nucleons)
|
||||
|
||||
def save(self):
|
||||
with open(self.path, 'w') as f:
|
||||
tmp = {i.content: i.metadata for i in self.nucleons}
|
||||
toml.dump(tmp, f)
|
||||
|
||||
class ElectronUnion():
|
||||
"取代原有 ElectronFile 类, 以支持复杂逻辑"
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.name = path.name.replace(path.suffix, "")
|
||||
with open(path, 'r') as f:
|
||||
all = toml.load(f)
|
||||
lst = list()
|
||||
for i in all.keys():
|
||||
lst.append(Electron(i, all[i]))
|
||||
self.electrons = lst
|
||||
self.electrons_dict = {i.content: i for i in lst}
|
||||
|
||||
def sync(self):
|
||||
"""同步 electrons_dict 中新增对到 electrons 中"""
|
||||
self.electrons = self.electrons_dict.values()
|
||||
|
||||
def save(self):
|
||||
#print(1)
|
||||
with open(self.path, 'w') as f:
|
||||
tmp = {i.content: i.metadata for i in self.electrons}
|
||||
#print(tmp)
|
||||
toml.dump(tmp, f)
|
||||
|
||||
"""class AtomicFile():
|
||||
def __init__(self, path, type_="unknown"):
|
||||
self.path = path
|
||||
self.type_ = type_
|
||||
@@ -167,6 +238,7 @@ class AtomicFile():
|
||||
def get_len(self):
|
||||
return len(self.datalist)
|
||||
|
||||
"""
|
||||
|
||||
class Atom():
|
||||
@staticmethod
|
||||
|
0
puzzles.py
Normal file
0
puzzles.py
Normal file
23
reactor.py
23
reactor.py
@@ -7,7 +7,7 @@ class Parser():
|
||||
|
||||
class Reactor():
|
||||
"""反应堆对象, 用于全面解析文件, 并处理和分配一次文件记忆流程的资源与策略"""
|
||||
def __init__(self, nucleon_file: pt.AtomicFile, electron_file: pt.AtomicFile, tasked_num):
|
||||
def __init__(self, nucleon_file: pt.NucleonUnion, electron_file: pt.ElectronUnion, tasked_num):
|
||||
# 导入原子对象
|
||||
self.reported = set()
|
||||
self.nucleon_file = nucleon_file
|
||||
@@ -17,7 +17,7 @@ class Reactor():
|
||||
self.atoms_review = list()
|
||||
counter = self.tasked_num
|
||||
|
||||
self.electron_dict = {elect.content: elect for elect in electron_file.datalist}
|
||||
self.electron_dict = electron_file.electrons_dict
|
||||
|
||||
def electron_dict_get_fallback(key) -> pt.Electron:
|
||||
value = self.electron_dict.get(key)
|
||||
@@ -26,20 +26,20 @@ class Reactor():
|
||||
if value is None:
|
||||
value = pt.Electron(key, {}) # 获取默认值
|
||||
self.electron_dict[key] = value # 将默认值存入字典
|
||||
value = self.electron_dict[key]
|
||||
electron_file.sync()
|
||||
|
||||
return value # 返回获取的值(可能是默认值)
|
||||
|
||||
for nucleon in nucleon_file.datalist:
|
||||
for nucleon in nucleon_file.nucleons:
|
||||
atom = (electron_dict_get_fallback(nucleon.content), nucleon)
|
||||
if atom[0].is_activated == 0:
|
||||
if atom[0]["is_activated"] == 0:
|
||||
if counter > 0:
|
||||
atom[0].is_activated = 1
|
||||
atom[0]["is_activated"] = 1
|
||||
self.atoms_new.append(atom)
|
||||
counter -= 1
|
||||
else:
|
||||
if atom[0].next_date <= aux.get_daystamp():
|
||||
atom[0].last_date = aux.get_daystamp()
|
||||
if int(atom[0]["next_date"]) <= aux.get_daystamp():
|
||||
atom[0]["last_date"] = aux.get_daystamp()
|
||||
self.atoms_review.append(atom)
|
||||
# 设置运行时
|
||||
self.index: int
|
||||
@@ -64,6 +64,9 @@ class Reactor():
|
||||
2: "新记忆模式",
|
||||
3: "总复习模式"
|
||||
}
|
||||
print("Atoms New:", self.atoms_new)
|
||||
print("Atoms Review:", self.atoms_review)
|
||||
|
||||
processions = {
|
||||
1: self.atoms_review,
|
||||
2: self.atoms_new,
|
||||
@@ -96,10 +99,6 @@ class Reactor():
|
||||
def save(self):
|
||||
print("Progress saved")
|
||||
# self.nucleon_file.save()
|
||||
temp = list()
|
||||
for i in self.electron_dict.keys():
|
||||
temp.append(self.electron_dict[i])
|
||||
self.electron_file.datalist = temp
|
||||
self.electron_file.save()
|
||||
|
||||
def report(self, atom, quality):
|
||||
|
BIN
readme_src/image_1.png
Normal file
BIN
readme_src/image_1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 102 KiB |
BIN
readme_src/image_2.png
Normal file
BIN
readme_src/image_2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
45
testfield/blank_maker.py
Normal file
45
testfield/blank_maker.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import random
|
||||
|
||||
class BlankPuzzle():
|
||||
"""填空题谜题生成器
|
||||
|
||||
Args:
|
||||
text: 原始字符串(需要 "/" 分割句子, 末尾应有 "/")
|
||||
min_denominator: 最小概率倒数(如占所有可生成填空数的 1/7 中的 7, 若期望值小于 1, 则取 1)
|
||||
"""
|
||||
def __init__(self, text, min_denominator):
|
||||
self.text = text
|
||||
self.min_denominator = min_denominator
|
||||
self.wording = "填空题 - 尚未刷新谜题"
|
||||
self.answer = ["填空题 - 尚未刷新谜题"]
|
||||
|
||||
def refresh(self): # 刷新谜题
|
||||
placeholder = "___SLASH___"
|
||||
tmp_text = self.text.replace("/", placeholder)
|
||||
words = tmp_text.split(placeholder)
|
||||
if not words:
|
||||
return ""
|
||||
words = [word for word in words if word]
|
||||
num_blanks = min(max(1, len(words) // self.min_denominator), len(words))
|
||||
indices_to_blank = random.sample(range(len(words)), num_blanks)
|
||||
indices_to_blank.sort()
|
||||
blanked_words = list(words)
|
||||
answer = list()
|
||||
for index in indices_to_blank:
|
||||
blanked_words[index] = "__" * len(words[index])
|
||||
answer.append(words[index])
|
||||
result = []
|
||||
for word in blanked_words:
|
||||
result.append(word)
|
||||
self.answer = answer
|
||||
self.wording = "".join(result)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.wording}\n{str(self.answer)}"
|
||||
|
||||
# demo
|
||||
text = """我联合国人民/同兹/决心/: /欲免/后世/再遭/今代人类/两度/身历/惨不堪言/之战祸/.../"""
|
||||
riddle = BlankPuzzle(text, 3)
|
||||
print(riddle)
|
||||
riddle.refresh()
|
||||
print(riddle)
|
45
testfield/selection_maker.py
Normal file
45
testfield/selection_maker.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import random
|
||||
|
||||
class SelectionPuzzle():
|
||||
"""选择题谜题生成器
|
||||
|
||||
Args:
|
||||
text: 原始字符串(需要 "/" 分割句子, 末尾应有 "/")
|
||||
min_denominator: 最小概率倒数(如占所有可生成填空数的 1/7 中的 7, 若期望值小于 1, 则取 1)
|
||||
"""
|
||||
def __init__(self, prefix_text, origin_dict, min_denominator):
|
||||
self.text = text
|
||||
self.min_denominator = min_denominator
|
||||
self.wording = "填空题 - 尚未刷新谜题"
|
||||
self.answer = ["填空题 - 尚未刷新谜题"]
|
||||
|
||||
def refresh(self): # 刷新谜题
|
||||
placeholder = "___SLASH___"
|
||||
tmp_text = self.text.replace("/", placeholder)
|
||||
words = tmp_text.split(placeholder)
|
||||
if not words:
|
||||
return ""
|
||||
words = [word for word in words if word]
|
||||
num_blanks = min(max(1, len(words) // self.min_denominator), len(words))
|
||||
indices_to_blank = random.sample(range(len(words)), num_blanks)
|
||||
indices_to_blank.sort()
|
||||
blanked_words = list(words)
|
||||
answer = list()
|
||||
for index in indices_to_blank:
|
||||
blanked_words[index] = "__" * len(words[index])
|
||||
answer.append(words[index])
|
||||
result = []
|
||||
for word in blanked_words:
|
||||
result.append(word)
|
||||
self.answer = answer
|
||||
self.wording = "".join(result)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.wording}\n{str(self.answer)}"
|
||||
|
||||
# demo
|
||||
text = """我联合国人民/同兹/决心/: /欲免/后世/再遭/今代人类/两度/身历/惨不堪言/之战祸/.../"""
|
||||
riddle = BlankPuzzle(text, 3)
|
||||
print(riddle)
|
||||
riddle.refresh()
|
||||
print(riddle)
|
Reference in New Issue
Block a user