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,106 @@
|
||||
using MrGameEng.Genetics;
|
||||
using Xunit;
|
||||
|
||||
namespace MrGameEng.Genetics.Tests;
|
||||
|
||||
public class GenomeTemplateTests
|
||||
{
|
||||
private static GeneDef Numeric(string name, float min, float max) =>
|
||||
new()
|
||||
{
|
||||
DefName = name,
|
||||
Kind = GeneKind.Numeric,
|
||||
Min = min,
|
||||
Max = max,
|
||||
};
|
||||
|
||||
private static GeneDef Discrete(string name, int variants = 2) =>
|
||||
new()
|
||||
{
|
||||
DefName = name,
|
||||
Kind = GeneKind.Discrete,
|
||||
Variants = variants,
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void Generate_UsesPerEntryBaseAndSpread_NotGeneDefault()
|
||||
{
|
||||
var gene = Numeric("opt", min: 0f, max: 100f); // GeneDef.Default is 0
|
||||
var template = new GenomeTemplate([
|
||||
new GenomeTemplate.Entry(gene, Base: 50f, Spread: 0.1f),
|
||||
]);
|
||||
var random = new Random(3);
|
||||
|
||||
for (var i = 0; i < 200; i++)
|
||||
{
|
||||
var allele = template.Generate(random)["opt"];
|
||||
Assert.InRange(allele.A, 45f, 55f); // around the template base, not 0
|
||||
Assert.InRange(allele.B, 45f, 55f);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Generate_TwoSpecies_DifferInCentre()
|
||||
{
|
||||
var gene = Numeric("opt", 0f, 100f);
|
||||
var oak = new GenomeTemplate([new GenomeTemplate.Entry(gene, 20f, 0f)]);
|
||||
var grass = new GenomeTemplate([new GenomeTemplate.Entry(gene, 80f, 0f)]);
|
||||
|
||||
Assert.Equal(20f, oak.Generate(new Random(1)).Express(gene), 3);
|
||||
Assert.Equal(80f, grass.Generate(new Random(1)).Express(gene), 3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Registry_CoversEntries_AndDrivesBreeding()
|
||||
{
|
||||
var template = new GenomeTemplate([
|
||||
new GenomeTemplate.Entry(Numeric("a", 0f, 10f), 5f, 0.1f),
|
||||
new GenomeTemplate.Entry(Discrete("morph"), 0f, 0f),
|
||||
]);
|
||||
var random = new Random(9);
|
||||
var a = template.Generate(random);
|
||||
var b = template.Generate(random);
|
||||
|
||||
var child = Genome.Breed(a, b, template.Registry, random);
|
||||
|
||||
Assert.True(child.Has("a"));
|
||||
Assert.True(child.Has("morph"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Breed_MutationChanceOverride_ForcesMutation()
|
||||
{
|
||||
var gene = Numeric("a", min: -100f, max: 100f);
|
||||
var registry = new Dictionary<string, GeneDef> { ["a"] = gene };
|
||||
var parent = new Genome { ["a"] = new Allele(10f, 10f) };
|
||||
|
||||
// Override chance to 1 → every inherited allele mutates away from 10.
|
||||
var moved = false;
|
||||
for (var seed = 0; seed < 30 && !moved; seed++)
|
||||
{
|
||||
var child = Genome.Breed(
|
||||
parent,
|
||||
parent,
|
||||
registry,
|
||||
new Random(seed),
|
||||
mutationChance: 1f
|
||||
);
|
||||
moved = child["a"].A != 10f || child["a"].B != 10f;
|
||||
}
|
||||
|
||||
Assert.True(moved, "with overridden mutationChance=1 the allele should mutate");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Breed_MutationChanceZero_KeepsAlleles()
|
||||
{
|
||||
var gene = Numeric("a", -100f, 100f);
|
||||
var registry = new Dictionary<string, GeneDef> { ["a"] = gene };
|
||||
var parent = new Genome { ["a"] = new Allele(10f, 10f) };
|
||||
|
||||
var child = Genome.Breed(parent, parent, registry, new Random(5), mutationChance: 0f);
|
||||
|
||||
Assert.Equal(10f, child["a"].A);
|
||||
Assert.Equal(10f, child["a"].B);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user