mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 09:16:38 -04:00
Add random deck option
This commit is contained in:
parent
29192d156a
commit
35c0a4e4ae
8 changed files with 117 additions and 36 deletions
|
@ -228,6 +228,7 @@ Matt Brubeck <mbrubeck@limpet.net>
|
||||||
Yaoliang Chen <yaoliang.ch@gmail.com>
|
Yaoliang Chen <yaoliang.ch@gmail.com>
|
||||||
KolbyML <https://github.com/KolbyML>
|
KolbyML <https://github.com/KolbyML>
|
||||||
Adnane Taghi <dev@soleuniverse.me>
|
Adnane Taghi <dev@soleuniverse.me>
|
||||||
|
jariji
|
||||||
|
|
||||||
********************
|
********************
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0fe0162f4a18e8ef2fbac1d9a33af8e38cf7260e
|
Subproject commit 480ef0da728c7ea3485c58529ae7ee02be3e5dba
|
|
@ -142,6 +142,8 @@ deck-config-new-gather-priority-tooltip-2 =
|
||||||
can stop before all subdecks have been checked. This order is fastest in large collections, and
|
can stop before all subdecks have been checked. This order is fastest in large collections, and
|
||||||
allows you to prioritize subdecks that are closer to the top.
|
allows you to prioritize subdecks that are closer to the top.
|
||||||
|
|
||||||
|
`Each from random deck`: Repeatedly picks a random deck and takes the first card from it.
|
||||||
|
|
||||||
`Ascending position`: Gathers cards by ascending position (due #), which is typically
|
`Ascending position`: Gathers cards by ascending position (due #), which is typically
|
||||||
the oldest-added first.
|
the oldest-added first.
|
||||||
|
|
||||||
|
@ -153,6 +155,7 @@ deck-config-new-gather-priority-tooltip-2 =
|
||||||
`Random cards`: Gathers cards in a random order.
|
`Random cards`: Gathers cards in a random order.
|
||||||
deck-config-new-gather-priority-deck = Deck
|
deck-config-new-gather-priority-deck = Deck
|
||||||
deck-config-new-gather-priority-deck-then-random-notes = Deck, then random notes
|
deck-config-new-gather-priority-deck-then-random-notes = Deck, then random notes
|
||||||
|
deck-config-new-gather-priority-each-from-random-deck = Each from random deck
|
||||||
deck-config-new-gather-priority-position-lowest-first = Ascending position
|
deck-config-new-gather-priority-position-lowest-first = Ascending position
|
||||||
deck-config-new-gather-priority-position-highest-first = Descending position
|
deck-config-new-gather-priority-position-highest-first = Descending position
|
||||||
deck-config-new-gather-priority-random-notes = Random notes
|
deck-config-new-gather-priority-random-notes = Random notes
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 17216b03db7249600542e388bd4ea124478400e5
|
Subproject commit fd5f984785ad07a0d3dbd893ee3d7e3671eaebd6
|
|
@ -56,6 +56,8 @@ message DeckConfig {
|
||||||
NEW_CARD_GATHER_PRIORITY_RANDOM_NOTES = 3;
|
NEW_CARD_GATHER_PRIORITY_RANDOM_NOTES = 3;
|
||||||
// Siblings are neither grouped nor ordered.
|
// Siblings are neither grouped nor ordered.
|
||||||
NEW_CARD_GATHER_PRIORITY_RANDOM_CARDS = 4;
|
NEW_CARD_GATHER_PRIORITY_RANDOM_CARDS = 4;
|
||||||
|
// Pick one card from random deck.
|
||||||
|
NEW_CARD_GATHER_PRIORITY_EACH_FROM_RANDOM_DECK = 6;
|
||||||
}
|
}
|
||||||
enum NewCardSortOrder {
|
enum NewCardSortOrder {
|
||||||
// Ascending card template ordinal.
|
// Ascending card template ordinal.
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
use rand::thread_rng;
|
||||||
|
use rand::Rng;
|
||||||
|
use std::collections::HashSet;
|
||||||
use super::DueCard;
|
use super::DueCard;
|
||||||
use super::NewCard;
|
use super::NewCard;
|
||||||
use super::QueueBuilder;
|
use super::QueueBuilder;
|
||||||
|
@ -65,6 +68,10 @@ impl QueueBuilder {
|
||||||
NewCardGatherPriority::Deck => {
|
NewCardGatherPriority::Deck => {
|
||||||
self.gather_new_cards_by_deck(col, NewCardSorting::LowestPosition)
|
self.gather_new_cards_by_deck(col, NewCardSorting::LowestPosition)
|
||||||
}
|
}
|
||||||
|
NewCardGatherPriority::EachFromRandomDeck => {
|
||||||
|
self.gather_new_cards_by_each_from_random_deck(col, NewCardSorting::LowestPosition)
|
||||||
|
}
|
||||||
|
|
||||||
NewCardGatherPriority::DeckThenRandomNotes => self.gather_new_cards_by_deck(
|
NewCardGatherPriority::DeckThenRandomNotes => self.gather_new_cards_by_deck(
|
||||||
col,
|
col,
|
||||||
NewCardSorting::RandomNotes(self.context.timing.days_elapsed),
|
NewCardSorting::RandomNotes(self.context.timing.days_elapsed),
|
||||||
|
@ -112,6 +119,55 @@ impl QueueBuilder {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gather_new_cards_by_each_from_random_deck(
|
||||||
|
&mut self,
|
||||||
|
col: &mut Collection,
|
||||||
|
sort: NewCardSorting,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut deck_ids = col.storage.get_active_deck_ids_sorted()?;
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
let mut cards_added = HashSet::<CardId>::new();
|
||||||
|
|
||||||
|
// Continue until global limit is reached or no more decks with cards.
|
||||||
|
while !self.limits.root_limit_reached(LimitKind::New) && !deck_ids.is_empty() {
|
||||||
|
let selected_index = rng.gen_range(0..deck_ids.len());
|
||||||
|
let selected_deck = deck_ids[selected_index];
|
||||||
|
|
||||||
|
if self.limits.limit_reached(selected_deck, LimitKind::New)? {
|
||||||
|
// Remove the deck from the list since it's at its limit.
|
||||||
|
deck_ids.swap_remove(selected_index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut found_card = false;
|
||||||
|
col.storage
|
||||||
|
.for_each_new_card_in_deck(selected_deck, sort, |card| {
|
||||||
|
let limit_reached = self.limits.limit_reached(selected_deck, LimitKind::New)?;
|
||||||
|
if limit_reached {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
else if !cards_added.contains(&card.id) && self.add_new_card(card) {
|
||||||
|
cards_added.insert(card.id);
|
||||||
|
self.limits
|
||||||
|
.decrement_deck_and_parent_limits(selected_deck, LimitKind::New)?;
|
||||||
|
found_card = true;
|
||||||
|
// Stop iterating this deck after getting one card.
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// If we couldn't find any card from this deck, remove it from consideration
|
||||||
|
if !found_card {
|
||||||
|
deck_ids.swap_remove(selected_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn gather_new_cards_sorted(
|
fn gather_new_cards_sorted(
|
||||||
&mut self,
|
&mut self,
|
||||||
col: &mut Collection,
|
col: &mut Collection,
|
||||||
|
|
|
@ -420,6 +420,21 @@ mod test {
|
||||||
];
|
];
|
||||||
assert_eq!(col.queue_as_deck_and_template(parent.id), cards);
|
assert_eq!(col.queue_as_deck_and_template(parent.id), cards);
|
||||||
|
|
||||||
|
// each from random deck - test that we get expected count with variety across decks
|
||||||
|
col.set_deck_gather_order(&mut parent, NewCardGatherPriority::EachFromRandomDeck);
|
||||||
|
let cards = col.queue_as_deck_and_template(parent.id);
|
||||||
|
|
||||||
|
// Verify we get cards from multiple decks
|
||||||
|
let unique_decks: std::collections::HashSet<_> = cards.iter().map(|(deck_id, _)| *deck_id).collect();
|
||||||
|
assert!(unique_decks.len() > 1, "EachFromRandomDeck should select from multiple decks");
|
||||||
|
|
||||||
|
// Verify the child limit is respected (child + grandchild <= 3)
|
||||||
|
let child_family_count = cards.iter().filter(|(deck_id, _)| *deck_id == child.id || *deck_id == grandchild.id).count();
|
||||||
|
assert!(child_family_count <= 3, "Child limit should be respected");
|
||||||
|
|
||||||
|
// Should get 7 (out of 8) cards total (respects child limit of 3)
|
||||||
|
assert_eq!(cards.len(), 7);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,10 @@ export function newGatherPriorityChoices(): Choice<DeckConfig_Config_NewCardGath
|
||||||
label: tr.deckConfigNewGatherPriorityDeckThenRandomNotes(),
|
label: tr.deckConfigNewGatherPriorityDeckThenRandomNotes(),
|
||||||
value: DeckConfig_Config_NewCardGatherPriority.DECK_THEN_RANDOM_NOTES,
|
value: DeckConfig_Config_NewCardGatherPriority.DECK_THEN_RANDOM_NOTES,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: tr.deckConfigNewGatherPriorityEachFromRandomDeck(),
|
||||||
|
value: DeckConfig_Config_NewCardGatherPriority.EACH_FROM_RANDOM_DECK,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: tr.deckConfigNewGatherPriorityPositionLowestFirst(),
|
label: tr.deckConfigNewGatherPriorityPositionLowestFirst(),
|
||||||
value: DeckConfig_Config_NewCardGatherPriority.LOWEST_POSITION,
|
value: DeckConfig_Config_NewCardGatherPriority.LOWEST_POSITION,
|
||||||
|
|
Loading…
Reference in a new issue