namespace MrGameEng.Genetics;
///
/// Shared, deterministic allele sampling used by both (gene defaults)
/// and (per-individual base overrides). Centralizes the numeric
/// spread+clamp and the weighted discrete pick so the two paths stay consistent.
///
internal static class GeneSampling
{
/// A numeric allele drawn as baseValue ± spread·|baseValue|, clamped to the gene's range.
public static float Numeric(GeneDef gene, float baseValue, float spread, Random random)
{
var value = baseValue + (random.NextSingle() * 2f - 1f) * spread * MathF.Abs(baseValue);
return Math.Clamp(value, gene.Min, gene.Max);
}
///
/// A discrete variant index in [0, Variants), picked from when
/// they match the variant count, otherwise the gene's own weights, otherwise uniformly.
///
public static float Variant(GeneDef gene, float[]? weights, Random random)
{
var variants = Math.Max(1, gene.Variants);
var w =
weights is { Length: > 0 } && weights.Length == variants
? weights
: gene.VariantWeights;
if (w.Length != variants)
{
return random.Next(variants);
}
var total = 0f;
foreach (var value in w)
{
total += MathF.Max(0f, value);
}
if (total <= 0f)
{
return random.Next(variants);
}
var roll = random.NextSingle() * total;
for (var i = 0; i < variants; i++)
{
roll -= MathF.Max(0f, w[i]);
if (roll < 0f)
{
return i;
}
}
return variants - 1;
}
}