66 lines
2.3 KiB
C#
66 lines
2.3 KiB
C#
namespace QuestionsApi.Models;
|
|
|
|
public static class ChoicePoolSelector
|
|
{
|
|
private const int PresentedChoiceCount = 5;
|
|
private const double MaximumRateMultiplier = 5.0;
|
|
|
|
public static List<string> SelectChoiceIds(
|
|
Question question,
|
|
IReadOnlyDictionary<string, int>? liveSelectionPercentages = null,
|
|
Random? random = null)
|
|
{
|
|
random ??= Random.Shared;
|
|
var rates = liveSelectionPercentages is { Count: > 0 }
|
|
? liveSelectionPercentages
|
|
: question.ChoiceSelectionPercentages;
|
|
var correct = question.Choices.FirstOrDefault(choice => choice.Id == question.CorrectChoiceId);
|
|
var remaining = question.Choices
|
|
.Where(choice => choice.Id != question.CorrectChoiceId)
|
|
.ToList();
|
|
var selected = new List<QuestionChoice>();
|
|
|
|
while (selected.Count < PresentedChoiceCount - 1 && remaining.Count > 0)
|
|
{
|
|
var totalWeight = remaining.Sum(choice => Weight(choice.Id, rates));
|
|
var threshold = random.NextDouble() * totalWeight;
|
|
var cumulative = 0.0;
|
|
var selectedIndex = remaining.Count - 1;
|
|
for (var index = 0; index < remaining.Count; index++)
|
|
{
|
|
cumulative += Weight(remaining[index].Id, rates);
|
|
if (threshold < cumulative)
|
|
{
|
|
selectedIndex = index;
|
|
break;
|
|
}
|
|
}
|
|
selected.Add(remaining[selectedIndex]);
|
|
remaining.RemoveAt(selectedIndex);
|
|
}
|
|
|
|
if (correct is not null)
|
|
selected.Add(correct);
|
|
else
|
|
selected = question.Choices.Take(PresentedChoiceCount).ToList();
|
|
|
|
Shuffle(selected, random);
|
|
return selected.Select(choice => choice.Id).ToList();
|
|
}
|
|
|
|
private static double Weight(string choiceId, IReadOnlyDictionary<string, int>? rates)
|
|
{
|
|
var rate = Math.Clamp(rates?.GetValueOrDefault(choiceId, 0) ?? 0, 0, 100);
|
|
return 1.0 + MaximumRateMultiplier * rate / 100.0;
|
|
}
|
|
|
|
private static void Shuffle<T>(IList<T> items, Random random)
|
|
{
|
|
for (var index = items.Count - 1; index > 0; index--)
|
|
{
|
|
var replacement = random.Next(index + 1);
|
|
(items[index], items[replacement]) = (items[replacement], items[index]);
|
|
}
|
|
}
|
|
}
|