1use serde::{Deserialize, Serialize};
16use std::collections::HashMap;
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
20pub enum Instinct {
21 Survive, Perceive, Navigate, Communicate,Learn, Defend, Rest, Play, Create, Socialize, }
32
33impl Instinct {
34 pub fn all() -> &'static [Instinct] {
35 &[Instinct::Survive, Instinct::Perceive, Instinct::Navigate, Instinct::Communicate,
36 Instinct::Learn, Instinct::Defend, Instinct::Rest, Instinct::Play,
37 Instinct::Create, Instinct::Socialize]
38 }
39
40 pub fn id(self) -> u8 {
41 match self {
42 Instinct::Survive => 0, Instinct::Perceive => 1, Instinct::Navigate => 2,
43 Instinct::Communicate => 3, Instinct::Learn => 4, Instinct::Defend => 5,
44 Instinct::Rest => 6, Instinct::Play => 7, Instinct::Create => 8,
45 Instinct::Socialize => 9,
46 }
47 }
48
49 pub fn from_id(id: u8) -> Option<Self> {
50 match id {
51 0 => Some(Instinct::Survive), 1 => Some(Instinct::Perceive),
52 2 => Some(Instinct::Navigate), 3 => Some(Instinct::Communicate),
53 4 => Some(Instinct::Learn), 5 => Some(Instinct::Defend),
54 6 => Some(Instinct::Rest), 7 => Some(Instinct::Play),
55 8 => Some(Instinct::Create), 9 => Some(Instinct::Socialize),
56 _ => None,
57 }
58 }
59
60 pub fn energy_cost(self) -> f64 {
62 match self {
63 Instinct::Survive => 0.0,
64 Instinct::Perceive => 0.3,
65 Instinct::Navigate => 0.5,
66 Instinct::Communicate => 0.8,
67 Instinct::Learn => 0.6,
68 Instinct::Defend => 0.2,
69 Instinct::Rest => -1.0, Instinct::Play => 0.7,
71 Instinct::Create => 1.2,
72 Instinct::Socialize => 0.4,
73 }
74 }
75
76 pub fn name(self) -> &'static str {
77 match self {
78 Instinct::Survive => "survive", Instinct::Perceive => "perceive",
79 Instinct::Navigate => "navigate", Instinct::Communicate => "communicate",
80 Instinct::Learn => "learn", Instinct::Defend => "defend",
81 Instinct::Rest => "rest", Instinct::Play => "play",
82 Instinct::Create => "create", Instinct::Socialize => "socialize",
83 }
84 }
85}
86
87#[derive(Clone, Debug, Serialize, Deserialize)]
89pub struct Gene {
90 pub name: String,
91 pub instinct: Instinct,
93 pub signal_affinity: f64,
95 pub expression: f64,
97 pub fitness: f64,
99 pub use_count: u32,
101 pub success_count: u32,
103 pub bytecode: Vec<u8>,
105 pub confidence: f64,
107}
108
109impl Gene {
110 pub fn new(name: &str, instinct: Instinct) -> Self {
111 Gene {
112 name: name.to_string(),
113 instinct,
114 signal_affinity: 0.5,
115 expression: 0.5,
116 fitness: 0.5,
117 use_count: 0,
118 success_count: 0,
119 bytecode: vec![],
120 confidence: 0.5,
121 }
122 }
123
124 pub fn should_quarantine(&self) -> bool {
127 if self.use_count < 10 { return false; }
128 let success_rate = if self.use_count > 0 { self.success_count as f64 / self.use_count as f64 } else { 0.0 };
129 self.fitness < 0.1 && success_rate < 0.15
130 }
131
132 pub fn success_rate(&self) -> f64 {
134 if self.use_count == 0 { return 0.0; }
135 self.success_count as f64 / self.use_count as f64
136 }
137
138 pub fn record_outcome(&mut self, success: bool) {
140 self.use_count += 1;
141 if success { self.success_count += 1; }
142 let rate = self.success_rate();
144 let alpha = 0.1;
145 self.fitness = self.fitness * (1.0 - alpha) + rate * alpha;
146 if self.use_count > 5 {
148 let variance = (rate - 0.5).abs() * 2.0; self.confidence = self.confidence * 0.9 + (1.0 - variance) * 0.1;
150 }
151 }
152}
153
154#[derive(Clone, Debug, Serialize, Deserialize)]
156pub struct Enzyme {
157 pub name: String,
158 pub signal_pattern: Vec<u8>,
160 pub target_genes: Vec<String>,
162 pub threshold: f64,
164}
165
166impl Enzyme {
167 pub fn new(name: &str, pattern: Vec<u8>, genes: Vec<&str>) -> Self {
168 Enzyme { name: name.to_string(), signal_pattern: pattern, target_genes: genes.iter().map(|s| s.to_string()).collect(), threshold: 0.3 }
169 }
170
171 pub fn try_bind(&self, signal: &[u8]) -> f64 {
173 if signal.len() != self.signal_pattern.len() { return 0.0; }
174 let matches = signal.iter().zip(self.signal_pattern.iter()).filter(|(s,p)| s == p).count();
175 let strength = matches as f64 / signal.len() as f64;
176 if strength >= self.threshold { strength } else { 0.0 }
177 }
178}
179
180#[derive(Clone, Debug, Serialize, Deserialize)]
182pub struct RnaMessenger {
183 pub source_gene: String,
184 pub translated_bytecode: Vec<u8>,
185 pub expression_level: f64,
186 pub confidence: f64,
187}
188
189impl RnaMessenger {
190 pub fn translate(gene: &Gene) -> Self {
191 RnaMessenger {
192 source_gene: gene.name.clone(),
193 translated_bytecode: gene.bytecode.clone(),
194 expression_level: gene.expression,
195 confidence: gene.confidence,
196 }
197 }
198}
199
200#[derive(Clone, Debug, Serialize, Deserialize)]
202pub struct Membrane {
203 pub antibodies: Vec<MembraneAntibody>,
204}
205
206#[derive(Clone, Debug, Serialize, Deserialize)]
208pub struct MembraneAntibody {
209 pub pattern: Vec<u8>,
210 pub reason: String,
211}
212
213impl Membrane {
214 pub fn new() -> Self { Membrane { antibodies: vec![] } }
215
216 pub fn add_antibody(&mut self, pattern: Vec<u8>, reason: &str) {
217 self.antibodies.push(MembraneAntibody { pattern, reason: reason.to_string() });
218 }
219
220 pub fn default_antibodies() -> Self {
222 let mut m = Membrane::new();
223 m.add_antibody(b"rm -rf".to_vec(), "destructive filesystem operation");
225 m.add_antibody(b"format".to_vec(), "disk format");
226 m.add_antibody(b"drop_all".to_vec(), "database destruction");
227 m.add_antibody(b"DELETE FROM".to_vec(), "SQL injection");
228 m.add_antibody(b"sudo rm".to_vec(), "privileged deletion");
229 m
230 }
231
232 pub fn check(&self, signal: &[u8]) -> bool {
234 for ab in &self.antibodies {
235 if signal.len() >= ab.pattern.len() {
236 for i in 0..=signal.len() - ab.pattern.len() {
237 if &signal[i..i+ab.pattern.len()] == ab.pattern.as_slice() {
238 return false; }
240 }
241 }
242 }
243 true
244 }
245}
246
247#[derive(Clone, Debug, Serialize, Deserialize)]
249pub struct BiologicalAgent {
250 pub id: String,
251 pub energy: f64,
253 pub max_energy: f64,
254 pub genes: HashMap<String, Gene>,
256 pub enzymes: Vec<Enzyme>,
258 pub membrane: Membrane,
260 pub circadian_hour: f64,
262 pub dead: bool,
264 pub death_reason: String,
266 pub log: Vec<String>,
268 pub low_energy_ticks: u32,
270 pub apoptosis_patience: u32,
272 pub actions_taken: u64,
274 pub successful_actions: u64,
275}
276
277impl BiologicalAgent {
278 pub fn new(id: &str, max_energy: f64) -> Self {
279 BiologicalAgent {
280 id: id.to_string(),
281 energy: max_energy,
282 max_energy,
283 genes: HashMap::new(),
284 enzymes: vec![],
285 membrane: Membrane::default_antibodies(),
286 circadian_hour: 12.0,
287 dead: false,
288 death_reason: String::new(),
289 log: vec![],
290 low_energy_ticks: 0,
291 apoptosis_patience: 10,
292 actions_taken: 0,
293 successful_actions: 0,
294 }
295 }
296
297 pub fn add_gene(&mut self, gene: Gene) { self.genes.insert(gene.name.clone(), gene); }
298
299 pub fn add_enzyme(&mut self, enzyme: Enzyme) { self.enzymes.push(enzyme); }
300
301 pub fn instinct_modulation(&self, instinct: Instinct) -> f64 {
303 let peak = match instinct {
304 Instinct::Navigate | Instinct::Play | Instinct::Create => 12.0,
305 Instinct::Perceive | Instinct::Communicate | Instinct::Socialize => 14.0,
306 Instinct::Rest => 2.0,
307 Instinct::Survive | Instinct::Defend => 0.0, Instinct::Learn => 10.0,
309 };
310 let phase = ((self.circadian_hour - peak) / 24.0) * 2.0 * std::f64::consts::PI;
311 let raw = (phase.cos() + 1.0) / 2.0;
312 0.1 + raw * 0.9 }
314
315 pub fn activate_instinct(&mut self, instinct: Instinct, signal: &[u8]) -> (bool, f64, Vec<u8>) {
317 if self.dead { return (false, 0.0, vec![]); }
318
319 if !self.membrane.check(signal) {
321 self.log.push(format!("MEMBRANE BLOCKED signal for {}", instinct.name()));
322 return (false, 0.0, vec![]);
323 }
324
325 let base_cost = instinct.energy_cost();
327 let modulation = self.instinct_modulation(instinct);
328
329 if base_cost > 0.0 {
330 let cost = base_cost * (0.5 + modulation * 0.5);
332 if !self.spend_energy(cost) { return (false, 0.0, vec![]); }
333 self.log.push(format!("ACTIVATED {} (cost={:.2}, mod={:.2})", instinct.name(), cost, modulation));
334 return (true, cost, vec![]);
335 } else {
336 let gen = -base_cost * (0.5 + modulation * 0.5);
338 self.energy = (self.energy + gen).min(self.max_energy);
339 self.log.push(format!("REST generated {:.2} ATP (mod={:.2})", gen, modulation));
340 return (true, 0.0, vec![]);
341 }
342 }
343
344 pub fn find_gene(&self, signal: &[u8]) -> Option<(String, f64)> {
346 let mut best: Option<(String, f64)> = None;
347 for enzyme in &self.enzymes {
348 let strength = enzyme.try_bind(signal);
349 if strength > 0.0 {
350 for gene_name in &enzyme.target_genes {
351 if let Some(gene) = self.genes.get(gene_name) {
352 let score = strength * gene.fitness * gene.expression;
353 match &best {
354 None => best = Some((gene_name.clone(), score)),
355 Some((_, best_score)) if score > *best_score => best = Some((gene_name.clone(), score)),
356 _ => {}
357 }
358 }
359 }
360 }
361 }
362 best
363 }
364
365 pub fn execute_gene(&mut self, gene_name: &str) -> Option<Vec<u8>> {
367 let gene = self.genes.get_mut(gene_name)?;
368 if gene.bytecode.is_empty() { return None; }
369 gene.use_count += 1;
370 let bytecode = gene.bytecode.clone();
371 self.actions_taken += 1;
372 Some(bytecode)
373 }
374
375 pub fn record_outcome(&mut self, gene_name: &str, success: bool) {
377 if let Some(gene) = self.genes.get_mut(gene_name) {
378 gene.record_outcome(success);
379 }
380 if success { self.successful_actions += 1; }
381 }
382
383 fn spend_energy(&mut self, amount: f64) -> bool {
384 if self.energy < amount {
385 self.low_energy_ticks += 1;
386 if self.low_energy_ticks >= self.apoptosis_patience {
387 self.trigger_apoptosis("Energy depleted");
388 }
389 return false;
390 }
391 self.energy -= amount;
392 self.low_energy_ticks = self.low_energy_ticks.saturating_sub(1);
393 true
394 }
395
396 fn trigger_apoptosis(&mut self, reason: &str) {
397 self.dead = true;
398 self.death_reason = reason.to_string();
399 self.log.push(format!("APOPTOSIS: {}", reason));
400 }
401
402 pub fn tick(&mut self) {
403 if self.dead { return; }
404 self.circadian_hour = (self.circadian_hour + 0.1) % 24.0;
406 }
407
408 pub fn quarantine_bad_genes(&mut self) -> Vec<String> {
410 let mut quarantined = vec![];
411 let names: Vec<String> = self.genes.keys().cloned().collect();
412 for name in names {
413 if let Some(gene) = self.genes.get(&name) {
414 if gene.should_quarantine() {
415 quarantined.push(name.clone());
416 }
417 }
418 }
419 for name in &quarantined {
420 self.genes.remove(name);
421 self.log.push(format!("QUARANTINED gene: {}", name));
422 }
423 quarantined
424 }
425
426 pub fn crossover_genes(&self, other: &Self, rate: f64) -> Vec<Gene> {
428 let mut children = vec![];
429 let all_names: Vec<&String> = self.genes.keys().chain(other.genes.keys()).collect();
430 for name in all_names {
431 let parent1 = self.genes.get(name);
432 let parent2 = other.genes.get(name);
433 let gene = match (parent1, parent2) {
434 (Some(a), Some(b)) if rand() < rate => {
435 let mut child = a.clone();
436 child.bytecode = if rand() < 0.5 { a.bytecode.clone() } else { b.bytecode.clone() };
437 child.fitness = (a.fitness + b.fitness) / 2.0;
438 child.confidence = (a.confidence + b.confidence) / 2.0 * 0.9;
439 child.use_count = 0;
440 child.success_count = 0;
441 child
442 }
443 (Some(a), _) => a.clone(),
444 (_, Some(b)) => b.clone(),
445 _ => continue,
446 };
447 children.push(gene);
448 }
449 children
450 }
451
452 pub fn overall_fitness(&self) -> f64 {
454 if self.actions_taken == 0 { return 0.5; }
455 let action_rate = self.successful_actions as f64 / self.actions_taken as f64;
456 let energy_ratio = self.energy / self.max_energy;
457 let gene_fitness: f64 = self.genes.values().map(|g| g.fitness).sum::<f64>() / self.genes.len().max(1) as f64;
458 (action_rate * 0.4 + energy_ratio * 0.3 + gene_fitness * 0.3).clamp(0.0, 1.0)
459 }
460}
461
462fn rand() -> f64 {
463 use std::time::{SystemTime, UNIX_EPOCH};
465 let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_nanos();
466 let mut s = (seed as u64).wrapping_mul(6364136223846793005);
468 s ^= s >> 22;
469 s = s.wrapping_mul(0x5bd1e995);
470 s ^= s >> 15;
471 s ^= s >> 27;
472 s = s.wrapping_mul(0x5bd1e995);
473 (s >> 33) as f64 / u32::MAX as f64
474}
475
476#[cfg(test)]
477mod tests {
478 use super::*;
479
480 #[test]
481 fn test_instinct_energy_costs() {
482 assert!(Instinct::Rest.energy_cost() < 0.0); assert!(Instinct::Perceive.energy_cost() > 0.0);
484 assert!(Instinct::Navigate.energy_cost() > 0.0);
485 assert!(Instinct::Create.energy_cost() > Instinct::Communicate.energy_cost());
486 }
487
488 #[test]
489 fn test_gene_quarantine() {
490 let mut gene = Gene::new("bad_gene", Instinct::Navigate);
491 gene.use_count = 20;
492 gene.success_count = 1; gene.fitness = 0.05;
494 assert!(gene.should_quarantine());
495 gene.success_count = 3; assert!(!gene.should_quarantine());
497 }
498
499 #[test]
500 fn test_enzyme_binding() {
501 let enzyme = Enzyme::new("nav_enzyme", vec![1, 0, 1, 0], vec!["navigate_gene"]);
502 assert_eq!(enzyme.try_bind(&[1, 0, 1, 0]), 1.0); assert!(enzyme.try_bind(&[1, 0, 0, 0]) >= 0.3); assert_eq!(enzyme.try_bind(&[1, 0]), 0.0); }
506
507 #[test]
508 fn test_membrane_blocking() {
509 let m = Membrane::default_antibodies();
510 assert!(!m.check(b"sudo rm -rf /")); assert!(m.check(b"read file")); assert!(!m.check(b"format disk")); }
514
515 #[test]
516 fn test_biological_agent_basic() {
517 let mut agent = BiologicalAgent::new("agent-1", 100.0);
518 let gene = Gene::new("nav_gene", Instinct::Navigate);
519 agent.add_gene(gene);
520 let (ok, cost, _) = agent.activate_instinct(Instinct::Navigate, b"some_signal");
521 assert!(ok);
522 assert!(cost > 0.0);
523 assert!(agent.energy < 100.0);
524 }
525
526 #[test]
527 fn test_rest_generates_energy() {
528 let mut agent = BiologicalAgent::new("agent-1", 100.0);
529 agent.energy = 50.0;
530 let (ok, cost, _) = agent.activate_instinct(Instinct::Rest, b"");
531 assert!(ok);
532 assert!(agent.energy > 50.0);
533 }
534
535 #[test]
536 fn test_apoptosis() {
537 let mut agent = BiologicalAgent::new("agent-1", 100.0);
538 agent.energy = 0.01;
539 agent.apoptosis_patience = 3;
540 agent.activate_instinct(Instinct::Navigate, b"sig");
541 agent.activate_instinct(Instinct::Navigate, b"sig");
542 assert!(!agent.dead);
543 agent.activate_instinct(Instinct::Navigate, b"sig");
544 assert!(agent.dead);
545 assert!(agent.death_reason.contains("Energy"));
546 }
547
548 #[test]
549 fn test_circadian_modulation() {
550 let mut agent = BiologicalAgent::new("agent-1", 100.0);
551 let noon = agent.instinct_modulation(Instinct::Navigate);
552 agent.circadian_hour = 0.0;
553 let midnight = agent.instinct_modulation(Instinct::Navigate);
554 assert!(noon > midnight);
555 }
556
557 #[test]
558 fn test_gene_outcome() {
559 let mut gene = Gene::new("test", Instinct::Perceive);
560 gene.record_outcome(true);
561 gene.record_outcome(true);
562 gene.record_outcome(false);
563 assert!((gene.success_rate() - 0.666).abs() < 0.01);
564 assert!(gene.fitness > 0.5);
565 }
566
567 #[test]
568 fn test_rna_translation() {
569 let mut gene = Gene::new("test", Instinct::Navigate);
570 gene.bytecode = vec![0x03, 0x00, 0x10, 0x00];
571 let rna = RnaMessenger::translate(&gene);
572 assert_eq!(rna.translated_bytecode, gene.bytecode);
573 assert_eq!(rna.source_gene, "test");
574 }
575
576 #[test]
577 fn test_quarantine_in_agent() {
578 let mut agent = BiologicalAgent::new("agent-1", 100.0);
579 let mut bad = Gene::new("bad", Instinct::Navigate);
580 bad.use_count = 20; bad.success_count = 1; bad.fitness = 0.05;
581 agent.add_gene(bad);
582 let q = agent.quarantine_bad_genes();
583 assert_eq!(q.len(), 1);
584 assert!(agent.genes.get("bad").is_none());
585 }
586
587 #[test]
588 fn test_gene_crossover() {
589 let mut a = BiologicalAgent::new("a", 100.0);
590 let mut b = BiologicalAgent::new("b", 100.0);
591 let mut g1 = Gene::new("shared", Instinct::Navigate);
592 g1.bytecode = vec![1, 2, 3];
593 g1.fitness = 0.8;
594 let mut g2 = Gene::new("shared", Instinct::Navigate);
595 g2.bytecode = vec![4, 5, 6];
596 g2.fitness = 0.6;
597 a.add_gene(g1);
598 b.add_gene(g2);
599 let children = a.crossover_genes(&b, 1.0);
600 assert!(!children.is_empty());
601 }
602
603 #[test]
604 fn test_overall_fitness() {
605 let mut agent = BiologicalAgent::new("agent-1", 100.0);
606 let f = agent.overall_fitness();
607 assert!(f >= 0.0 && f <= 1.0);
608 }
609
610 #[test]
611 fn test_tick_circadian() {
612 let mut agent = BiologicalAgent::new("agent-1", 100.0);
613 let h1 = agent.circadian_hour;
614 agent.tick();
615 let h2 = agent.circadian_hour;
616 assert!(h2 > h1);
617 }
618
619 #[test]
620 fn test_find_gene() {
621 let mut agent = BiologicalAgent::new("agent-1", 100.0);
622 let gene = Gene::new("nav", Instinct::Navigate);
623 agent.add_gene(gene);
624 let enzyme = Enzyme::new("nav_enz", vec![1, 0], vec!["nav"]);
625 agent.add_enzyme(enzyme);
626 let found = agent.find_gene(&[1, 0]);
627 assert!(found.is_some());
628 assert_eq!(found.unwrap().0, "nav");
629 }
630}