cyberhybridhub/server/lib/spelling/spelling_item_selector.dart
nathanAndrinoid 4459852f83
Some checks failed
Admin portal tests / test (push) Has been cancelled
spelling
2026-06-05 03:22:35 +00:00

57 lines
1.6 KiB
Dart

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<SpellingItemRecord?> pickForUser(String firebaseUid) async {
final SpellingItemsDb? itemsDb = _itemsDb;
if (itemsDb == null) {
throw StateError('SpellingItemSelector requires itemsDb for pickForUser');
}
final List<SpellingItemRecord> items =
await itemsDb.listActiveWithProgress(firebaseUid);
if (items.isEmpty) {
return null;
}
return _weightedPick(items);
}
/// Visible for unit tests.
SpellingItemRecord? pickFromCandidates(List<SpellingItemRecord> items) {
if (items.isEmpty) {
return null;
}
return _weightedPick(items);
}
SpellingItemRecord _weightedPick(List<SpellingItemRecord> items) {
final List<double> weights = items
.map(
(SpellingItemRecord item) =>
SpellingProgressPolicy.weightForScore(item.familiarityScore),
)
.toList(growable: false);
final double total = weights.fold<double>(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;
}
}