CI / build-test (push) Successful in 1m17s
The organism-agnostic core of the gene system, built on the formula engine. GeneDef is a def describing a gene's kind (numeric/discrete), allele generation range/spread, mutation, variant distribution and its effects on named traits as formulas. Genome is a managed, variable- composition map (geneId -> Allele pair): generated from a gene set, bred meiotically with per-gene mutation, expressed to a phenotype (numeric mean / discrete lower-allele dominance); open composition allows hybrids. Phenotype.Compute aggregates each gene's effect formulas into a trait map (variable `value` = the gene's expressed phenotype, other gene ids and an environment context resolve too), so systems read traits, never genes. Nothing here is species-specific. Def JSON now supports string-named enums (JsonStringEnumConverter) so a gene's kind reads as "Discrete". Covered by GeneticsTests (generation/expression/breeding/traits) and GeneDefLoadTests (GeneDef through the real DefDatabase). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
69 lines
2.4 KiB
C#
69 lines
2.4 KiB
C#
using MrGameEng.Genetics;
|
|
using MrGameEng.Mods;
|
|
using Xunit;
|
|
|
|
namespace MrGameEng.Genetics.Tests;
|
|
|
|
/// <summary>
|
|
/// Verifies <see cref="GeneDef"/> loads through the real <see cref="DefDatabase"/> the way the game
|
|
/// loads <c>genes.json</c>: string-named <see cref="GeneKind"/>, parent inheritance and effect maps.
|
|
/// </summary>
|
|
public sealed class GeneDefLoadTests : IDisposable
|
|
{
|
|
private readonly string _root = Directory.CreateTempSubdirectory("mrge-gene-tests-").FullName;
|
|
|
|
public void Dispose() => Directory.Delete(_root, recursive: true);
|
|
|
|
private DefDatabase Load(string defsJson)
|
|
{
|
|
var modDir = Path.Combine(_root, "mod");
|
|
Directory.CreateDirectory(Path.Combine(modDir, "Defs"));
|
|
File.WriteAllText(Path.Combine(modDir, "Defs", "genes.json"), defsJson);
|
|
var database = new DefDatabase();
|
|
database.RegisterType<GeneDef>("Gene");
|
|
database.Load([new Mod(new ModInfo { Id = "mod" }, modDir)]);
|
|
return database;
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_NumericGeneWithParentAndEffects_Resolves()
|
|
{
|
|
var database = Load(
|
|
"""
|
|
{ "type": "Gene", "defs": [
|
|
{ "defName": "BaseGene", "abstract": true, "spread": 0.1, "mutationChance": 0.05 },
|
|
{ "defName": "GeneVigor", "parent": "BaseGene", "default": 1.0, "min": 0.1, "max": 3.0,
|
|
"tags": ["growth"], "effects": { "vigor": "value" } }
|
|
]}
|
|
"""
|
|
);
|
|
|
|
var gene = database.Get<GeneDef>("GeneVigor");
|
|
Assert.Equal(GeneKind.Numeric, gene.Kind);
|
|
Assert.Equal(0.1f, gene.Spread); // inherited from parent
|
|
Assert.Equal(0.05f, gene.MutationChance); // inherited
|
|
Assert.Equal(3.0f, gene.Max);
|
|
Assert.Equal(["growth"], gene.Tags);
|
|
Assert.Equal("value", gene.Effects["vigor"]);
|
|
Assert.Single(gene.CompiledEffects); // formula compiles
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_DiscreteKindByName_Parses()
|
|
{
|
|
var database = Load(
|
|
"""
|
|
{ "type": "Gene", "defs": [
|
|
{ "defName": "GeneMorph", "kind": "Discrete", "variants": 2,
|
|
"variantWeights": [0.8, 0.2], "effects": { "variant": "value" } }
|
|
]}
|
|
"""
|
|
);
|
|
|
|
var gene = database.Get<GeneDef>("GeneMorph");
|
|
Assert.Equal(GeneKind.Discrete, gene.Kind);
|
|
Assert.Equal(2, gene.Variants);
|
|
Assert.Equal([0.8f, 0.2f], gene.VariantWeights);
|
|
}
|
|
}
|