Genetics: GenomeTemplate (per-organism gene allotment) + breed override
CI / build-test (push) Successful in 1m17s
CI / build-test (push) Successful in 1m17s
Adds the species/organism layer on top of the gene foundation. GenomeTemplate carries which GeneDefs an individual has plus the per-organism base value and spread its alleles are drawn around (and an optional discrete-variant override), so the same shared GeneDef expresses different centres for different species — Generate() draws an individual, Registry() feeds breeding and trait computation. Genome.Breed gains an optional mutationChance that overrides every gene's fixed MutationChance, so a caller can drive mutation from an evolvable trait. Allele sampling (numeric spread+clamp, weighted discrete pick) is factored into a shared GeneSampling used by both Generate paths. Covered by GenomeTemplateTests (per-species centres, registry-driven breeding, mutation override on/off). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
bc18db5df6
commit
ead22517ee
@@ -0,0 +1,56 @@
|
||||
namespace MrGameEng.Genetics;
|
||||
|
||||
/// <summary>
|
||||
/// Shared, deterministic allele sampling used by both <see cref="Genome.Generate"/> (gene defaults)
|
||||
/// and <see cref="GenomeTemplate"/> (per-individual base overrides). Centralizes the numeric
|
||||
/// spread+clamp and the weighted discrete pick so the two paths stay consistent.
|
||||
/// </summary>
|
||||
internal static class GeneSampling
|
||||
{
|
||||
/// <summary>A numeric allele drawn as <c>baseValue ± spread·|baseValue|</c>, clamped to the gene's range.</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A discrete variant index in <c>[0, Variants)</c>, picked from <paramref name="weights"/> when
|
||||
/// they match the variant count, otherwise the gene's own weights, otherwise uniformly.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user