README.md

# fsrs_ex (Fsrs)

_在 Elixir 中使用 FSRS(Free Spaced Repetition Scheduler)构建间隔重复系统。_

`fsrs_ex` 是一个面向 Elixir 的 FSRS 调度器实现,模块名为 `Fsrs`。

本项目定位非常明确:**直接对齐并移植 `open-spaced-repetition/py-fsrs` 的调度器行为**,当前基线为 **py-fsrs v6.3.0(FSRS-6)**。

## 项目状态

- 算法版本:FSRS-6(21 参数)
- 对齐基线:`py-fsrs v6.3.0`
- 许可证:MIT
- 仓库定位:公开仓库(public)

## 这个库做了什么

- 提供 `Scheduler` / `Card` / `ReviewLog` / `Rating` / `State` 的完整建模
- 支持学习、复习、再学习三种状态迁移
- 支持 `reschedule_card/3`(按历史日志重放重排)
- 支持 `to_dict` / `from_dict` 与 `to_json` / `from_json`
- 与 Python 版本保持跨语言数据互通字段格式(含 `+00:00` 时间格式)

## 直接移植(Port)范围说明

本项目明确是从 Python 版本移植,重点对齐如下:

- 默认参数(21 个)与 py-fsrs v6.3.0 一致
- 参数数量和边界校验与 py-fsrs 行为一致
- 可回忆性计算使用“按天 elapsed days”语义(非小数天)
- `reschedule_card` 语义与 py-fsrs 保持一致(校验 card_id、排序日志后重放)
- 序列化字段名与结构保持一致

## 安装

在 `mix.exs` 中加入依赖:

```elixir
def deps do
  [
    {:fsrs_ex, "~> 0.1.0"}
  ]
end
```

然后执行:

```bash
mix deps.get
```

## 快速开始

```elixir
alias Fsrs

# 1) 创建调度器
scheduler = Fsrs.new_scheduler(enable_fuzzing: false)

# 2) 创建卡片
card = Fsrs.new_card()

# 3) 评分复习(:again | :hard | :good | :easy)
{updated_card, review_log} = Fsrs.review_card(scheduler, card, :good)

updated_card.due
review_log.rating
```

## 常用用法

### 自定义调度器

```elixir
scheduler = Fsrs.new_scheduler(
  desired_retention: 0.9,
  learning_steps: [{:minutes, 1}, {:seconds, 95}, 300],
  relearning_steps: [{:seconds, 90}, {:minutes, 15}],
  maximum_interval: 36500,
  enable_fuzzing: true
)
```

说明:

- `learning_steps` / `relearning_steps` 内部统一保存为秒
- 支持三种输入:`60`、`{:seconds, 60}`、`{:minutes, 1}`

### 计算可回忆性

```elixir
retrievability = Fsrs.get_card_retrievability(scheduler, updated_card)
```

### 使用历史日志重排卡片状态

```elixir
rescheduled = Fsrs.reschedule_card(scheduler, card, review_logs)
```

### 序列化(跨语言互通)

```elixir
json = Fsrs.Card.to_json(updated_card)
card2 = Fsrs.Card.from_json(json)

map = Fsrs.Scheduler.to_dict(scheduler)
scheduler2 = Fsrs.Scheduler.from_dict(map)
```

## Port 对拍验证(Python vs Elixir)

本仓库包含完整对拍资产,均已纳入版本管理:

- Python 生成脚本:`test/fixtures/generate_py_fixture.py`
- 固定夹具文件:`test/fixtures/py_fsrs_v6_3_0_fixture.json`
- Elixir 对拍测试:`test/fsrs_py_parity_test.exs`

你可以本地复现:

```bash
python3 -m venv .venv
.venv/bin/pip install fsrs==6.3.0
.venv/bin/python test/fixtures/generate_py_fixture.py test/fixtures/py_fsrs_v6_3_0_fixture.json
mix test test/fsrs_py_parity_test.exs
```

## 致谢与参考

### 核心来源(Port 基线)

- `open-spaced-repetition/py-fsrs`(本项目直接移植基线)
  - https://github.com/open-spaced-repetition/py-fsrs

### 算法文档与官方生态

- `open-spaced-repetition/fsrs4anki` Wiki: The Algorithm(FSRS-6 公式与参数)
  - https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm
- `open-spaced-repetition/free-spaced-repetition-scheduler`
  - https://github.com/open-spaced-repetition/free-spaced-repetition-scheduler
- `open-spaced-repetition/fsrs-rs`
  - https://github.com/open-spaced-repetition/fsrs-rs

### 算法作者与社区贡献者

- Jarrett Ye(L-M-Sherlock)
  - GitHub: https://github.com/L-M-Sherlock
  - X: https://x.com/JarrettYe

> 感谢 open-spaced-repetition 社区及所有贡献者。本项目是面向 Elixir 生态的工程化移植实现,不是官方仓库。

## 许可证

MIT,详见 `LICENSE`。