Add "each from random deck" gather priority

This commit is contained in:
jar 2025-09-19 23:31:21 +00:00
parent 3890e12c9e
commit f2923b92e1
6 changed files with 88 additions and 0 deletions

View file

@ -243,6 +243,7 @@ Lee Doughty <32392044+leedoughty@users.noreply.github.com>
memchr <memchr@proton.me> memchr <memchr@proton.me>
Max Romanowski <maxr777@proton.me> Max Romanowski <maxr777@proton.me>
Aldlss <ayaldlss@gmail.com> Aldlss <ayaldlss@gmail.com>
jariji
******************** ********************

View file

@ -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.
@ -151,6 +153,7 @@ deck-config-new-gather-priority-tooltip-2 =
`Random notes`: Picks notes at random, then gathers all of its cards. `Random notes`: Picks notes at random, then gathers all of its cards.
`Random cards`: Gathers cards in a random order. `Random cards`: Gathers cards in a random order.
deck-config-new-gather-priority-each-from-random-deck = Each from random deck
deck-config-new-card-sort-order = New card sort order deck-config-new-card-sort-order = New card sort order
deck-config-new-card-sort-order-tooltip-2 = deck-config-new-card-sort-order-tooltip-2 =
`Card type, then order gathered`: Shows cards in order of card type number. `Card type, then order gathered`: Shows cards in order of card type number.

View file

@ -79,6 +79,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.

View file

@ -1,6 +1,11 @@
// 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 std::collections::HashSet;
use rand::rng;
use rand::Rng;
use super::DueCard; use super::DueCard;
use super::NewCard; use super::NewCard;
use super::QueueBuilder; use super::QueueBuilder;
@ -66,6 +71,9 @@ 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 => { NewCardGatherPriority::DeckThenRandomNotes => {
self.gather_new_cards_by_deck(col, NewCardSorting::RandomNotes(salt)) self.gather_new_cards_by_deck(col, NewCardSorting::RandomNotes(salt))
} }
@ -110,6 +118,53 @@ 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 = 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.random_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.
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,

View file

@ -425,6 +425,29 @@ 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(())
} }

View file

@ -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,