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; } }