learning randomization

This commit is contained in:
Stefan Hardegger
2025-11-22 14:28:26 +01:00
parent 33377009d0
commit de4e7c4c6e
6 changed files with 1265 additions and 99 deletions

View File

@@ -26,8 +26,6 @@ describe("SM-2 Algorithm", () => {
expect(INITIAL_PROGRESS.interval).toBe(1)
expect(INITIAL_PROGRESS.consecutiveCorrect).toBe(0)
expect(INITIAL_PROGRESS.incorrectCount).toBe(0)
expect(INITIAL_PROGRESS.lastReviewDate).toBeNull()
expect(INITIAL_PROGRESS.nextReviewDate).toBeNull()
})
})
@@ -51,7 +49,6 @@ describe("SM-2 Algorithm", () => {
interval: 1,
consecutiveCorrect: 1,
incorrectCount: 0,
lastReviewDate: new Date("2025-01-01"),
nextReviewDate: new Date("2025-01-02"),
}
@@ -69,7 +66,6 @@ describe("SM-2 Algorithm", () => {
interval: 6,
consecutiveCorrect: 2,
incorrectCount: 0,
lastReviewDate: new Date("2025-01-02"),
nextReviewDate: new Date("2025-01-08"),
}
@@ -88,7 +84,6 @@ describe("SM-2 Algorithm", () => {
interval: 50,
consecutiveCorrect: 5,
incorrectCount: 2,
lastReviewDate: new Date("2025-01-01"),
nextReviewDate: new Date("2025-02-20"),
}
@@ -130,7 +125,6 @@ describe("SM-2 Algorithm", () => {
interval: 365,
consecutiveCorrect: 10,
incorrectCount: 0,
lastReviewDate: new Date("2025-01-01"),
nextReviewDate: new Date("2026-01-01"),
}
@@ -149,7 +143,6 @@ describe("SM-2 Algorithm", () => {
interval: 16,
consecutiveCorrect: 3,
incorrectCount: 0,
lastReviewDate: new Date("2025-01-01"),
nextReviewDate: new Date("2025-01-17"),
}
@@ -165,7 +158,6 @@ describe("SM-2 Algorithm", () => {
interval: 16,
consecutiveCorrect: 5,
incorrectCount: 1,
lastReviewDate: new Date("2025-01-01"),
nextReviewDate: new Date("2025-01-17"),
}
@@ -180,7 +172,6 @@ describe("SM-2 Algorithm", () => {
interval: 6,
consecutiveCorrect: 2,
incorrectCount: 0,
lastReviewDate: new Date("2025-01-01"),
nextReviewDate: new Date("2025-01-07"),
}
@@ -195,7 +186,6 @@ describe("SM-2 Algorithm", () => {
interval: 1,
consecutiveCorrect: 0,
incorrectCount: 5,
lastReviewDate: new Date("2025-01-01"),
nextReviewDate: new Date("2025-01-02"),
}
@@ -211,7 +201,6 @@ describe("SM-2 Algorithm", () => {
interval: 6,
consecutiveCorrect: 2,
incorrectCount: 0,
lastReviewDate: new Date("2025-01-01"),
nextReviewDate: new Date("2025-01-07"),
}
@@ -226,7 +215,6 @@ describe("SM-2 Algorithm", () => {
interval: 6,
consecutiveCorrect: 2,
incorrectCount: 0,
lastReviewDate: new Date("2025-01-01"),
nextReviewDate: new Date("2025-01-07"),
}
@@ -254,7 +242,6 @@ describe("SM-2 Algorithm", () => {
interval: 16,
consecutiveCorrect: 3,
incorrectCount: 0,
lastReviewDate: new Date("2025-01-01"),
nextReviewDate: new Date("2025-01-17"),
}
@@ -269,7 +256,6 @@ describe("SM-2 Algorithm", () => {
interval: result.interval,
consecutiveCorrect: result.consecutiveCorrect,
incorrectCount: result.incorrectCount,
lastReviewDate: new Date("2025-01-17"),
nextReviewDate: result.nextReviewDate,
}
result = calculateIncorrectAnswer(progress, new Date("2025-01-18"))
@@ -282,7 +268,6 @@ describe("SM-2 Algorithm", () => {
interval: result.interval,
consecutiveCorrect: result.consecutiveCorrect,
incorrectCount: result.incorrectCount,
lastReviewDate: new Date("2025-01-18"),
nextReviewDate: result.nextReviewDate,
}
result = calculateIncorrectAnswer(progress, new Date("2025-01-19"))
@@ -301,25 +286,25 @@ describe("SM-2 Algorithm", () => {
nextReviewDate: new Date("2025-01-14T10:00:00Z"), // Due
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
{
id: "2",
nextReviewDate: new Date("2025-01-16T10:00:00Z"), // Not due
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
{
id: "3",
nextReviewDate: new Date("2025-01-13T10:00:00Z"), // Due
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
]
const selected = selectCardsForSession(cards, 10, now)
const selected = selectCardsForSession(cards, 10, now, false)
expect(selected.length).toBe(2)
expect(selected.map((c) => c.id)).toContain("1")
@@ -334,7 +319,7 @@ describe("SM-2 Algorithm", () => {
nextReviewDate: new Date("2025-01-14T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
{
id: "2",
@@ -345,7 +330,7 @@ describe("SM-2 Algorithm", () => {
},
]
const selected = selectCardsForSession(cards, 10, now)
const selected = selectCardsForSession(cards, 10, now, false)
expect(selected.length).toBe(1)
expect(selected[0].id).toBe("1")
@@ -372,11 +357,11 @@ describe("SM-2 Algorithm", () => {
nextReviewDate: new Date("2025-01-14T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
]
const selected = selectCardsForSession(cards, 10, now)
const selected = selectCardsForSession(cards, 10, now, false)
expect(selected[0].id).toBe("hard")
expect(selected[1].id).toBe("normal")
@@ -390,25 +375,25 @@ describe("SM-2 Algorithm", () => {
nextReviewDate: new Date("2025-01-14T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
{
id: "2",
nextReviewDate: new Date("2025-01-12T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
{
id: "3",
nextReviewDate: new Date("2025-01-13T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
]
const selected = selectCardsForSession(cards, 10, now)
const selected = selectCardsForSession(cards, 10, now, false)
expect(selected[0].id).toBe("2") // Oldest
expect(selected[1].id).toBe("3")
@@ -422,25 +407,25 @@ describe("SM-2 Algorithm", () => {
nextReviewDate: new Date("2025-01-14T10:00:00Z"),
incorrectCount: 1,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
{
id: "2",
nextReviewDate: new Date("2025-01-14T10:00:00Z"),
incorrectCount: 3,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
{
id: "3",
nextReviewDate: new Date("2025-01-14T10:00:00Z"),
incorrectCount: 2,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
]
const selected = selectCardsForSession(cards, 10, now)
const selected = selectCardsForSession(cards, 10, now, false)
expect(selected[0].id).toBe("2") // incorrectCount: 3
expect(selected[1].id).toBe("3") // incorrectCount: 2
@@ -454,25 +439,25 @@ describe("SM-2 Algorithm", () => {
nextReviewDate: new Date("2025-01-14T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 3,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
{
id: "2",
nextReviewDate: new Date("2025-01-14T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
{
id: "3",
nextReviewDate: new Date("2025-01-14T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 2,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
]
const selected = selectCardsForSession(cards, 10, now)
const selected = selectCardsForSession(cards, 10, now, false)
expect(selected[0].id).toBe("2") // consecutiveCorrect: 1
expect(selected[1].id).toBe("3") // consecutiveCorrect: 2
@@ -486,18 +471,18 @@ describe("SM-2 Algorithm", () => {
nextReviewDate: null, // New card
incorrectCount: 0,
consecutiveCorrect: 0,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
{
id: "2",
nextReviewDate: new Date("2025-01-14T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
]
const selected = selectCardsForSession(cards, 10, now)
const selected = selectCardsForSession(cards, 10, now, false)
expect(selected.length).toBe(2)
expect(selected[0].id).toBe("1") // New cards first
@@ -510,10 +495,10 @@ describe("SM-2 Algorithm", () => {
nextReviewDate: new Date("2025-01-14T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
}))
const selected = selectCardsForSession(cards, 5, now)
const selected = selectCardsForSession(cards, 5, now, false)
expect(selected.length).toBe(5)
})
@@ -546,11 +531,11 @@ describe("SM-2 Algorithm", () => {
nextReviewDate: new Date("2025-01-12T10:00:00Z"),
incorrectCount: 5,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
]
const selected = selectCardsForSession(cards, 10, now)
const selected = selectCardsForSession(cards, 10, now, false)
// Expected order:
// 1. HARD difficulty has priority
@@ -564,7 +549,7 @@ describe("SM-2 Algorithm", () => {
})
it("should handle empty card list", () => {
const selected = selectCardsForSession([], 10, now)
const selected = selectCardsForSession([], 10, now, false)
expect(selected.length).toBe(0)
})
@@ -586,7 +571,7 @@ describe("SM-2 Algorithm", () => {
},
]
const selected = selectCardsForSession(cards, 10, now)
const selected = selectCardsForSession(cards, 10, now, false)
expect(selected.length).toBe(0)
})
@@ -597,18 +582,18 @@ describe("SM-2 Algorithm", () => {
nextReviewDate: new Date("2025-01-16T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
{
id: "2",
nextReviewDate: new Date("2025-01-17T10:00:00Z"),
incorrectCount: 0,
consecutiveCorrect: 1,
manualDifficulty: Difficulty.NORMAL,
manualDifficulty: Difficulty.MEDIUM,
},
]
const selected = selectCardsForSession(cards, 10, now)
const selected = selectCardsForSession(cards, 10, now, false)
expect(selected.length).toBe(0)
})
})