From bf0602085526c45d000e2961a9119e9a45ad0ddd Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Tue, 9 Jan 2024 12:26:46 +1000 Subject: [PATCH] Use card.reps - 1 when calculating fuzz (#2933) https://github.com/open-spaced-repetition/fsrs4anki-helper/issues/343#issuecomment-1879584562 https://forums.ankiweb.net/t/reschedule-is-inconsistent-with-normal-schedule-in-fuzz/39363 --- proto/anki/scheduler.proto | 4 ++-- rslib/src/error/mod.rs | 4 ++-- rslib/src/scheduler/answering/mod.rs | 19 ++++++++++++++----- rslib/src/scheduler/fsrs/memory_state.rs | 2 +- rslib/src/scheduler/states/fuzz.rs | 2 +- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index 27bb73709..c80026a87 100644 --- a/proto/anki/scheduler.proto +++ b/proto/anki/scheduler.proto @@ -53,8 +53,8 @@ service SchedulerService { returns (ComputeOptimalRetentionResponse); rpc EvaluateWeights(EvaluateWeightsRequest) returns (EvaluateWeightsResponse); rpc ComputeMemoryState(cards.CardId) returns (ComputeMemoryStateResponse); - // The number of days the calculated interval will be fuzzed by. Utilized by - // the FSRS add-on. + // The number of days the calculated interval was fuzzed by on the previous + // review (if any). Utilized by the FSRS add-on. rpc FuzzDelta(FuzzDeltaRequest) returns (FuzzDeltaResponse); } diff --git a/rslib/src/error/mod.rs b/rslib/src/error/mod.rs index 77c6b164e..cde7a4304 100644 --- a/rslib/src/error/mod.rs +++ b/rslib/src/error/mod.rs @@ -114,9 +114,9 @@ pub enum AnkiError { InvalidMethodIndex, InvalidServiceIndex, FsrsWeightsInvalid, - // Returned by fsrs-rs; may happen even if 1000+ reviews + /// Returned by fsrs-rs; may happen even if 1000+ reviews FsrsInsufficientData, - // Generated by our backend if count < 1000 + /// Generated by our backend if count < 1000 FsrsInsufficientReviews { count: usize, }, diff --git a/rslib/src/scheduler/answering/mod.rs b/rslib/src/scheduler/answering/mod.rs index 45c805fd8..6624e0a30 100644 --- a/rslib/src/scheduler/answering/mod.rs +++ b/rslib/src/scheduler/answering/mod.rs @@ -398,7 +398,7 @@ impl Collection { }; let desired_retention = fsrs_enabled.then_some(config.inner.desired_retention); Ok(CardStateUpdater { - fuzz_seed: get_fuzz_seed(&card), + fuzz_seed: get_fuzz_seed(&card, false), card, deck, config, @@ -519,14 +519,23 @@ pub mod test_helpers { } impl Card { - pub(crate) fn get_fuzz_factor(&self) -> Option { - get_fuzz_factor(get_fuzz_seed(self)) + /// If for_reschedule is true, we use card.reps - 1 to match the previous + /// review. + pub(crate) fn get_fuzz_factor(&self, for_reschedule: bool) -> Option { + get_fuzz_factor(get_fuzz_seed(self, for_reschedule)) } } /// Return a consistent seed for a given card at a given number of reps. -fn get_fuzz_seed(card: &Card) -> Option { - get_fuzz_seed_for_id_and_reps(card.id, card.reps) +/// If for_reschedule is true, we use card.reps - 1 to match the previous +/// review. +fn get_fuzz_seed(card: &Card, for_reschedule: bool) -> Option { + let reps = if for_reschedule { + card.reps.saturating_sub(1) + } else { + card.reps + }; + get_fuzz_seed_for_id_and_reps(card.id, reps) } /// If in test environment, disable fuzzing. diff --git a/rslib/src/scheduler/fsrs/memory_state.rs b/rslib/src/scheduler/fsrs/memory_state.rs index 7cd626502..55565b06e 100644 --- a/rslib/src/scheduler/fsrs/memory_state.rs +++ b/rslib/src/scheduler/fsrs/memory_state.rs @@ -95,7 +95,7 @@ impl Collection { ) as f32; card.interval = with_review_fuzz( - card.get_fuzz_factor(), + card.get_fuzz_factor(true), interval, 1, req.max_interval, diff --git a/rslib/src/scheduler/states/fuzz.rs b/rslib/src/scheduler/states/fuzz.rs index 1a893aacd..56c2e197d 100644 --- a/rslib/src/scheduler/states/fuzz.rs +++ b/rslib/src/scheduler/states/fuzz.rs @@ -49,7 +49,7 @@ impl Collection { .or_not_found(card.deck_id)?; let config = self.home_deck_config(deck.config_id(), card.original_deck_id)?; let fuzzed = with_review_fuzz( - card.get_fuzz_factor(), + card.get_fuzz_factor(true), interval as f32, 1, config.inner.maximum_review_interval,