Improve dynamic question learning loop
This commit is contained in:
@@ -49,13 +49,52 @@ public sealed class DynamicQuestionService
|
||||
}
|
||||
|
||||
var mastery = await _store.MasteryAsync(userId);
|
||||
var dueReviews = await _store.DueReviewsAsync(userId, DateTime.UtcNow);
|
||||
var dueByObjective = dueReviews.ToDictionary(value => value.ObjectiveId, StringComparer.Ordinal);
|
||||
var candidates = unseen
|
||||
.Where(group => group.Key != run.PreviousObjectiveId || unseen.Count == 1)
|
||||
.OrderBy(group => mastery.TryGetValue(group.Key, out var value) ? value.Elo : 1000)
|
||||
.ThenBy(_ => RandomNumberGenerator.GetInt32(int.MaxValue))
|
||||
.ToList();
|
||||
var weakestBand = candidates.Take(Math.Min(4, candidates.Count)).ToList();
|
||||
var selectedObjective = weakestBand[RandomNumberGenerator.GetInt32(weakestBand.Count)];
|
||||
var roll = RandomNumberGenerator.GetInt32(100);
|
||||
IGrouping<string, DynamicQuestionBlueprint> selectedObjective;
|
||||
var dueCandidates = candidates.Where(value => dueByObjective.ContainsKey(value.Key)).ToList();
|
||||
if (roll < 50 && dueCandidates.Count == 0 && dueByObjective.Count > 0)
|
||||
dueCandidates = byObjective
|
||||
.Where(value => dueByObjective.ContainsKey(value.Key)
|
||||
&& (value.Key != run.PreviousObjectiveId || dueByObjective.Count == 1))
|
||||
.ToList();
|
||||
if (roll < 50 && dueCandidates.Count > 0)
|
||||
{
|
||||
selectedObjective = dueCandidates
|
||||
.OrderBy(value => dueByObjective[value.Key].DueUtc)
|
||||
.ThenBy(_ => RandomNumberGenerator.GetInt32(int.MaxValue))
|
||||
.First();
|
||||
}
|
||||
else if (roll < 80)
|
||||
{
|
||||
var systemAttempts = byObjective
|
||||
.GroupBy(group => group.OrderByDescending(value => value.Version).First().System)
|
||||
.ToDictionary(
|
||||
group => group.Key,
|
||||
group => group.Sum(objective => mastery.TryGetValue(objective.Key, out var value) ? value.Attempts : 0),
|
||||
StringComparer.Ordinal);
|
||||
selectedObjective = candidates
|
||||
.OrderBy(group =>
|
||||
{
|
||||
var latest = group.OrderByDescending(value => value.Version).First();
|
||||
var weight = DynamicBlueprintCoveragePolicy.Weight(latest);
|
||||
return (systemAttempts.GetValueOrDefault(latest.System) + 1) / weight;
|
||||
})
|
||||
.ThenBy(_ => RandomNumberGenerator.GetInt32(int.MaxValue))
|
||||
.First();
|
||||
}
|
||||
else
|
||||
{
|
||||
var weakestBand = candidates
|
||||
.OrderBy(group => mastery.TryGetValue(group.Key, out var value) ? value.Elo : 1000)
|
||||
.Take(Math.Min(4, candidates.Count))
|
||||
.ToList();
|
||||
selectedObjective = weakestBand[RandomNumberGenerator.GetInt32(weakestBand.Count)];
|
||||
}
|
||||
var blueprint = selectedObjective
|
||||
.OrderByDescending(value => value.Version)
|
||||
.First();
|
||||
@@ -97,11 +136,15 @@ public sealed class DynamicQuestionService
|
||||
DifficultyTier = selectedFamily.DifficultyTier,
|
||||
EffectiveAbilityElo = effectiveAbility,
|
||||
FamilyElo = selectedFamilyElo,
|
||||
IsScheduledReview = dueByObjective.TryGetValue(blueprint.ObjectiveId, out var reviewItem),
|
||||
ReviewItemId = reviewItem?.Id ?? "",
|
||||
ReviewStage = reviewItem?.Stage ?? 0,
|
||||
ExpiresUtc = DateTime.UtcNow.AddDays(7)
|
||||
};
|
||||
var generated = _generator.Generate(
|
||||
blueprint, claims.Seed, claims.Nonce, claims.GeneratorVersion, claims.CaseFamilyId);
|
||||
run.SeenObjectiveIds.Add(blueprint.ObjectiveId);
|
||||
if (!run.SeenObjectiveIds.Contains(blueprint.ObjectiveId, StringComparer.Ordinal))
|
||||
run.SeenObjectiveIds.Add(blueprint.ObjectiveId);
|
||||
run.PreviousObjectiveId = blueprint.ObjectiveId;
|
||||
run.RecentCaseFamilyIds.Add(generated.CaseFamilyId);
|
||||
run.RecentCaseFamilyIds = run.RecentCaseFamilyIds.TakeLast(30).ToList();
|
||||
|
||||
Reference in New Issue
Block a user