Don't use SM2 memory state when cards are manually introduced

https://github.com/open-spaced-repetition/fsrs4anki/issues/540#issuecomment-1848833376
This commit is contained in:
Damien Elmes 2023-12-12 09:40:31 +10:00
parent 70ee28b559
commit db93939ded
2 changed files with 20 additions and 8 deletions

View file

@ -274,12 +274,11 @@ pub(crate) fn single_card_revlog_to_item(
} as f32 } as f32
/ 1000.0, / 1000.0,
}); });
if let Some((mut items, found_learning)) = if let Some((mut items, revlogs_complete)) =
single_card_revlog_to_items(entries, next_day_at, false) single_card_revlog_to_items(entries, next_day_at, false)
{ {
let mut item = items.pop().unwrap(); let mut item = items.pop().unwrap();
if found_learning { if revlogs_complete {
// we assume the revlog is complete
Ok(Some(FsrsItemWithStartingState { Ok(Some(FsrsItemWithStartingState {
item, item,
starting_state: None, starting_state: None,

View file

@ -162,26 +162,39 @@ fn fsrs_items_for_training(revlogs: Vec<RevlogEntry>, next_day_at: TimestampSecs
/// expects multiple items for a given card when training - for revlog /// expects multiple items for a given card when training - for revlog
/// `[1,2,3]`, we create FSRSItems corresponding to `[1,2]` and `[1,2,3]` /// `[1,2,3]`, we create FSRSItems corresponding to `[1,2]` and `[1,2,3]`
/// in training, and `[1]`, [1,2]` and `[1,2,3]` when calculating memory /// in training, and `[1]`, [1,2]` and `[1,2,3]` when calculating memory
/// state. Returns (items, found_learn_entry), the latter of which is used /// state.
/// to determine whether the revlogs have been truncated when not training. ///
/// Returns (items, revlog_complete), the latter of which is assumed
/// when the revlogs have a learning step, or start with manual scheduling. When
/// revlogs are incomplete, the starting difficulty is later inferred from the
/// SM2 data, instead of using the standard FSRS initial difficulty.
pub(crate) fn single_card_revlog_to_items( pub(crate) fn single_card_revlog_to_items(
mut entries: Vec<RevlogEntry>, mut entries: Vec<RevlogEntry>,
next_day_at: TimestampSecs, next_day_at: TimestampSecs,
training: bool, training: bool,
) -> Option<(Vec<FSRSItem>, bool)> { ) -> Option<(Vec<FSRSItem>, bool)> {
let mut last_learn_entry = None; let mut last_learn_entry = None;
let mut found_learn_entry = false; let mut revlogs_complete = false;
for (index, entry) in entries.iter().enumerate().rev() { for (index, entry) in entries.iter().enumerate().rev() {
if matches!( if matches!(
(entry.review_kind, entry.button_chosen), (entry.review_kind, entry.button_chosen),
(RevlogReviewKind::Learning, 1..=4) (RevlogReviewKind::Learning, 1..=4)
) { ) {
last_learn_entry = Some(index); last_learn_entry = Some(index);
found_learn_entry = true; revlogs_complete = true;
} else if last_learn_entry.is_some() { } else if last_learn_entry.is_some() {
break; break;
} }
} }
if !revlogs_complete {
revlogs_complete = matches!(
entries.first(),
Some(RevlogEntry {
review_kind: RevlogReviewKind::Manual,
..
})
);
}
let first_relearn = entries let first_relearn = entries
.iter() .iter()
.enumerate() .enumerate()
@ -240,7 +253,7 @@ pub(crate) fn single_card_revlog_to_items(
if items.is_empty() { if items.is_empty() {
None None
} else { } else {
Some((items, found_learn_entry)) Some((items, revlogs_complete))
} }
} }