1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
package coffee.liz.rookery
import coffee.liz.rookery.srs.DrillPicker
import coffee.liz.rookery.srs.Fsrs
import coffee.liz.rookery.srs.Grade
import coffee.liz.rookery.srs.Memory
import coffee.liz.rookery.srs.Review
import coffee.liz.rookery.srs.gradeAnswer
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class SrsTest {
private val day = Memory.DAY_MS.toLong()
@Test fun goodReviewsGrowStabilityAndMissesShrinkIt() {
var m = Fsrs.first(Review("AB", 0, Grade.GOOD))
val s0 = m.stability
m = Fsrs.next(m, Review("AB", 3 * day, Grade.GOOD))
assertTrue(m.stability > s0)
val before = m.stability
m = Fsrs.next(m, Review("AB", 10 * day, Grade.AGAIN))
assertTrue(m.stability < before)
assertEquals(1, m.lapses)
assertEquals(3, m.reps)
}
@Test fun difficultyTracksGrades() {
val easy = Fsrs.replay(List(5) { Review("x", it * day, Grade.EASY) })!!
val hard = Fsrs.replay(List(5) { Review("x", it * day, Grade.AGAIN) })!!
assertTrue(easy.difficulty < hard.difficulty)
assertTrue(hard.difficulty <= 10.0 && easy.difficulty >= 1.0)
}
@Test fun replayIsOrderIndependent() {
val rs = listOf(Review("x", 5 * day, Grade.HARD), Review("x", 0, Grade.GOOD), Review("x", 2 * day, Grade.AGAIN))
assertEquals(Fsrs.replay(rs), Fsrs.replay(rs.reversed()))
}
@Test fun retrievabilityIsNinetyPercentAfterStabilityDays() {
val m = Memory(stability = 4.0, difficulty = 5.0, lastReview = 0, reps = 1, lapses = 0)
assertEquals(0.9, m.retrievability(4 * day), 1e-9)
assertEquals(1.0, m.retrievability(0), 1e-9)
}
@Test fun answerGrading() {
assertEquals(Grade.AGAIN, gradeAnswer(false, 100))
assertEquals(Grade.EASY, gradeAnswer(true, 1_000))
assertEquals(Grade.GOOD, gradeAnswer(true, 2_000))
assertEquals(Grade.HARD, gradeAnswer(true, 6_000))
}
@Test fun pickerFavoursWeakAndMissedItems() {
val now = 30 * day
val strong = Memory(100.0, 2.0, now - day, 10, 0)
val weak = Memory(1.0, 9.0, now - 10 * day, 10, 6)
val items = listOf("strong", "weak")
val picker = DrillPicker(Random(1))
val counts = items.associateWith { 0 }.toMutableMap()
repeat(2_000) {
val p = picker.pick(items, { if (it == "strong") strong else weak }, { if (it == "weak") 2 else 0 }, emptyList(), now)!!
counts[p] = counts.getValue(p) + 1
}
assertTrue(counts.getValue("weak") > 8 * counts.getValue("strong"), "$counts")
}
@Test fun pickerAvoidsRecentItemsWhenItCan() {
val picker = DrillPicker(Random(2))
repeat(50) { assertEquals("b", picker.pick(listOf("a", "b"), { null }, { 0 }, listOf("a"), 0)) }
assertEquals("a", picker.pick(listOf("a"), { null }, { 0 }, listOf("a"), 0)) // nothing else to pick
}
}
|