From 01e9a569f562ce505c2f3fefeb02c832754bfd46 Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Thu, 12 Sep 2024 21:02:35 +0800 Subject: [PATCH] compute retrievability for all states (#122) Co-authored-by: ishiko --- __tests__/FSRSV5.test.ts | 41 ++++++++++++++++++++++++++++++++++++---- package.json | 2 +- src/fsrs/default.ts | 2 +- src/fsrs/fsrs.ts | 13 +++++-------- 4 files changed, 44 insertions(+), 14 deletions(-) diff --git a/__tests__/FSRSV5.test.ts b/__tests__/FSRSV5.test.ts index a9d1b19..b7380d9 100644 --- a/__tests__/FSRSV5.test.ts +++ b/__tests__/FSRSV5.test.ts @@ -123,18 +123,18 @@ describe('FSRS V5 ', () => { describe('get retrievability', () => { const fsrs = new FSRS({}) - test('return undefined for non-review cards', () => { + test('return 0.00% for new cards', () => { const card = createEmptyCard() const now = new Date() - const expected = undefined + const expected = '0.00%' expect(fsrs.get_retrievability(card, now)).toBe(expected) }) test('return retrievability percentage for review cards', () => { const card = createEmptyCard('2023-12-01 04:00:00') const sc = fsrs.repeat(card, '2023-12-01 04:05:00') - const r = [undefined, undefined, undefined, '90.26%'] - const r_number = [undefined, undefined, undefined, 0.90260891] + const r = ['100.00%', '100.00%', '100.00%', '90.26%'] + const r_number = [1, 1, 1, 0.9026208] Grades.forEach((grade, index) => { expect(fsrs.get_retrievability(sc[grade].card, sc[grade].card.due)).toBe( r[index] @@ -144,6 +144,39 @@ describe('get retrievability', () => { ).toBe(r_number[index]) }) }) + + test('fake the current system time', () => { + const card = createEmptyCard('2023-12-01 04:00:00') + const sc = fsrs.repeat(card, '2023-12-01 04:05:00') + const r = ['100.00%', '100.00%', '100.00%', '90.26%'] + const r_number = [1, 1, 1, 0.9026208] + jest.useFakeTimers() + Grades.forEach((grade, index) => { + jest.setSystemTime(sc[grade].card.due) + expect(fsrs.get_retrievability(sc[grade].card)).toBe(r[index]) + expect(fsrs.get_retrievability(sc[grade].card, undefined, false)).toBe( + r_number[index] + ) + }) + jest.useRealTimers() + }) + + test('loop Again', () => { + const fsrs = new FSRS({}) + let card = createEmptyCard() + let now = new Date() + let i = 0 + while (i < 10 ** 3) { + card = fsrs.next(card, now, Rating.Again).card + now = card.due + i++ + + const r = fsrs.get_retrievability(card, now, false) + console.debug(`Loop ${i}: s:${card.stability} r:${r} `) + + expect(r).not.toBeNaN() + } + }) }) describe('fsrs.next method', () => { diff --git a/package.json b/package.json index 42c3d60..0507a4c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ts-fsrs", - "version": "4.2.0", + "version": "4.3.0", "description": "ts-fsrs is a versatile package based on TypeScript that supports ES modules, CommonJS, and UMD. It implements the Free Spaced Repetition Scheduler (FSRS) algorithm, enabling developers to integrate FSRS into their flashcard applications to enhance the user learning experience.", "main": "dist/index.cjs", "umd": "dist/index.umd.js", diff --git a/src/fsrs/default.ts b/src/fsrs/default.ts index ffc484b..6c5fb97 100644 --- a/src/fsrs/default.ts +++ b/src/fsrs/default.ts @@ -11,7 +11,7 @@ export const default_w = [ export const default_enable_fuzz = false export const default_enable_short_term = true -export const FSRSVersion: string = 'v4.2.0 using FSRS V5.0' +export const FSRSVersion: string = 'v4.3.0 using FSRS V5.0' export const generatorParameters = ( props?: Partial diff --git a/src/fsrs/fsrs.ts b/src/fsrs/fsrs.ts index 659fa61..3910dbf 100644 --- a/src/fsrs/fsrs.ts +++ b/src/fsrs/fsrs.ts @@ -204,16 +204,13 @@ export class FSRS extends FSRSAlgorithm { */ get_retrievability( card: CardInput | Card, - now: DateInput, + now?: DateInput, format: T = true as T - ): undefined | (T extends true ? string : number) { + ): (T extends true ? string : number) { const processedCard = TypeConvert.card(card) - now = TypeConvert.time(now) - if (processedCard.state !== State.Review) { - return undefined - } - const t = Math.max(now.diff(processedCard.last_review as Date, 'days'), 0) - const r = this.forgetting_curve(t, +processedCard.stability.toFixed(2)) + now = now ? TypeConvert.time(now) : new Date() + const t = processedCard.state !== State.New ? Math.max(now.diff(processedCard.last_review as Date, 'days'), 0) : 0 + const r = processedCard.state !== State.New ? this.forgetting_curve(t, +processedCard.stability.toFixed(8)) : 0 return (format ? `${(r * 100).toFixed(2)}%` : r) as T extends true ? string : number