Adapt dynamic question difficulty to learner Elo
This commit is contained in:
@@ -11,9 +11,14 @@ public sealed class DynamicQuestionGenerator
|
||||
public const int CurrentVersion = 2;
|
||||
private static readonly Regex Placeholder = new(@"\{\{([a-zA-Z0-9_]+)\}\}", RegexOptions.Compiled);
|
||||
|
||||
public DynamicGeneratedQuestion Generate(DynamicQuestionBlueprint blueprint, string seed, string nonce, int generatorVersion = CurrentVersion)
|
||||
public DynamicGeneratedQuestion Generate(
|
||||
DynamicQuestionBlueprint blueprint,
|
||||
string seed,
|
||||
string nonce,
|
||||
int generatorVersion = CurrentVersion,
|
||||
string caseFamilyId = "")
|
||||
=> blueprint.QualityStandardVersion >= 2
|
||||
? GenerateV2(blueprint, seed, nonce)
|
||||
? GenerateV2(blueprint, seed, nonce, caseFamilyId)
|
||||
: GenerateLegacy(blueprint, seed, nonce, generatorVersion);
|
||||
|
||||
private DynamicGeneratedQuestion GenerateLegacy(DynamicQuestionBlueprint blueprint, string seed, string nonce, int generatorVersion)
|
||||
@@ -117,13 +122,16 @@ public sealed class DynamicQuestionGenerator
|
||||
return new DynamicGeneratedQuestion { Question = question, Blueprint = blueprint };
|
||||
}
|
||||
|
||||
private DynamicGeneratedQuestion GenerateV2(DynamicQuestionBlueprint blueprint, string seed, string nonce)
|
||||
private DynamicGeneratedQuestion GenerateV2(DynamicQuestionBlueprint blueprint, string seed, string nonce, string caseFamilyId)
|
||||
{
|
||||
if (blueprint.CaseFamilies.Count == 0)
|
||||
throw new InvalidOperationException("quality-standard-2 blueprints require case families");
|
||||
var draw = new DeterministicDraw($"{blueprint.ExternalId}|{blueprint.Version}|{CurrentVersion}|{seed}");
|
||||
var family = blueprint.CaseFamilies.OrderBy(value => value.Id, StringComparer.Ordinal)
|
||||
.ElementAt(draw.Next(blueprint.CaseFamilies.Count));
|
||||
var orderedFamilies = blueprint.CaseFamilies.OrderBy(value => value.Id, StringComparer.Ordinal).ToList();
|
||||
var family = string.IsNullOrWhiteSpace(caseFamilyId)
|
||||
? orderedFamilies[draw.Next(orderedFamilies.Count)]
|
||||
: orderedFamilies.FirstOrDefault(value => value.Id == caseFamilyId)
|
||||
?? throw new InvalidOperationException($"case family {caseFamilyId} is unavailable");
|
||||
var values = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
var numbers = new Dictionary<string, double>(StringComparer.Ordinal);
|
||||
if (family.ValueBundles.Count > 0)
|
||||
@@ -178,7 +186,14 @@ public sealed class DynamicQuestionGenerator
|
||||
Discipline = blueprint.Discipline,
|
||||
System = blueprint.System,
|
||||
PerformanceCategory = blueprint.PerformanceCategory,
|
||||
Difficulty = blueprint.Difficulty,
|
||||
Difficulty = family.DifficultyTier switch
|
||||
{
|
||||
"foundation" => 1,
|
||||
"standard" => 2,
|
||||
"advanced" => 4,
|
||||
"expert" => 5,
|
||||
_ => blueprint.Difficulty
|
||||
},
|
||||
Labs = family.Labs.Select(lab => new QuestionLab
|
||||
{
|
||||
Name = lab.Name,
|
||||
@@ -327,6 +342,19 @@ public sealed class DynamicQuestionGenerator
|
||||
if (blueprint.References.Count == 0) return "at least one reference required";
|
||||
if (blueprint.CaseFamilies.Select(value => value.Id).Distinct(StringComparer.Ordinal).Count() != blueprint.CaseFamilies.Count)
|
||||
return "case family ids must be unique";
|
||||
if (blueprint.QualityStandardVersion >= 3)
|
||||
{
|
||||
foreach (var tier in DynamicDifficultyPolicy.TierElos)
|
||||
{
|
||||
var tierFamilies = blueprint.CaseFamilies
|
||||
.Where(value => value.DifficultyTier == tier.Key).ToList();
|
||||
if (tierFamilies.Count < 2) return $"at least two {tier.Key} case families required";
|
||||
if (tierFamilies.Any(value => Math.Abs(value.NominalElo - tier.Value) > 100))
|
||||
return $"{tier.Key} nominal Elo must be within 100 points of {tier.Value}";
|
||||
}
|
||||
if (blueprint.CaseFamilies.Any(value => !DynamicDifficultyPolicy.TierElos.ContainsKey(value.DifficultyTier)))
|
||||
return "difficultyTier must be foundation, standard, advanced, or expert";
|
||||
}
|
||||
foreach (var family in blueprint.CaseFamilies)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(family.Id)) return "case family id required";
|
||||
|
||||
Reference in New Issue
Block a user