ts-fsrs 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/fsrs.d.ts CHANGED
@@ -3,33 +3,83 @@ import { Card, FSRSParameters, SchedulingCard } from "./index";
3
3
  export default class FSRS {
4
4
  private param;
5
5
  private readonly intervalModifier;
6
- private seed;
6
+ private seed?;
7
7
  constructor(param?: FSRSParameters);
8
8
  repeat: (card: Card, now: dayjs.Dayjs) => import("./models").SchedulingLog;
9
+ get_retrievability: (card: Card, now: dayjs.Dayjs) => undefined | string;
9
10
  init_ds(s: SchedulingCard): void;
10
11
  /**
11
12
  *
12
- * @param s 调度卡片
13
- * @param last_d 难度
14
- * @param last_s 稳定性
15
- * @param retrievability 可提取性
13
+ * @param s scheduling Card
14
+ * @param last_d Difficulty
15
+ * @param last_s Stability
16
+ * @param retrievability Retrievability
16
17
  */
17
18
  next_ds(s: SchedulingCard, last_d: number, last_s: number, retrievability: number): void;
18
19
  /**
19
- * 初始化记忆稳定化衰减
20
- * @param r Rating
20
+ * The formula used is :
21
+ * $$S_0(G) = w_0 + G \cdot w_1$$
22
+ * $$\max \{S_0,0.1\}$$
23
+ * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
24
+ * @return Stability (interval when R=90%)
21
25
  */
22
- init_stability(r: number): number;
26
+ init_stability(g: number): number;
23
27
  /**
24
- * 初始化记忆稳定化曲线
25
- * @param r Rating
28
+ * The formula used is :
29
+ * $$D_0(G) = w_2 + (G-2) \cdot w_3$$
30
+ * $$\min \{\max \{D_0(G),1\},10\}$$
31
+ * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
32
+ * @return Difficulty D \in [1,10]
26
33
  */
27
- init_difficulty(r: number): number;
34
+ init_difficulty(g: number): number;
28
35
  apply_fuzz(ivl: number): number;
29
36
  next_interval(s: number): number;
37
+ /**
38
+ * The formula used is :
39
+ * $$next_d = D + w_4 \cdot (R - 2)$$
40
+ * $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
41
+ * @param d
42
+ * @param r Rating[0.again,1.hard,2.good,3.easy]
43
+ * @return next_D
44
+ */
30
45
  next_difficulty(d: number, r: number): number;
46
+ /**
47
+ * The formula used is :
48
+ * $$\min \{\max \{D_0,1\},10\}$$
49
+ */
31
50
  constrain_difficulty(difficulty: number): number;
51
+ /**
52
+ * The formula used is :
53
+ * $$w_5 \cdot init +(1 - w_5) \cdot current$$
54
+ * @param init $$w_2 : D_0(2) = w_2 + (R-2) \cdot w_3= w_2$$
55
+ * @param current $$D + w_4 \cdot (R - 2)$$
56
+ * @return difficulty
57
+ */
32
58
  mean_reversion(init: number, current: number): number;
59
+ /**
60
+ * The formula used is :
61
+ * $$S^\prime_r(D,S,R) = S\cdot(e^{w_6}\cdot (11-D)\cdot S^{w_7}\cdot(e^{w_8\cdot(1-R)}-1)+1)$$
62
+ * @param d Difficulty D \in [1,10]
63
+ * @param s Stability (interval when R=90%)
64
+ * @param r Retrievability (probability of recall)
65
+ * @return S^\prime_r new stability after recall
66
+ */
33
67
  next_recall_stability(d: number, s: number, r: number): number;
68
+ /**
69
+ * The formula used is :
70
+ * $$S^\prime_f(D,S,R) = w_9\cdot D^{w_{10}}\cdot S^{w_{11}}\cdot e^{w_{12}\cdot(1-R)}.$$
71
+ * @param d Difficulty D \in [1,10]
72
+ * @param s Stability (interval when R=90%)
73
+ * @param r Retrievability (probability of recall)
74
+ * @return S^\prime_f new stability after forgetting
75
+ */
34
76
  next_forget_stability(d: number, s: number, r: number): number;
77
+ /**
78
+ * The formula used is :
79
+ * $$R(t,S) = 0.9^{\frac{t}{S}}$$
80
+ * @param t t days since the last review
81
+ * @param s Stability (interval when R=90%)
82
+ * @return r Retrievability (probability of recall)
83
+ */
84
+ current_retrievability(t: number, s: number): number;
35
85
  }
package/lib/fsrs.js CHANGED
@@ -30,8 +30,8 @@ class FSRS {
30
30
  break;
31
31
  case index_1.State.Learning:
32
32
  case index_1.State.Relearning:
33
- hard_interval = this.next_interval(s.hard.stability);
34
- good_interval = Math.max(this.next_interval(s.good.stability), hard_interval + 1);
33
+ hard_interval = 0;
34
+ good_interval = this.next_interval(s.good.stability);
35
35
  easy_interval = Math.max(this.next_interval(s.easy.stability * this.param.easy_bonus), good_interval + 1);
36
36
  s.schedule(now, hard_interval, good_interval, easy_interval);
37
37
  break;
@@ -39,7 +39,7 @@ class FSRS {
39
39
  const interval = card.elapsed_days;
40
40
  const last_d = card.difficulty;
41
41
  const last_s = card.stability;
42
- const retrievability = Math.exp(Math.log(0.9) * interval / last_s);
42
+ const retrievability = this.current_retrievability(interval, last_s);
43
43
  this.next_ds(s, last_d, last_s, retrievability);
44
44
  hard_interval = this.next_interval(last_s * this.param.hard_factor);
45
45
  good_interval = this.next_interval(s.good.stability);
@@ -51,9 +51,15 @@ class FSRS {
51
51
  }
52
52
  return s.record_log(card, now);
53
53
  };
54
+ this.get_retrievability = (card, now) => {
55
+ if (card.state !== index_1.State.Review) {
56
+ return undefined;
57
+ }
58
+ const t = Math.max(now.diff((0, dayjs_1.default)(card.last_review), "days"), 0);
59
+ return (this.current_retrievability(t, card.stability) * 100).toFixed(2) + '%';
60
+ };
54
61
  this.param = param || (0, index_1.generatorParameters)();
55
62
  this.intervalModifier = Math.log(this.param.request_retention) / Math.log(0.9);
56
- this.seed = this.param.enable_fuzz ? "" : String(this.param.maximum_interval) + String(this.param.easy_bonus);
57
63
  }
58
64
  init_ds(s) {
59
65
  s.again.difficulty = this.init_difficulty(index_1.Rating.Again);
@@ -67,10 +73,10 @@ class FSRS {
67
73
  }
68
74
  /**
69
75
  *
70
- * @param s 调度卡片
71
- * @param last_d 难度
72
- * @param last_s 稳定性
73
- * @param retrievability 可提取性
76
+ * @param s scheduling Card
77
+ * @param last_d Difficulty
78
+ * @param last_s Stability
79
+ * @param retrievability Retrievability
74
80
  */
75
81
  next_ds(s, last_d, last_s, retrievability) {
76
82
  s.again.difficulty = this.next_difficulty(last_d, index_1.Rating.Again);
@@ -83,18 +89,24 @@ class FSRS {
83
89
  s.easy.stability = this.next_recall_stability(s.easy.difficulty, last_s, retrievability);
84
90
  }
85
91
  /**
86
- * 初始化记忆稳定化衰减
87
- * @param r Rating
92
+ * The formula used is :
93
+ * $$S_0(G) = w_0 + G \cdot w_1$$
94
+ * $$\max \{S_0,0.1\}$$
95
+ * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
96
+ * @return Stability (interval when R=90%)
88
97
  */
89
- init_stability(r) {
90
- return Math.max(this.param.w[0] + this.param.w[1] * r, 0.1);
98
+ init_stability(g) {
99
+ return Math.max(this.param.w[0] + this.param.w[1] * g, 0.1);
91
100
  }
92
101
  /**
93
- * 初始化记忆稳定化曲线
94
- * @param r Rating
102
+ * The formula used is :
103
+ * $$D_0(G) = w_2 + (G-2) \cdot w_3$$
104
+ * $$\min \{\max \{D_0(G),1\},10\}$$
105
+ * @param g Grade (rating at Anki) [0.again,1.hard,2.good,3.easy]
106
+ * @return Difficulty D \in [1,10]
95
107
  */
96
- init_difficulty(r) {
97
- return Math.min(Math.max(this.param.w[2] + this.param.w[3] * (r - 2), 1), 10);
108
+ init_difficulty(g) {
109
+ return Math.min(Math.max(this.param.w[2] + this.param.w[3] * (g - 2), 1), 10);
98
110
  }
99
111
  apply_fuzz(ivl) {
100
112
  if (!this.param.enable_fuzz || ivl < 2.5)
@@ -110,24 +122,69 @@ class FSRS {
110
122
  const newInterval = this.apply_fuzz(s * this.intervalModifier);
111
123
  return Math.min(Math.max(Math.round(newInterval), 1), this.param.maximum_interval);
112
124
  }
125
+ /**
126
+ * The formula used is :
127
+ * $$next_d = D + w_4 \cdot (R - 2)$$
128
+ * $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
129
+ * @param d
130
+ * @param r Rating[0.again,1.hard,2.good,3.easy]
131
+ * @return next_D
132
+ */
113
133
  next_difficulty(d, r) {
114
134
  const next_d = d + this.param.w[4] * (r - 2);
115
135
  return this.constrain_difficulty(this.mean_reversion(this.param.w[2], next_d));
116
136
  }
137
+ /**
138
+ * The formula used is :
139
+ * $$\min \{\max \{D_0,1\},10\}$$
140
+ */
117
141
  constrain_difficulty(difficulty) {
118
142
  return Math.min(Math.max(Number(difficulty.toFixed(2)), 1), 10);
119
143
  }
144
+ /**
145
+ * The formula used is :
146
+ * $$w_5 \cdot init +(1 - w_5) \cdot current$$
147
+ * @param init $$w_2 : D_0(2) = w_2 + (R-2) \cdot w_3= w_2$$
148
+ * @param current $$D + w_4 \cdot (R - 2)$$
149
+ * @return difficulty
150
+ */
120
151
  mean_reversion(init, current) {
121
152
  return this.param.w[5] * init + (1 - this.param.w[5]) * current;
122
153
  }
154
+ /**
155
+ * The formula used is :
156
+ * $$S^\prime_r(D,S,R) = S\cdot(e^{w_6}\cdot (11-D)\cdot S^{w_7}\cdot(e^{w_8\cdot(1-R)}-1)+1)$$
157
+ * @param d Difficulty D \in [1,10]
158
+ * @param s Stability (interval when R=90%)
159
+ * @param r Retrievability (probability of recall)
160
+ * @return S^\prime_r new stability after recall
161
+ */
123
162
  next_recall_stability(d, s, r) {
124
163
  return s * (1 + Math.exp(this.param.w[6]) *
125
164
  (11 - d) *
126
165
  Math.pow(s, this.param.w[7]) *
127
166
  (Math.exp((1 - r) * this.param.w[8]) - 1));
128
167
  }
168
+ /**
169
+ * The formula used is :
170
+ * $$S^\prime_f(D,S,R) = w_9\cdot D^{w_{10}}\cdot S^{w_{11}}\cdot e^{w_{12}\cdot(1-R)}.$$
171
+ * @param d Difficulty D \in [1,10]
172
+ * @param s Stability (interval when R=90%)
173
+ * @param r Retrievability (probability of recall)
174
+ * @return S^\prime_f new stability after forgetting
175
+ */
129
176
  next_forget_stability(d, s, r) {
130
177
  return this.param.w[9] * Math.pow(d, this.param.w[10]) * Math.pow(s, this.param.w[11]) * Math.exp((1 - r) * this.param.w[12]);
131
178
  }
179
+ /**
180
+ * The formula used is :
181
+ * $$R(t,S) = 0.9^{\frac{t}{S}}$$
182
+ * @param t t days since the last review
183
+ * @param s Stability (interval when R=90%)
184
+ * @return r Retrievability (probability of recall)
185
+ */
186
+ current_retrievability(t, s) {
187
+ return Math.exp(Math.log(0.9) * t / s);
188
+ }
132
189
  }
133
190
  exports.default = FSRS;
package/lib/index.d.ts CHANGED
@@ -18,7 +18,7 @@ declare const generatorParameters: (props?: {
18
18
  w: number[];
19
19
  enable_fuzz: boolean;
20
20
  };
21
- declare const FSRS_Version = 1;
21
+ declare const FSRS_Version = 1.2;
22
22
  export { fsrs, FSRS_Version, State, Rating, SchedulingCard, createEmptyCard, generatorParameters };
23
23
  export type { StateType, RatingType, ReviewLog, Card, SchedulingLog, FSRSParameters };
24
24
  export { default_request_retention, default_maximum_interval, default_easy_bonus, default_hard_factor, default_w, default_enable_fuzz };
package/lib/index.js CHANGED
@@ -56,5 +56,5 @@ const generatorParameters = (props) => {
56
56
  };
57
57
  };
58
58
  exports.generatorParameters = generatorParameters;
59
- const FSRS_Version = 1.00;
59
+ const FSRS_Version = 1.20;
60
60
  exports.FSRS_Version = FSRS_Version;
package/lib/scheduler.js CHANGED
@@ -23,7 +23,7 @@ class SchedulingCard {
23
23
  }
24
24
  else if (state === models_1.State.Learning || state === models_1.State.Relearning) {
25
25
  this.again.state = state;
26
- this.hard.state = models_1.State.Review;
26
+ this.hard.state = state;
27
27
  this.good.state = models_1.State.Review;
28
28
  this.easy.state = models_1.State.Review;
29
29
  }
@@ -42,7 +42,7 @@ class SchedulingCard {
42
42
  this.good.scheduled_days = good_interval;
43
43
  this.easy.scheduled_days = easy_interval;
44
44
  this.again.due = now.add(5, 'minutes');
45
- this.hard.due = now.add(hard_interval, 'days');
45
+ this.hard.due = hard_interval > 0 ? now.add(hard_interval, 'days') : now.add(10, 'minutes');
46
46
  this.good.due = now.add(good_interval, 'days');
47
47
  this.easy.due = now.add(easy_interval, 'days');
48
48
  return this;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-fsrs",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "description": "ts-fsrs is a TypeScript package used to implement the Free Spaced Repetition Scheduler (FSRS) algorithm. It helps developers apply FSRS to their flashcard applications, thereby improving the user learning experience.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -20,11 +20,16 @@
20
20
  },
21
21
  "scripts": {
22
22
  "test": "ts-node test/index.ts",
23
- "build": "tsc"
23
+ "build": "tsc",
24
+ "test_publish": "yalc publish"
24
25
  },
25
26
  "author": "ishiko",
26
27
  "license": "MIT",
27
- "files": ["lib/**/*"],
28
+ "files": [
29
+ "lib/**/*",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
28
33
  "repository": {
29
34
  "type": "git",
30
35
  "url": "git+https://github.com/ishiko732/ts-fsrs.git"