import 'dart:math'; import 'spelling_item_record.dart'; import 'spelling_items_db.dart'; import 'spelling_progress_policy.dart'; /// Picks a shared spelling item for a user using familiarity-weighted randomness. class SpellingItemSelector { SpellingItemSelector({ SpellingItemsDb? itemsDb, Random? random, }) : _itemsDb = itemsDb, _random = random ?? Random(); final SpellingItemsDb? _itemsDb; final Random _random; Future pickForUser(String firebaseUid) async { final SpellingItemsDb? itemsDb = _itemsDb; if (itemsDb == null) { throw StateError('SpellingItemSelector requires itemsDb for pickForUser'); } final List items = await itemsDb.listActiveWithProgress(firebaseUid); if (items.isEmpty) { return null; } return _weightedPick(items); } /// Visible for unit tests. SpellingItemRecord? pickFromCandidates(List items) { if (items.isEmpty) { return null; } return _weightedPick(items); } SpellingItemRecord _weightedPick(List items) { final List weights = items .map( (SpellingItemRecord item) => SpellingProgressPolicy.weightForScore(item.familiarityScore), ) .toList(growable: false); final double total = weights.fold(0, (double a, double b) => a + b); var roll = _random.nextDouble() * total; for (var i = 0; i < items.length; i++) { roll -= weights[i]; if (roll < 0) { return items[i]; } } return items.last; } }