1use super::Rng;
7
8#[derive(Debug, Clone, Copy)]
10pub struct Gene { pub allele_a: u8, pub allele_b: u8 }
11
12impl Gene {
13 pub fn new(a: u8, b: u8) -> Self { Self { allele_a: a, allele_b: b } }
14 pub fn homozygous(&self) -> bool { self.allele_a == self.allele_b }
15 pub fn express(&self) -> u8 { self.allele_a.max(self.allele_b) }
17 pub fn express_codominant(&self) -> f32 { (self.allele_a as f32 + self.allele_b as f32) * 0.5 }
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum TraitType {
24 Size, Strength, Speed, Intelligence, Aggression, Coloration,
25 Pattern, HornSize, TailLength, WingSpan, Armor, Venom,
26 Bioluminescence, Regeneration, Camouflage, SenseAcuity,
27}
28
29#[derive(Debug, Clone)]
31pub struct Genome {
32 pub genes: Vec<(TraitType, Gene)>,
33 pub mutation_rate: f32,
34}
35
36impl Genome {
37 pub fn random(rng: &mut Rng) -> Self {
38 let traits = [
39 TraitType::Size, TraitType::Strength, TraitType::Speed,
40 TraitType::Intelligence, TraitType::Aggression, TraitType::Coloration,
41 TraitType::Pattern, TraitType::HornSize, TraitType::TailLength,
42 TraitType::WingSpan, TraitType::Armor, TraitType::Venom,
43 TraitType::Bioluminescence, TraitType::Regeneration,
44 TraitType::Camouflage, TraitType::SenseAcuity,
45 ];
46 let genes = traits.iter().map(|&t| {
47 (t, Gene::new(rng.range_u32(0, 256) as u8, rng.range_u32(0, 256) as u8))
48 }).collect();
49 Self { genes, mutation_rate: 0.02 }
50 }
51
52 pub fn phenotype(&self, trait_type: TraitType) -> f32 {
54 self.genes.iter()
55 .find(|(t, _)| *t == trait_type)
56 .map(|(_, g)| g.express_codominant() / 255.0)
57 .unwrap_or(0.5)
58 }
59
60 pub fn crossover(&self, other: &Genome, rng: &mut Rng) -> Genome {
62 let mut child_genes = Vec::with_capacity(self.genes.len());
63 for (i, (trait_type, gene_a)) in self.genes.iter().enumerate() {
64 let gene_b = &other.genes[i].1;
65 let allele_a = if rng.coin(0.5) { gene_a.allele_a } else { gene_a.allele_b };
67 let allele_b = if rng.coin(0.5) { gene_b.allele_a } else { gene_b.allele_b };
68 child_genes.push((*trait_type, Gene::new(allele_a, allele_b)));
69 }
70 Genome { genes: child_genes, mutation_rate: (self.mutation_rate + other.mutation_rate) * 0.5 }
71 }
72
73 pub fn mutate(&mut self, rng: &mut Rng) {
75 for (_, gene) in &mut self.genes {
76 if rng.coin(self.mutation_rate) {
77 let delta = (rng.gaussian() * 10.0) as i16;
78 gene.allele_a = (gene.allele_a as i16 + delta).clamp(0, 255) as u8;
79 }
80 if rng.coin(self.mutation_rate) {
81 let delta = (rng.gaussian() * 10.0) as i16;
82 gene.allele_b = (gene.allele_b as i16 + delta).clamp(0, 255) as u8;
83 }
84 }
85 }
86
87 pub fn fitness(&self, env: &Environment) -> f32 {
89 let mut score = 0.0_f32;
90 score += (self.phenotype(TraitType::Size) - env.ideal_size).abs() * -1.0;
91 score += self.phenotype(TraitType::Speed) * env.predation_pressure;
92 score += self.phenotype(TraitType::Camouflage) * env.predation_pressure * 0.5;
93 score += self.phenotype(TraitType::Armor) * env.predation_pressure * 0.3;
94 score += self.phenotype(TraitType::Intelligence) * 0.2;
95 score += 1.0; score.max(0.0)
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct Environment {
103 pub ideal_size: f32,
104 pub predation_pressure: f32,
105 pub food_availability: f32,
106 pub temperature: f32,
107}
108
109impl Default for Environment {
110 fn default() -> Self {
111 Self { ideal_size: 0.5, predation_pressure: 0.5, food_availability: 0.5, temperature: 0.5 }
112 }
113}
114
115#[derive(Debug, Clone)]
117pub struct Population {
118 pub genomes: Vec<Genome>,
119 pub generation: u32,
120}
121
122impl Population {
123 pub fn random(size: usize, rng: &mut Rng) -> Self {
124 let genomes = (0..size).map(|_| Genome::random(rng)).collect();
125 Self { genomes, generation: 0 }
126 }
127
128 pub fn evolve(&mut self, env: &Environment, rng: &mut Rng) {
130 let n = self.genomes.len();
131 if n < 2 { return; }
132
133 let fitnesses: Vec<f32> = self.genomes.iter().map(|g| g.fitness(env)).collect();
135 let total_fitness: f32 = fitnesses.iter().sum();
136 if total_fitness < 0.01 { return; }
137
138 let mut next_gen = Vec::with_capacity(n);
139 for _ in 0..n {
140 let parent_a = roulette_select(&fitnesses, total_fitness, rng);
141 let parent_b = roulette_select(&fitnesses, total_fitness, rng);
142 let mut child = self.genomes[parent_a].crossover(&self.genomes[parent_b], rng);
143 child.mutate(rng);
144 next_gen.push(child);
145 }
146
147 self.genomes = next_gen;
148 self.generation += 1;
149 }
150
151 pub fn avg_phenotype(&self, trait_type: TraitType) -> f32 {
153 if self.genomes.is_empty() { return 0.5; }
154 let sum: f32 = self.genomes.iter().map(|g| g.phenotype(trait_type)).sum();
155 sum / self.genomes.len() as f32
156 }
157}
158
159fn roulette_select(fitnesses: &[f32], total: f32, rng: &mut Rng) -> usize {
160 let mut target = rng.next_f32() * total;
161 for (i, &f) in fitnesses.iter().enumerate() {
162 target -= f;
163 if target <= 0.0 { return i; }
164 }
165 fitnesses.len() - 1
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_genome_random() {
174 let mut rng = Rng::new(42);
175 let g = Genome::random(&mut rng);
176 assert_eq!(g.genes.len(), 16);
177 }
178
179 #[test]
180 fn test_crossover() {
181 let mut rng = Rng::new(42);
182 let a = Genome::random(&mut rng);
183 let b = Genome::random(&mut rng);
184 let child = a.crossover(&b, &mut rng);
185 assert_eq!(child.genes.len(), a.genes.len());
186 }
187
188 #[test]
189 fn test_evolution_changes_population() {
190 let mut rng = Rng::new(42);
191 let mut pop = Population::random(50, &mut rng);
192 let env = Environment { ideal_size: 0.8, predation_pressure: 0.7, ..Default::default() };
193 let initial_speed = pop.avg_phenotype(TraitType::Speed);
194 for _ in 0..100 { pop.evolve(&env, &mut rng); }
195 let final_speed = pop.avg_phenotype(TraitType::Speed);
197 assert!(pop.generation == 100);
199 }
200
201 #[test]
202 fn test_phenotype_range() {
203 let mut rng = Rng::new(42);
204 let g = Genome::random(&mut rng);
205 let p = g.phenotype(TraitType::Size);
206 assert!(p >= 0.0 && p <= 1.0);
207 }
208}