ts-fsrs 5.2.3 → 5.3.1
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/CHANGELOG.md +17 -0
- package/dist/index.cjs +111 -194
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.mjs +111 -195
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +111 -194
- package/dist/index.umd.js.map +1 -1
- package/package.json +25 -36
- package/README.md +0 -174
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# ts-fsrs
|
|
2
|
+
|
|
3
|
+
## 5.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#315](https://github.com/open-spaced-repetition/ts-fsrs/pull/315) [`40e712e`](https://github.com/open-spaced-repetition/ts-fsrs/commit/40e712ebf229997154682294f40436bf1524022d) Thanks [@moaaz-ae](https://github.com/moaaz-ae)! - fix: prevent unexpected skipping of learning steps when Rating.Good is selected in State.Learning (#311)
|
|
8
|
+
|
|
9
|
+
## 5.3.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [#266](https://github.com/open-spaced-repetition/ts-fsrs/pull/266) [`17fb34d`](https://github.com/open-spaced-repetition/ts-fsrs/commit/17fb34d849c66f2035c5a239e2dfd64ed40055c9) Thanks [@ishiko732](https://github.com/ishiko732)! - Non-decreasing SInc(Hard) in same-day reviews — sync with fsrs-rs#376 changes
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- [#314](https://github.com/open-spaced-repetition/ts-fsrs/pull/314) [`87f94f2`](https://github.com/open-spaced-repetition/ts-fsrs/commit/87f94f277f7030a449490cef943e1aebe1585b64) Thanks [@user1823](https://github.com/user1823)! - Performance improvements
|
package/dist/index.cjs
CHANGED
|
@@ -54,6 +54,9 @@ class TypeConvert {
|
|
|
54
54
|
throw new Error(`Invalid state:[${value}]`);
|
|
55
55
|
}
|
|
56
56
|
static time(value) {
|
|
57
|
+
if (value instanceof Date) {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
57
60
|
const date = new Date(value);
|
|
58
61
|
if (typeof value === "object" && value !== null && !Number.isNaN(Date.parse(value) || +date)) {
|
|
59
62
|
return date;
|
|
@@ -80,15 +83,19 @@ class TypeConvert {
|
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
|
|
86
|
+
/* istanbul ignore next -- @preserve */
|
|
83
87
|
Date.prototype.scheduler = function(t, isDay) {
|
|
84
88
|
return date_scheduler(this, t, isDay);
|
|
85
89
|
};
|
|
90
|
+
/* istanbul ignore next -- @preserve */
|
|
86
91
|
Date.prototype.diff = function(pre, unit) {
|
|
87
92
|
return date_diff(this, pre, unit);
|
|
88
93
|
};
|
|
94
|
+
/* istanbul ignore next -- @preserve */
|
|
89
95
|
Date.prototype.format = function() {
|
|
90
96
|
return formatDate(this);
|
|
91
97
|
};
|
|
98
|
+
/* istanbul ignore next -- @preserve */
|
|
92
99
|
Date.prototype.dueFormat = function(last_review, unit, timeUnit) {
|
|
93
100
|
return show_diff_message(this, last_review, unit, timeUnit);
|
|
94
101
|
};
|
|
@@ -148,12 +155,15 @@ function show_diff_message(due, last_review, unit, timeUnit = TIMEUNITFORMAT) {
|
|
|
148
155
|
}
|
|
149
156
|
return `${Math.floor(diff)}${unit ? timeUnit[i] : ""}`;
|
|
150
157
|
}
|
|
158
|
+
/* istanbul ignore next -- @preserve */
|
|
151
159
|
function fixDate(value) {
|
|
152
160
|
return TypeConvert.time(value);
|
|
153
161
|
}
|
|
162
|
+
/* istanbul ignore next -- @preserve */
|
|
154
163
|
function fixState(value) {
|
|
155
164
|
return TypeConvert.state(value);
|
|
156
165
|
}
|
|
166
|
+
/* istanbul ignore next -- @preserve */
|
|
157
167
|
function fixRating(value) {
|
|
158
168
|
return TypeConvert.rating(value);
|
|
159
169
|
}
|
|
@@ -197,6 +207,10 @@ function get_fuzz_range(interval, elapsed_days, maximum_interval) {
|
|
|
197
207
|
function clamp(value, min, max) {
|
|
198
208
|
return Math.min(Math.max(value, min), max);
|
|
199
209
|
}
|
|
210
|
+
function roundTo(num, decimals) {
|
|
211
|
+
const factor = 10 ** decimals;
|
|
212
|
+
return Math.round(num * factor) / factor;
|
|
213
|
+
}
|
|
200
214
|
function dateDiffInDays(last, cur) {
|
|
201
215
|
const utc1 = Date.UTC(
|
|
202
216
|
last.getUTCFullYear(),
|
|
@@ -469,7 +483,7 @@ function alea(seed) {
|
|
|
469
483
|
return prng;
|
|
470
484
|
}
|
|
471
485
|
|
|
472
|
-
const version="5.
|
|
486
|
+
const version="5.3.1";
|
|
473
487
|
|
|
474
488
|
const default_request_retention = 0.9;
|
|
475
489
|
const default_maximum_interval = 36500;
|
|
@@ -643,11 +657,11 @@ function createEmptyCard(now, afterHandler) {
|
|
|
643
657
|
const computeDecayFactor = (decayOrParams) => {
|
|
644
658
|
const decay = typeof decayOrParams === "number" ? -decayOrParams : -decayOrParams[20];
|
|
645
659
|
const factor = Math.exp(Math.pow(decay, -1) * Math.log(0.9)) - 1;
|
|
646
|
-
return { decay, factor:
|
|
660
|
+
return { decay, factor: roundTo(factor, 8) };
|
|
647
661
|
};
|
|
648
662
|
function forgetting_curve(decayOrParams, elapsed_days, stability) {
|
|
649
663
|
const { decay, factor } = computeDecayFactor(decayOrParams);
|
|
650
|
-
return
|
|
664
|
+
return roundTo(Math.pow(1 + factor * elapsed_days / stability, decay), 8);
|
|
651
665
|
}
|
|
652
666
|
class FSRSAlgorithm {
|
|
653
667
|
param;
|
|
@@ -681,7 +695,7 @@ class FSRSAlgorithm {
|
|
|
681
695
|
throw new Error("Requested retention rate should be in the range (0,1]");
|
|
682
696
|
}
|
|
683
697
|
const { decay, factor } = computeDecayFactor(this.param.w);
|
|
684
|
-
return
|
|
698
|
+
return roundTo((Math.pow(request_retention, 1 / decay) - 1) / factor, 8);
|
|
685
699
|
}
|
|
686
700
|
/**
|
|
687
701
|
* Get the parameters of the algorithm.
|
|
@@ -748,8 +762,9 @@ class FSRSAlgorithm {
|
|
|
748
762
|
* @return {number} Difficulty $$D \in [1,10]$$
|
|
749
763
|
*/
|
|
750
764
|
init_difficulty(g) {
|
|
751
|
-
const
|
|
752
|
-
|
|
765
|
+
const w = this.param.w;
|
|
766
|
+
const d = w[4] - Math.exp((g - 1) * w[5]) + 1;
|
|
767
|
+
return roundTo(d, 8);
|
|
753
768
|
}
|
|
754
769
|
/**
|
|
755
770
|
* If fuzzing is disabled or ivl is less than 2.5, it returns the original interval.
|
|
@@ -784,7 +799,7 @@ class FSRSAlgorithm {
|
|
|
784
799
|
* @see https://github.com/open-spaced-repetition/fsrs4anki/issues/697
|
|
785
800
|
*/
|
|
786
801
|
linear_damping(delta_d, old_d) {
|
|
787
|
-
return
|
|
802
|
+
return roundTo(delta_d * (10 - old_d) / 9, 8);
|
|
788
803
|
}
|
|
789
804
|
/**
|
|
790
805
|
* The formula used is :
|
|
@@ -812,9 +827,8 @@ class FSRSAlgorithm {
|
|
|
812
827
|
* @return {number} difficulty
|
|
813
828
|
*/
|
|
814
829
|
mean_reversion(init, current) {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
);
|
|
830
|
+
const w = this.param.w;
|
|
831
|
+
return roundTo(w[7] * init + (1 - w[7]) * current, 8);
|
|
818
832
|
}
|
|
819
833
|
/**
|
|
820
834
|
* The formula used is :
|
|
@@ -826,13 +840,17 @@ class FSRSAlgorithm {
|
|
|
826
840
|
* @return {number} S^\prime_r new stability after recall
|
|
827
841
|
*/
|
|
828
842
|
next_recall_stability(d, s, r, g) {
|
|
829
|
-
const
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
843
|
+
const w = this.param.w;
|
|
844
|
+
const hard_penalty = Rating.Hard === g ? w[15] : 1;
|
|
845
|
+
const easy_bound = Rating.Easy === g ? w[16] : 1;
|
|
846
|
+
return roundTo(
|
|
847
|
+
clamp(
|
|
848
|
+
s * (1 + Math.exp(w[8]) * (11 - d) * Math.pow(s, -w[9]) * (Math.exp((1 - r) * w[10]) - 1) * hard_penalty * easy_bound),
|
|
849
|
+
S_MIN,
|
|
850
|
+
36500
|
|
851
|
+
),
|
|
852
|
+
8
|
|
853
|
+
);
|
|
836
854
|
}
|
|
837
855
|
/**
|
|
838
856
|
* The formula used is :
|
|
@@ -845,11 +863,15 @@ class FSRSAlgorithm {
|
|
|
845
863
|
* @return {number} S^\prime_f new stability after forgetting
|
|
846
864
|
*/
|
|
847
865
|
next_forget_stability(d, s, r) {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
866
|
+
const w = this.param.w;
|
|
867
|
+
return roundTo(
|
|
868
|
+
clamp(
|
|
869
|
+
w[11] * Math.pow(d, -w[12]) * (Math.pow(s + 1, w[13]) - 1) * Math.exp((1 - r) * w[14]),
|
|
870
|
+
S_MIN,
|
|
871
|
+
36500
|
|
872
|
+
),
|
|
873
|
+
8
|
|
874
|
+
);
|
|
853
875
|
}
|
|
854
876
|
/**
|
|
855
877
|
* The formula used is :
|
|
@@ -858,9 +880,10 @@ class FSRSAlgorithm {
|
|
|
858
880
|
* @param {Grade} g Grade (Rating[0.again,1.hard,2.good,3.easy])
|
|
859
881
|
*/
|
|
860
882
|
next_short_term_stability(s, g) {
|
|
861
|
-
const
|
|
862
|
-
const
|
|
863
|
-
|
|
883
|
+
const w = this.param.w;
|
|
884
|
+
const sinc = Math.pow(s, -w[19]) * Math.exp(w[17] * (g - 3 + w[18]));
|
|
885
|
+
const maskedSinc = g >= Rating.Hard ? Math.max(sinc, 1) : sinc;
|
|
886
|
+
return roundTo(clamp(s * maskedSinc, S_MIN, 36500), 8);
|
|
864
887
|
}
|
|
865
888
|
/**
|
|
866
889
|
* The formula used is :
|
|
@@ -876,9 +899,10 @@ class FSRSAlgorithm {
|
|
|
876
899
|
* @param memory_state - The current state of memory, which can be null.
|
|
877
900
|
* @param t - The time elapsed since the last review.
|
|
878
901
|
* @param {Rating} g Grade (Rating[0.Manual,1.Again,2.Hard,3.Good,4.Easy])
|
|
902
|
+
* @param r - Optional retrievability value. If not provided, it will be calculated.
|
|
879
903
|
* @returns The next state of memory with updated difficulty and stability.
|
|
880
904
|
*/
|
|
881
|
-
next_state(memory_state, t, g) {
|
|
905
|
+
next_state(memory_state, t, g, r) {
|
|
882
906
|
const { difficulty: d, stability: s } = memory_state ?? {
|
|
883
907
|
difficulty: 0,
|
|
884
908
|
stability: 0
|
|
@@ -906,22 +930,22 @@ class FSRSAlgorithm {
|
|
|
906
930
|
`Invalid memory state { difficulty: ${d}, stability: ${s} }`
|
|
907
931
|
);
|
|
908
932
|
}
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
if (g === 1) {
|
|
933
|
+
const w = this.param.w;
|
|
934
|
+
r = typeof r === "number" ? r : this.forgetting_curve(t, s);
|
|
935
|
+
let new_s;
|
|
936
|
+
if (t === 0 && this.param.enable_short_term) {
|
|
937
|
+
new_s = this.next_short_term_stability(s, g);
|
|
938
|
+
} else if (g === 1) {
|
|
939
|
+
const s_after_fail = this.next_forget_stability(d, s, r);
|
|
915
940
|
let [w_17, w_18] = [0, 0];
|
|
916
941
|
if (this.param.enable_short_term) {
|
|
917
|
-
w_17 =
|
|
918
|
-
w_18 =
|
|
942
|
+
w_17 = w[17];
|
|
943
|
+
w_18 = w[18];
|
|
919
944
|
}
|
|
920
945
|
const next_s_min = s / Math.exp(w_17 * w_18);
|
|
921
|
-
new_s = clamp(
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
new_s = s_after_short_term;
|
|
946
|
+
new_s = clamp(roundTo(next_s_min, 8), S_MIN, s_after_fail);
|
|
947
|
+
} else {
|
|
948
|
+
new_s = this.next_recall_stability(d, s, r, g);
|
|
925
949
|
}
|
|
926
950
|
const new_d = this.next_difficulty(d, g);
|
|
927
951
|
return { difficulty: new_d, stability: new_s };
|
|
@@ -947,9 +971,7 @@ class BasicScheduler extends AbstractScheduler {
|
|
|
947
971
|
const steps_strategy = this.learningStepsStrategy(
|
|
948
972
|
parameters,
|
|
949
973
|
card.state,
|
|
950
|
-
|
|
951
|
-
// not adding 1 can cause slight variations in the memory state’s ds.
|
|
952
|
-
this.current.state === State.Learning && grade !== Rating.Again && grade !== Rating.Hard ? card.learning_steps + 1 : card.learning_steps
|
|
974
|
+
card.learning_steps
|
|
953
975
|
);
|
|
954
976
|
const scheduled_minutes = Math.max(
|
|
955
977
|
0,
|
|
@@ -1006,9 +1028,7 @@ class BasicScheduler extends AbstractScheduler {
|
|
|
1006
1028
|
if (exist) {
|
|
1007
1029
|
return exist;
|
|
1008
1030
|
}
|
|
1009
|
-
const next =
|
|
1010
|
-
next.difficulty = clamp(this.algorithm.init_difficulty(grade), 1, 10);
|
|
1011
|
-
next.stability = this.algorithm.init_stability(grade);
|
|
1031
|
+
const next = this.next_ds(this.elapsed_days, grade);
|
|
1012
1032
|
this.applyLearningSteps(next, grade, State.Learning);
|
|
1013
1033
|
const item = {
|
|
1014
1034
|
card: next,
|
|
@@ -1022,14 +1042,11 @@ class BasicScheduler extends AbstractScheduler {
|
|
|
1022
1042
|
if (exist) {
|
|
1023
1043
|
return exist;
|
|
1024
1044
|
}
|
|
1025
|
-
const
|
|
1026
|
-
const next = TypeConvert.card(this.current);
|
|
1027
|
-
next.difficulty = this.algorithm.next_difficulty(difficulty, grade);
|
|
1028
|
-
next.stability = this.algorithm.next_short_term_stability(stability, grade);
|
|
1045
|
+
const next = this.next_ds(this.elapsed_days, grade);
|
|
1029
1046
|
this.applyLearningSteps(
|
|
1030
1047
|
next,
|
|
1031
1048
|
grade,
|
|
1032
|
-
state
|
|
1049
|
+
this.last.state
|
|
1033
1050
|
/** Learning or Relearning */
|
|
1034
1051
|
);
|
|
1035
1052
|
const item = {
|
|
@@ -1045,21 +1062,14 @@ class BasicScheduler extends AbstractScheduler {
|
|
|
1045
1062
|
return exist;
|
|
1046
1063
|
}
|
|
1047
1064
|
const interval = this.elapsed_days;
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
const next_hard = TypeConvert.card(this.current);
|
|
1052
|
-
const next_good = TypeConvert.card(this.current);
|
|
1053
|
-
const next_easy = TypeConvert.card(this.current);
|
|
1054
|
-
this.next_ds(
|
|
1055
|
-
next_again,
|
|
1056
|
-
next_hard,
|
|
1057
|
-
next_good,
|
|
1058
|
-
next_easy,
|
|
1059
|
-
difficulty,
|
|
1060
|
-
stability,
|
|
1061
|
-
retrievability
|
|
1065
|
+
const retrievability = this.algorithm.forgetting_curve(
|
|
1066
|
+
interval,
|
|
1067
|
+
this.current.stability
|
|
1062
1068
|
);
|
|
1069
|
+
const next_again = this.next_ds(interval, Rating.Again, retrievability);
|
|
1070
|
+
const next_hard = this.next_ds(interval, Rating.Hard, retrievability);
|
|
1071
|
+
const next_good = this.next_ds(interval, Rating.Good, retrievability);
|
|
1072
|
+
const next_easy = this.next_ds(interval, Rating.Easy, retrievability);
|
|
1063
1073
|
this.next_interval(next_hard, next_good, next_easy, interval);
|
|
1064
1074
|
this.next_state(next_hard, next_good, next_easy);
|
|
1065
1075
|
this.applyLearningSteps(next_again, Rating.Again, State.Relearning);
|
|
@@ -1089,50 +1099,20 @@ class BasicScheduler extends AbstractScheduler {
|
|
|
1089
1099
|
/**
|
|
1090
1100
|
* Review next_ds
|
|
1091
1101
|
*/
|
|
1092
|
-
next_ds(
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
difficulty,
|
|
1102
|
-
stability,
|
|
1103
|
-
retrievability
|
|
1104
|
-
);
|
|
1105
|
-
next_again.stability = clamp(+nextSMin.toFixed(8), S_MIN, s_after_fail);
|
|
1106
|
-
next_hard.difficulty = this.algorithm.next_difficulty(
|
|
1107
|
-
difficulty,
|
|
1108
|
-
Rating.Hard
|
|
1109
|
-
);
|
|
1110
|
-
next_hard.stability = this.algorithm.next_recall_stability(
|
|
1111
|
-
difficulty,
|
|
1112
|
-
stability,
|
|
1113
|
-
retrievability,
|
|
1114
|
-
Rating.Hard
|
|
1115
|
-
);
|
|
1116
|
-
next_good.difficulty = this.algorithm.next_difficulty(
|
|
1117
|
-
difficulty,
|
|
1118
|
-
Rating.Good
|
|
1119
|
-
);
|
|
1120
|
-
next_good.stability = this.algorithm.next_recall_stability(
|
|
1121
|
-
difficulty,
|
|
1122
|
-
stability,
|
|
1123
|
-
retrievability,
|
|
1124
|
-
Rating.Good
|
|
1125
|
-
);
|
|
1126
|
-
next_easy.difficulty = this.algorithm.next_difficulty(
|
|
1127
|
-
difficulty,
|
|
1128
|
-
Rating.Easy
|
|
1129
|
-
);
|
|
1130
|
-
next_easy.stability = this.algorithm.next_recall_stability(
|
|
1131
|
-
difficulty,
|
|
1132
|
-
stability,
|
|
1133
|
-
retrievability,
|
|
1134
|
-
Rating.Easy
|
|
1102
|
+
next_ds(t, g, r) {
|
|
1103
|
+
const next_state = this.algorithm.next_state(
|
|
1104
|
+
{
|
|
1105
|
+
difficulty: this.current.difficulty,
|
|
1106
|
+
stability: this.current.stability
|
|
1107
|
+
},
|
|
1108
|
+
t,
|
|
1109
|
+
g,
|
|
1110
|
+
r
|
|
1135
1111
|
);
|
|
1112
|
+
const card = TypeConvert.card(this.current);
|
|
1113
|
+
card.difficulty = next_state.difficulty;
|
|
1114
|
+
card.stability = next_state.stability;
|
|
1115
|
+
return card;
|
|
1136
1116
|
}
|
|
1137
1117
|
/**
|
|
1138
1118
|
* Review next_interval
|
|
@@ -1175,12 +1155,11 @@ class LongTermScheduler extends AbstractScheduler {
|
|
|
1175
1155
|
}
|
|
1176
1156
|
this.current.scheduled_days = 0;
|
|
1177
1157
|
this.current.elapsed_days = 0;
|
|
1178
|
-
const next_again = TypeConvert.card(this.current);
|
|
1179
|
-
const next_hard = TypeConvert.card(this.current);
|
|
1180
|
-
const next_good = TypeConvert.card(this.current);
|
|
1181
|
-
const next_easy = TypeConvert.card(this.current);
|
|
1182
|
-
this.init_ds(next_again, next_hard, next_good, next_easy);
|
|
1183
1158
|
const first_interval = 0;
|
|
1159
|
+
const next_again = this.next_ds(first_interval, Rating.Again);
|
|
1160
|
+
const next_hard = this.next_ds(first_interval, Rating.Hard);
|
|
1161
|
+
const next_good = this.next_ds(first_interval, Rating.Good);
|
|
1162
|
+
const next_easy = this.next_ds(first_interval, Rating.Easy);
|
|
1184
1163
|
this.next_interval(
|
|
1185
1164
|
next_again,
|
|
1186
1165
|
next_hard,
|
|
@@ -1192,31 +1171,20 @@ class LongTermScheduler extends AbstractScheduler {
|
|
|
1192
1171
|
this.update_next(next_again, next_hard, next_good, next_easy);
|
|
1193
1172
|
return this.next.get(grade);
|
|
1194
1173
|
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
1,
|
|
1205
|
-
10
|
|
1206
|
-
);
|
|
1207
|
-
next_hard.stability = this.algorithm.init_stability(Rating.Hard);
|
|
1208
|
-
next_good.difficulty = clamp(
|
|
1209
|
-
this.algorithm.init_difficulty(Rating.Good),
|
|
1210
|
-
1,
|
|
1211
|
-
10
|
|
1212
|
-
);
|
|
1213
|
-
next_good.stability = this.algorithm.init_stability(Rating.Good);
|
|
1214
|
-
next_easy.difficulty = clamp(
|
|
1215
|
-
this.algorithm.init_difficulty(Rating.Easy),
|
|
1216
|
-
1,
|
|
1217
|
-
10
|
|
1174
|
+
next_ds(t, g, r) {
|
|
1175
|
+
const next_state = this.algorithm.next_state(
|
|
1176
|
+
{
|
|
1177
|
+
difficulty: this.current.difficulty,
|
|
1178
|
+
stability: this.current.stability
|
|
1179
|
+
},
|
|
1180
|
+
t,
|
|
1181
|
+
g,
|
|
1182
|
+
r
|
|
1218
1183
|
);
|
|
1219
|
-
|
|
1184
|
+
const card = TypeConvert.card(this.current);
|
|
1185
|
+
card.difficulty = next_state.difficulty;
|
|
1186
|
+
card.stability = next_state.stability;
|
|
1187
|
+
return card;
|
|
1220
1188
|
}
|
|
1221
1189
|
/**
|
|
1222
1190
|
* @see https://github.com/open-spaced-repetition/ts-fsrs/issues/98#issuecomment-2241923194
|
|
@@ -1230,72 +1198,20 @@ class LongTermScheduler extends AbstractScheduler {
|
|
|
1230
1198
|
return exist;
|
|
1231
1199
|
}
|
|
1232
1200
|
const interval = this.elapsed_days;
|
|
1233
|
-
const
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
const next_hard = TypeConvert.card(this.current);
|
|
1237
|
-
const next_good = TypeConvert.card(this.current);
|
|
1238
|
-
const next_easy = TypeConvert.card(this.current);
|
|
1239
|
-
this.next_ds(
|
|
1240
|
-
next_again,
|
|
1241
|
-
next_hard,
|
|
1242
|
-
next_good,
|
|
1243
|
-
next_easy,
|
|
1244
|
-
difficulty,
|
|
1245
|
-
stability,
|
|
1246
|
-
retrievability
|
|
1201
|
+
const retrievability = this.algorithm.forgetting_curve(
|
|
1202
|
+
interval,
|
|
1203
|
+
this.current.stability
|
|
1247
1204
|
);
|
|
1205
|
+
const next_again = this.next_ds(interval, Rating.Again, retrievability);
|
|
1206
|
+
const next_hard = this.next_ds(interval, Rating.Hard, retrievability);
|
|
1207
|
+
const next_good = this.next_ds(interval, Rating.Good, retrievability);
|
|
1208
|
+
const next_easy = this.next_ds(interval, Rating.Easy, retrievability);
|
|
1248
1209
|
this.next_interval(next_again, next_hard, next_good, next_easy, interval);
|
|
1249
1210
|
this.next_state(next_again, next_hard, next_good, next_easy);
|
|
1250
1211
|
next_again.lapses += 1;
|
|
1251
1212
|
this.update_next(next_again, next_hard, next_good, next_easy);
|
|
1252
1213
|
return this.next.get(grade);
|
|
1253
1214
|
}
|
|
1254
|
-
/**
|
|
1255
|
-
* Review next_ds
|
|
1256
|
-
*/
|
|
1257
|
-
next_ds(next_again, next_hard, next_good, next_easy, difficulty, stability, retrievability) {
|
|
1258
|
-
next_again.difficulty = this.algorithm.next_difficulty(
|
|
1259
|
-
difficulty,
|
|
1260
|
-
Rating.Again
|
|
1261
|
-
);
|
|
1262
|
-
const s_after_fail = this.algorithm.next_forget_stability(
|
|
1263
|
-
difficulty,
|
|
1264
|
-
stability,
|
|
1265
|
-
retrievability
|
|
1266
|
-
);
|
|
1267
|
-
next_again.stability = clamp(stability, S_MIN, s_after_fail);
|
|
1268
|
-
next_hard.difficulty = this.algorithm.next_difficulty(
|
|
1269
|
-
difficulty,
|
|
1270
|
-
Rating.Hard
|
|
1271
|
-
);
|
|
1272
|
-
next_hard.stability = this.algorithm.next_recall_stability(
|
|
1273
|
-
difficulty,
|
|
1274
|
-
stability,
|
|
1275
|
-
retrievability,
|
|
1276
|
-
Rating.Hard
|
|
1277
|
-
);
|
|
1278
|
-
next_good.difficulty = this.algorithm.next_difficulty(
|
|
1279
|
-
difficulty,
|
|
1280
|
-
Rating.Good
|
|
1281
|
-
);
|
|
1282
|
-
next_good.stability = this.algorithm.next_recall_stability(
|
|
1283
|
-
difficulty,
|
|
1284
|
-
stability,
|
|
1285
|
-
retrievability,
|
|
1286
|
-
Rating.Good
|
|
1287
|
-
);
|
|
1288
|
-
next_easy.difficulty = this.algorithm.next_difficulty(
|
|
1289
|
-
difficulty,
|
|
1290
|
-
Rating.Easy
|
|
1291
|
-
);
|
|
1292
|
-
next_easy.stability = this.algorithm.next_recall_stability(
|
|
1293
|
-
difficulty,
|
|
1294
|
-
stability,
|
|
1295
|
-
retrievability,
|
|
1296
|
-
Rating.Easy
|
|
1297
|
-
);
|
|
1298
|
-
}
|
|
1299
1215
|
/**
|
|
1300
1216
|
* Review/New next_interval
|
|
1301
1217
|
*/
|
|
@@ -1983,6 +1899,7 @@ exports.fsrs = fsrs;
|
|
|
1983
1899
|
exports.generatorParameters = generatorParameters;
|
|
1984
1900
|
exports.get_fuzz_range = get_fuzz_range;
|
|
1985
1901
|
exports.migrateParameters = migrateParameters;
|
|
1902
|
+
exports.roundTo = roundTo;
|
|
1986
1903
|
exports.show_diff_message = show_diff_message;
|
|
1987
1904
|
module.exports = Object.assign(exports.default || {}, exports)
|
|
1988
1905
|
//# sourceMappingURL=index.cjs.map
|