diff --git a/.gitignore b/.gitignore index 83c4619..5841f3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .vscode -__pycache__/ \ No newline at end of file +.directory +__pycache__/ +scripts/ +.idea \ No newline at end of file diff --git a/README.md b/README.md index 25af91e..fccd98f 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,17 @@ ## 概述 -"潜进" (HeurAMS, 中文含义: 启发式辅助记忆软件) 是一款为古诗词设计的记忆辅助软件, 集成记忆拟合算法、自然语音技术与生成式人工智能, 提供科学的记忆训练解决方案 +"潜进" (HeurAMS, 中文含义: 启发式辅助记忆软件) 是为习题册, 古诗词, 及其他问答/记忆/理解型题目设计的记忆辅助软件, 集成记忆拟合算法、自然语音技术与生成式人工智能, 提供优化的 ## 核心特性 ### 科学记忆拟合算法 -- 采用经实证的 SM-2 间隔重复算法 +- 采用经实证的 SM-2 间隔重复算法, 此算法亦用作 Anki 闪卡记忆软件的默认闪卡调度器 +> TODO: 将添加 FSRS 算法 (Anki 的新可选闪卡调度器) 与一种 SM-15 变体算法作为后续替代 +> 参考 https://github.com/slaypni/SM-15 +> 为什么使用 SM-15 的变体? +> SM-2 后续算法仅有论文, 无具体方程, SM-15 是基于 SM-15 描述实现的变体算法 - 动态优化每首诗词的记忆间隔时间表 - 实时跟踪记忆曲线,最大化长期记忆保留率与稳定性 diff --git a/cache/voice/臣密言:臣以险衅, 夙遭闵凶..wav b/cache/voice/臣密言:臣以险衅, 夙遭闵凶..wav deleted file mode 100644 index a0ad0ec..0000000 Binary files a/cache/voice/臣密言:臣以险衅, 夙遭闵凶..wav and /dev/null differ diff --git a/electron/陈情表.toml b/electron/陈情表.toml index e69de29..670bb85 100644 --- a/electron/陈情表.toml +++ b/electron/陈情表.toml @@ -0,0 +1,251 @@ +["臣密言:臣以险衅, 夙遭闵凶."] +efactor = 2.5 +real_rept = 2 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["生孩六月, 慈父见背;行年四岁, 舅夺母志."] +efactor = 2.5 +real_rept = 2 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["祖母刘愍臣孤弱, 躬亲抚养."] +efactor = 2.5 +real_rept = 2 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["臣少多疾病, 九岁不行, 零丁孤苦, 至于成立."] +efactor = 2.5 +real_rept = 2 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["既无伯叔, 终鲜兄弟, 门衰祚薄, 晚有儿息."] +efactor = 2.5 +real_rept = 11 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["外无期功强近之亲, 内无应门五尺之僮, 茕茕孑立, 形影相吊."] +efactor = 2.5 +real_rept = 2 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["而刘夙婴疾病, 常在床蓐, 臣侍汤药, 未曾废离."] +efactor = 2.5 +real_rept = 2 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["逮奉圣朝, 沐浴清化."] +efactor = 2.5 +real_rept = 2 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["前太守臣逵察臣孝廉;后刺史臣荣举臣秀才."] +efactor = 2.5 +real_rept = 2 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["臣以供养无主, 辞不赴命."] +efactor = 2.5 +real_rept = 2 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["诏书特下, 拜臣郎中, 寻蒙国恩, 除臣洗马."] +efactor = 2.5 +real_rept = 2 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["猥以微贱, 当侍东宫, 非臣陨首所能上报."] +efactor = 2.5 +real_rept = 2 +rept = 0 +interval = 1 +last_date = 11 +next_date = 12 +is_activated = 1 + +["臣具以表闻, 辞不就职."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["诏书切峻, 责臣逋慢;郡县逼迫, 催臣上道;州司临门, 急于星火."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["臣欲奉诏奔驰, 则刘病日笃, 欲苟顺私情, 则告诉不许."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["臣之进退, 实为狼狈."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["伏惟圣朝以孝治天下, 凡在故老, 犹蒙矜育, 况臣孤苦, 特为尤甚."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["且臣少仕伪朝, 历职郎署, 本图宦达, 不矜名节."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["今臣亡国贱俘, 至微至陋, 过蒙拔擢, 宠命优渥, 岂敢盘桓, 有所希冀!"] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["但以刘日薄西山, 气息奄奄, 人命危浅, 朝不虑夕."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["臣无祖母, 无以至今日, 祖母无臣, 无以终余年."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["母孙二人, 更相为命, 是以区区不能废远."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["臣密今年四十有四, 祖母今年九十有六, 是臣尽节于陛下之日长, 报养刘之日短."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["乌鸟私情, 愿乞终养."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["臣之辛苦, 非独蜀之人士及二州牧伯所见明知, 皇天后土, 实所共鉴."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["愿陛下矜悯愚诚, 听臣微志, 庶刘侥幸, 保卒余年."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["臣生当陨首, 死当结草."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 + +["臣不胜犬马怖惧之情, 谨拜表以闻."] +efactor = 2.5 +real_rept = 0 +rept = 0 +interval = 0 +last_date = 0 +next_date = 0 +is_activated = 0 diff --git a/main.py b/main.py index 6728422..2c2d25f 100644 --- a/main.py +++ b/main.py @@ -12,7 +12,7 @@ import particles as pt from reactor import Reactor import auxiliary as aux -ver = '0.2.6' +ver = '0.3.0' config = aux.ConfigFile("config.toml") @@ -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']) diff --git a/nucleon/陈情表.toml b/nucleon/陈情表.toml index 6618f2c..f8004aa 100644 --- a/nucleon/陈情表.toml +++ b/nucleon/陈情表.toml @@ -1,3 +1,18 @@ +# 散列表的键翻译 +["keydata"] +note = "笔记" +keyword_note = "关键词翻译" +translation = "语句翻译" + +# 测试项目元数据 +["testdata"] +# 记忆时显示的额外信息 +additional_inf = ["translation", "note"] +# 填空测试 +fill_blank = ["translation"] +# 选择题测试 +draw_card = ["keyword_note"] + ["臣密言:臣以险衅, 夙遭闵凶."] note = [] translation = "臣子李密陈言:我因命运不好,小时候遭遇到了不幸" diff --git a/particles.py b/particles.py index a089b3a..07f6b27 100644 --- a/particles.py +++ b/particles.py @@ -122,16 +122,7 @@ 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): @@ -142,6 +133,24 @@ class Nucleon(): def placeholder(): return Nucleon("核子对象样例内容", {}) +class NucleonUnion(): + "取代原有 NucleonFile 类, 以支持复杂逻辑" + def __init__(self, path): + 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 = + def query(content): + + class AtomicFile(): def __init__(self, path, type_="unknown"): self.path = path diff --git a/puzzlemarkers.py b/puzzlemarkers.py new file mode 100644 index 0000000..e69de29 diff --git a/testfield/blank_maker.py b/testfield/blank_maker.py new file mode 100644 index 0000000..97858f9 --- /dev/null +++ b/testfield/blank_maker.py @@ -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) \ No newline at end of file diff --git a/testfield/selection_maker.py b/testfield/selection_maker.py new file mode 100644 index 0000000..149fb8b --- /dev/null +++ b/testfield/selection_maker.py @@ -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) \ No newline at end of file diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..04521a3 --- /dev/null +++ b/todo.md @@ -0,0 +1,3 @@ +- [] 基于释义的评估 +> 使用 EFACTOR 取最低值方法 +- [] 附加属性 \ No newline at end of file