From 91a00b4305f92e4a4282331196cf614aed4f1e39 Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Fri, 12 Jun 2026 23:43:50 +0300 Subject: [PATCH] G2: bump engine (gene foundation), load genes + gene demo command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Engine pointer -> bc18db5: brings in the organism-agnostic gene foundation (GeneDef, managed Genome, Phenotype trait computation) plus a parallel MrGameEng.Net library the game does not use. Game wiring: register the "Gene" def type and ship Mods/Core/Defs/ genes.json — a full gene set covering the plant phenotype dimensions (environment optima/tolerances, vigor, lifecycle, reproduction) plus a discrete morph gene, each declaring its trait effects as formulas. A dev-console 'gene [seed]' command generates a genome from these defs, prints its alleles, expressed values and computed traits, then breeds a child — demonstrating the whole pipeline end to end. These genes seed the G3 migration of plants onto traits. Co-Authored-By: Claude Opus 4.8 --- Mods/Core/Defs/genes.json | 53 ++++++++++++++++++++++++++++ engine | 2 +- src/LittleSim/Content/GameContent.cs | 2 ++ src/LittleSim/Scenes/WorldScene.cs | 48 +++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 Mods/Core/Defs/genes.json diff --git a/Mods/Core/Defs/genes.json b/Mods/Core/Defs/genes.json new file mode 100644 index 0000000..de74c8c --- /dev/null +++ b/Mods/Core/Defs/genes.json @@ -0,0 +1,53 @@ +{ + "type": "Gene", + // Организм-агностичные гены: каждый ген задаёт, как генерируются/мутируют аллели и как ген + // вкладывается в признаки (effects: имя признака → формула; value — выраженное значение гена). + // Этот набор покрывает измерения генома растения (его используют системы роста/жизненного цикла). + "defs": [ + { "defName": "BaseNumericGene", "abstract": true, "kind": "Numeric", + "spread": 0.08, "mutationChance": 0.05, "mutationMagnitude": 0.12 }, + + { "defName": "GeneOptimalLight", "parent": "BaseNumericGene", "label": "gene.optimalLight", + "default": 0.6, "min": 0.0, "max": 1.0, "tags": ["environment", "light"], + "effects": { "optimalLight": "value" } }, + { "defName": "GeneLightTolerance", "parent": "BaseNumericGene", "label": "gene.lightTolerance", + "default": 0.5, "min": 0.05, "max": 1.0, "tags": ["environment", "light"], + "effects": { "lightTolerance": "value" } }, + { "defName": "GeneOptimalTemperature", "parent": "BaseNumericGene", "label": "gene.optimalTemperature", + "default": 14, "min": -20, "max": 45, "tags": ["environment", "temperature"], + "effects": { "optimalTemperature": "value" } }, + { "defName": "GeneTemperatureTolerance", "parent": "BaseNumericGene", "label": "gene.temperatureTolerance", + "default": 16, "min": 2, "max": 40, "tags": ["environment", "temperature"], + "effects": { "temperatureTolerance": "value" } }, + { "defName": "GeneOptimalFertility", "parent": "BaseNumericGene", "label": "gene.optimalFertility", + "default": 1.4, "min": 0.1, "max": 3.0, "tags": ["environment", "soil"], + "effects": { "optimalFertility": "value" } }, + { "defName": "GeneFertilityTolerance", "parent": "BaseNumericGene", "label": "gene.fertilityTolerance", + "default": 0.9, "min": 0.1, "max": 3.0, "tags": ["environment", "soil"], + "effects": { "fertilityTolerance": "value" } }, + + { "defName": "GeneVigor", "parent": "BaseNumericGene", "label": "gene.vigor", + "default": 1.0, "min": 0.1, "max": 3.0, "tags": ["growth"], + "effects": { "vigor": "value" } }, + { "defName": "GeneLifespan", "parent": "BaseNumericGene", "label": "gene.lifespan", + "default": 160, "min": 5, "max": 500, "tags": ["lifecycle"], + "effects": { "lifespan": "value" } }, + { "defName": "GeneDispersalRange", "parent": "BaseNumericGene", "label": "gene.dispersalRange", + "default": 2, "min": 1, "max": 8, "tags": ["lifecycle", "reproduction"], + "effects": { "dispersalRange": "value" } }, + { "defName": "GeneReproduceInterval", "parent": "BaseNumericGene", "label": "gene.reproduceInterval", + "default": 14, "min": 1, "max": 60, "tags": ["lifecycle", "reproduction"], + "effects": { "reproduceInterval": "value" } }, + { "defName": "GeneSelfPollination", "parent": "BaseNumericGene", "label": "gene.selfPollination", + "default": 0.2, "min": 0.0, "max": 1.0, "tags": ["reproduction"], + "effects": { "selfPollination": "value" } }, + { "defName": "GeneMutationRate", "parent": "BaseNumericGene", "label": "gene.mutationRate", + "default": 0.05, "min": 0.0, "max": 1.0, "tags": ["reproduction"], + "effects": { "mutationRate": "value" } }, + + // Дискретный ген морфы: вариант 0 доминирует, 1 (рецессивный) виден только в гомозиготе. + { "defName": "GeneMorph", "kind": "Discrete", "label": "gene.morph", + "variants": 2, "variantWeights": [0.82, 0.18], "mutationChance": 0.05, "tags": ["morphology"], + "effects": { "variant": "value" } } + ] +} diff --git a/engine b/engine index 10898b0..bc18db5 160000 --- a/engine +++ b/engine @@ -1 +1 @@ -Subproject commit 10898b08a065ffc564c00473069e02b0e5e06c2a +Subproject commit bc18db5df637d7683e22e4306834ba355033a612 diff --git a/src/LittleSim/Content/GameContent.cs b/src/LittleSim/Content/GameContent.cs index ed41727..347da4c 100644 --- a/src/LittleSim/Content/GameContent.cs +++ b/src/LittleSim/Content/GameContent.cs @@ -1,5 +1,6 @@ using MrGameEng.Atlases; using MrGameEng.Core; +using MrGameEng.Genetics; using MrGameEng.Mods; namespace LittleSim.Content; @@ -56,6 +57,7 @@ public sealed class GameContent var defs = new DefDatabase(); defs.RegisterType("Terrain"); + defs.RegisterType("Gene"); defs.RegisterType("Plant"); defs.RegisterType("Pawn"); defs.RegisterType("WorldPreset"); diff --git a/src/LittleSim/Scenes/WorldScene.cs b/src/LittleSim/Scenes/WorldScene.cs index 84461fd..34b2a0f 100644 --- a/src/LittleSim/Scenes/WorldScene.cs +++ b/src/LittleSim/Scenes/WorldScene.cs @@ -11,6 +11,7 @@ using MrGameEng.Assets; using MrGameEng.Core; using MrGameEng.DevConsole; using MrGameEng.Formulas; +using MrGameEng.Genetics; using MrGameEng.Graphics; using MrGameEng.Host; using MrGameEng.Input; @@ -486,10 +487,57 @@ public sealed class WorldScene : Scene } } ); + console.Register( + "gene", + "gene [seed] — generate a genome from Gene defs, show alleles/traits and a bred child", + (c, args) => RunGeneDemo(c, content, args) + ); console.Register( "menu", "menu — return to the main menu", (_, _) => Switch(new MainMenuScene()) ); } + + // Демонстрация генной системы (фаза G2): из Gene-дефов генерируем геном, печатаем аллели, + // выраженные значения и признаки (вычисленные формулами), затем скрещиваем двух особей. + private static void RunGeneDemo(DevConsole console, GameContent content, string[] args) + { + var genes = content.Defs.All(); + if (genes.Count == 0) + { + console.WriteLine("no Gene defs loaded"); + return; + } + + var registry = genes.ToDictionary(g => g.DefName, g => g, StringComparer.Ordinal); + var seed = args.Length > 0 ? int.Parse(args[0]) : Random.Shared.Next(); + var random = new Random(seed); + + console.WriteLine($"genome from {genes.Count} genes, seed {seed}:"); + var parentA = Genome.Generate(genes, random); + foreach (var gene in genes) + { + var allele = parentA[gene.DefName]; + console.WriteLine( + $" {gene.DefName}: [{allele.A:0.##}, {allele.B:0.##}] -> {parentA.Express(gene):0.###}" + ); + } + + console.WriteLine("traits:"); + foreach (var (trait, value) in Phenotype.Compute(parentA, registry).OrderBy(t => t.Key)) + { + console.WriteLine($" {trait} = {value:0.###}"); + } + + var parentB = Genome.Generate(genes, random); + var child = Genome.Breed(parentA, parentB, registry, random); + var childTraits = Phenotype.Compute(child, registry); + console.WriteLine( + $"bred child: {child.Alleles.Count} genes, " + + $"vigor={childTraits.GetValueOrDefault("vigor"):0.###}, " + + $"lifespan={childTraits.GetValueOrDefault("lifespan"):0.#}, " + + $"variant={childTraits.GetValueOrDefault("variant"):0}" + ); + } }