From 54226104c4f3f005931055b49b1ce386d0e3479b Mon Sep 17 00:00:00 2001 From: David Grundberg <75159519+individ-divided@users.noreply.github.com> Date: Fri, 9 Jan 2026 20:42:56 +0100 Subject: [PATCH] Implement 'Interleaved Decks' gathering priority. --- .../src/scheduler/queue/builder/gathering.rs | 93 ++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/rslib/src/scheduler/queue/builder/gathering.rs b/rslib/src/scheduler/queue/builder/gathering.rs index 479595ad7..2e6ceb924 100644 --- a/rslib/src/scheduler/queue/builder/gathering.rs +++ b/rslib/src/scheduler/queue/builder/gathering.rs @@ -1,6 +1,11 @@ // Copyright: Ankitects Pty Ltd and contributors // 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::NewCard; use super::QueueBuilder; @@ -70,8 +75,7 @@ impl QueueBuilder { self.gather_new_cards_by_deck(col, NewCardSorting::RandomNotes(salt)) } NewCardGatherPriority::InterleavedDecks => { - // TODO: Actually implement the new gathering option - self.gather_new_cards_by_deck(col, NewCardSorting::RandomNotes(salt)) + self.gather_new_cards_by_interleaved_decks(col, NewCardSorting::LowestPosition) } NewCardGatherPriority::LowestPosition => { self.gather_new_cards_sorted(col, NewCardSorting::LowestPosition) @@ -114,6 +118,91 @@ impl QueueBuilder { Ok(()) } + fn gather_new_cards_by_interleaved_decks( + &mut self, + col: &mut Collection, + sort: NewCardSorting, + ) -> Result<()> { + struct InterleavedDeckData { + deck_id: DeckId, + depleted: bool, + cards: std::iter::Peekable>, + } + let mut decks: Vec = vec![]; + for deck_id in col.storage.get_active_deck_ids_sorted()? { + let x: std::iter::Peekable> = col + .storage + .new_cards_in_deck(deck_id, sort)? + .into_iter() + .peekable(); + decks.push(InterleavedDeckData { + deck_id, + depleted: false, + cards: x, + }); + } + let mut rng = rng(); + let mut do_continue = true; + while do_continue { + do_continue = false; + let mut non_depleted_decks = 0; + for deck_data in &mut decks { + if self + .limits + .limit_reached(deck_data.deck_id, LimitKind::New)? + { + deck_data.depleted = true; + continue; + } + if deck_data.cards.peek().is_none() { + deck_data.depleted = true; + } else { + non_depleted_decks += 1; + } + } + let root_limit = self.limits.get_root_limits().get(LimitKind::New); + let mut sampled_deck_ids = HashSet::::new(); + let sampling = root_limit < non_depleted_decks; + if sampling { + // switch to sampling + + let mut deck_ids: Vec = vec![]; + for deck_data in &decks { + if !deck_data.depleted { + deck_ids.push(deck_data.deck_id); + } + } + for _i in 0..root_limit { + let selected_index = rng.random_range(0..deck_ids.len()); + let selected_deck_id = deck_ids[selected_index]; + sampled_deck_ids.insert(selected_deck_id); + deck_ids.swap_remove(selected_index); + } + } + for deck_data in &mut decks { + if self.limits.root_limit_reached(LimitKind::New) { + do_continue = false; + break; + } + if sampling && !sampled_deck_ids.contains(&deck_data.deck_id) { + continue; + } + if deck_data.depleted { + continue; + } + if let Some(card) = deck_data.cards.next() { + if self.add_new_card(card) { + self.limits + .decrement_deck_and_parent_limits(deck_data.deck_id, LimitKind::New)?; + do_continue = true; + } + } + } + } + + Ok(()) + } + fn gather_new_cards_sorted( &mut self, col: &mut Collection,