diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index 27bb7370980..c80026a87af 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 77c6b164efd..cde7a4304a6 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 45c805fd88f..6624e0a3047 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 7cd626502bb..55565b06e70 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 1a893aacdc1..56c2e197ded 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,