diff --git a/rslib/src/scheduler/answering/learning.rs b/rslib/src/scheduler/answering/learning.rs index d22ca6add..92f532648 100644 --- a/rslib/src/scheduler/answering/learning.rs +++ b/rslib/src/scheduler/answering/learning.rs @@ -1,6 +1,8 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +use rand::{prelude::*, rngs::StdRng}; + use super::{CardStateUpdater, RevlogEntryPartial}; use crate::{ card::{CardQueue, CardType}, @@ -35,7 +37,7 @@ impl CardStateUpdater { match interval { IntervalKind::InSecs(secs) => { self.card.queue = CardQueue::Learn; - self.card.due = TimestampSecs::now().0 as i32 + secs as i32; + self.card.due = self.fuzzed_next_learning_timestamp(secs); } IntervalKind::InDays(days) => { self.card.queue = CardQueue::DayLearn; @@ -45,4 +47,24 @@ impl CardStateUpdater { RevlogEntryPartial::maybe_new(current, next.into(), 0.0, self.secs_until_rollover()) } + + /// Adds secs + fuzz to current time + pub(super) fn fuzzed_next_learning_timestamp(&self, secs: u32) -> i32 { + TimestampSecs::now().0 as i32 + self.with_learning_fuzz(secs) as i32 + } + + /// Add up to 25% increase to seconds, but no more than 5 minutes. + fn with_learning_fuzz(&self, secs: u32) -> u32 { + if let Some(seed) = self.fuzz_seed { + let mut rng = StdRng::seed_from_u64(seed); + let upper_exclusive = secs + ((secs as f32) * 0.25).min(300.0).floor() as u32; + if secs >= upper_exclusive { + secs + } else { + rng.gen_range(secs, upper_exclusive) + } + } else { + secs + } + } } diff --git a/rslib/src/scheduler/answering/preview.rs b/rslib/src/scheduler/answering/preview.rs index 0e8d4bdb9..74b29a780 100644 --- a/rslib/src/scheduler/answering/preview.rs +++ b/rslib/src/scheduler/answering/preview.rs @@ -27,7 +27,7 @@ impl CardStateUpdater { let interval = next.interval_kind(); match interval { IntervalKind::InSecs(secs) => { - self.card.due = self.now.0 as i32 + secs as i32; + self.card.due = self.fuzzed_next_learning_timestamp(secs); } IntervalKind::InDays(_days) => { // unsupported diff --git a/rslib/src/scheduler/answering/relearning.rs b/rslib/src/scheduler/answering/relearning.rs index cbb2bf7a8..79215276c 100644 --- a/rslib/src/scheduler/answering/relearning.rs +++ b/rslib/src/scheduler/answering/relearning.rs @@ -4,7 +4,6 @@ use super::{CardStateUpdater, RevlogEntryPartial}; use crate::{ card::{CardQueue, CardType}, - prelude::*, scheduler::states::{CardState, IntervalKind, RelearnState}, }; @@ -25,7 +24,7 @@ impl CardStateUpdater { match interval { IntervalKind::InSecs(secs) => { self.card.queue = CardQueue::Learn; - self.card.due = TimestampSecs::now().0 as i32 + secs as i32; + self.card.due = self.fuzzed_next_learning_timestamp(secs); } IntervalKind::InDays(days) => { self.card.queue = CardQueue::DayLearn; diff --git a/rslib/src/scheduler/states/learning.rs b/rslib/src/scheduler/states/learning.rs index 621b3024c..82c9f9605 100644 --- a/rslib/src/scheduler/states/learning.rs +++ b/rslib/src/scheduler/states/learning.rs @@ -32,14 +32,14 @@ impl LearnState { fn answer_again(self, ctx: &StateContext) -> LearnState { LearnState { remaining_steps: ctx.steps.remaining_for_failed(), - scheduled_secs: ctx.with_learning_fuzz(ctx.steps.again_delay_secs_learn()), + scheduled_secs: ctx.steps.again_delay_secs_learn(), } } fn answer_hard(self, ctx: &StateContext) -> CardState { if let Some(hard_delay) = ctx.steps.hard_delay_secs(self.remaining_steps) { LearnState { - scheduled_secs: ctx.with_learning_fuzz(hard_delay), + scheduled_secs: hard_delay, ..self } .into() @@ -56,7 +56,7 @@ impl LearnState { if let Some(good_delay) = ctx.steps.good_delay_secs(self.remaining_steps) { LearnState { remaining_steps: ctx.steps.remaining_for_good(self.remaining_steps), - scheduled_secs: ctx.with_learning_fuzz(good_delay), + scheduled_secs: good_delay, } .into() } else { diff --git a/rslib/src/scheduler/states/mod.rs b/rslib/src/scheduler/states/mod.rs index 27844c434..4adc6e165 100644 --- a/rslib/src/scheduler/states/mod.rs +++ b/rslib/src/scheduler/states/mod.rs @@ -121,21 +121,6 @@ impl<'a> StateContext<'a> { .round() as u32 } - /// Add up to 25% increase to seconds, but no more than 5 minutes. - pub(crate) fn with_learning_fuzz(&self, secs: u32) -> u32 { - if let Some(seed) = self.fuzz_seed { - let mut rng = StdRng::seed_from_u64(seed); - let upper_exclusive = secs + ((secs as f32) * 0.25).min(300.0).floor() as u32; - if secs >= upper_exclusive { - secs - } else { - rng.gen_range(secs, upper_exclusive) - } - } else { - secs - } - } - pub(crate) fn fuzzed_graduating_interval_good(&self) -> u32 { self.with_review_fuzz(self.graduating_interval_good as f32) } diff --git a/rslib/src/scheduler/states/preview_filter.rs b/rslib/src/scheduler/states/preview_filter.rs index b22cb2445..5835eaa2a 100644 --- a/rslib/src/scheduler/states/preview_filter.rs +++ b/rslib/src/scheduler/states/preview_filter.rs @@ -18,18 +18,18 @@ impl PreviewState { NextCardStates { current: self.into(), again: PreviewState { - scheduled_secs: ctx.with_learning_fuzz(ctx.preview_step * 60), + scheduled_secs: ctx.preview_step * 60, ..self } .into(), hard: PreviewState { // ~15 minutes with the default setting - scheduled_secs: ctx.with_learning_fuzz(ctx.preview_step * 90), + scheduled_secs: ctx.preview_step * 90, ..self } .into(), good: PreviewState { - scheduled_secs: ctx.with_learning_fuzz(ctx.preview_step * 120), + scheduled_secs: ctx.preview_step * 120, ..self } .into(), diff --git a/rslib/src/scheduler/states/relearning.rs b/rslib/src/scheduler/states/relearning.rs index 017b4612c..a295717db 100644 --- a/rslib/src/scheduler/states/relearning.rs +++ b/rslib/src/scheduler/states/relearning.rs @@ -36,7 +36,7 @@ impl RelearnState { RelearnState { learning: LearnState { remaining_steps: ctx.relearn_steps.remaining_for_failed(), - scheduled_secs: ctx.with_learning_fuzz(again_delay), + scheduled_secs: again_delay, }, review: ReviewState { scheduled_days: self.review.failing_review_interval(ctx), @@ -57,7 +57,7 @@ impl RelearnState { { RelearnState { learning: LearnState { - scheduled_secs: ctx.with_learning_fuzz(hard_delay), + scheduled_secs: hard_delay, ..self.learning }, review: ReviewState { @@ -78,7 +78,7 @@ impl RelearnState { { RelearnState { learning: LearnState { - scheduled_secs: ctx.with_learning_fuzz(good_delay), + scheduled_secs: good_delay, remaining_steps: ctx .relearn_steps .remaining_for_good(self.learning.remaining_steps), diff --git a/rslib/src/scheduler/states/review.rs b/rslib/src/scheduler/states/review.rs index a5fdfe14b..ebc7f7c4c 100644 --- a/rslib/src/scheduler/states/review.rs +++ b/rslib/src/scheduler/states/review.rs @@ -86,7 +86,7 @@ impl ReviewState { RelearnState { learning: LearnState { remaining_steps: ctx.relearn_steps.remaining_for_failed(), - scheduled_secs: ctx.with_learning_fuzz(again_delay), + scheduled_secs: again_delay, }, review: again_review, }