use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Instinct {
Survive,
Flee,
Defend,
Guard,
Perceive,
Navigate,
Report,
Hoard,
Rest,
Cooperate,
Communicate,
Teach,
Share,
Learn,
Curious,
Explore,
Mourn,
Evolve,
}
impl Instinct {
pub fn name(&self) -> &'static str {
match self {
Instinct::Survive => "Survive",
Instinct::Flee => "Flee",
Instinct::Defend => "Defend",
Instinct::Guard => "Guard",
Instinct::Perceive => "Perceive",
Instinct::Navigate => "Navigate",
Instinct::Report => "Report",
Instinct::Hoard => "Hoard",
Instinct::Rest => "Rest",
Instinct::Cooperate => "Cooperate",
Instinct::Communicate => "Communicate",
Instinct::Teach => "Teach",
Instinct::Share => "Share",
Instinct::Learn => "Learn",
Instinct::Curious => "Curious",
Instinct::Explore => "Explore",
Instinct::Mourn => "Mourn",
Instinct::Evolve => "Evolve",
}
}
pub fn source(&self) -> &'static str {
match self {
Instinct::Survive | Instinct::Cooperate => "both",
Instinct::Flee | Instinct::Guard | Instinct::Report
| Instinct::Hoard | Instinct::Teach | Instinct::Curious
| Instinct::Mourn | Instinct::Evolve => "flux-instinct",
Instinct::Defend | Instinct::Perceive | Instinct::Navigate
| Instinct::Rest | Instinct::Communicate | Instinct::Share
| Instinct::Learn | Instinct::Explore => "cuda-genepool",
}
}
pub fn all() -> &'static [Instinct] {
&[
Instinct::Survive,
Instinct::Flee,
Instinct::Defend,
Instinct::Guard,
Instinct::Perceive,
Instinct::Navigate,
Instinct::Report,
Instinct::Hoard,
Instinct::Rest,
Instinct::Cooperate,
Instinct::Communicate,
Instinct::Teach,
Instinct::Share,
Instinct::Learn,
Instinct::Curious,
Instinct::Explore,
Instinct::Mourn,
Instinct::Evolve,
]
}
}
impl fmt::Display for Instinct {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Severity {
Critical = 0,
High = 1,
Normal = 2,
Low = 3,
Once = 4,
Rare = 5,
}
impl fmt::Display for Severity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Severity::Critical => write!(f, "CRITICAL"),
Severity::High => write!(f, "HIGH"),
Severity::Normal => write!(f, "NORMAL"),
Severity::Low => write!(f, "LOW"),
Severity::Once => write!(f, "ONCE"),
Severity::Rare => write!(f, "RARE"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Enforcement {
Must, Should, Cannot, May, }
impl fmt::Display for Enforcement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Enforcement::Must => write!(f, "MUST"),
Enforcement::Should => write!(f, "SHOULD"),
Enforcement::Cannot => write!(f, "CANNOT"),
Enforcement::May => write!(f, "MAY"),
}
}
}
#[derive(Debug, Clone)]
pub struct Assertion {
pub instinct: Instinct,
pub enforcement: Enforcement,
pub description: String,
pub condition: String,
}
impl Assertion {
pub fn new(instinct: Instinct, enforcement: Enforcement, description: &str, condition: &str) -> Self {
Self {
instinct,
enforcement,
description: description.to_string(),
condition: condition.to_string(),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct State {
pub energy: f32, pub threat: f32, pub trust: f32, pub peer_alive: bool,
pub has_work: bool,
pub idle_cycles: u32,
pub capacity: f32, }
impl Default for State {
fn default() -> Self {
Self {
energy: 0.7,
threat: 0.0,
trust: 0.5,
peer_alive: true,
has_work: false,
idle_cycles: 0,
capacity: 0.8,
}
}
}
#[derive(Debug, Clone)]
pub struct Reflex {
pub instinct: Instinct,
pub urgency: f32, pub severity: Severity,
pub assertion: Assertion,
pub reason: String,
}
#[derive(Debug, Clone, Copy)]
pub struct Thresholds {
pub energy_critical: f32, pub energy_low: f32, pub energy_tired: f32, pub threat_high: f32, pub trust_cooperate: f32, pub trust_teach: f32, pub curiosity_interval: u32, pub exploration_interval: u32, pub evolve_interval: u32, pub sensory_gap: f32, }
impl Default for Thresholds {
fn default() -> Self {
Self {
energy_critical: 0.15,
energy_low: 0.4,
energy_tired: 0.6,
threat_high: 0.7,
trust_cooperate: 0.6,
trust_teach: 0.8,
curiosity_interval: 100,
exploration_interval: 200,
evolve_interval: 500,
sensory_gap: 0.5,
}
}
}
pub struct InstinctEngine {
thresholds: Thresholds,
mourned_peers: Vec<u32>, }
impl InstinctEngine {
pub fn new() -> Self {
Self {
thresholds: Thresholds::default(),
mourned_peers: Vec::new(),
}
}
pub fn with_thresholds(thresholds: Thresholds) -> Self {
Self {
thresholds,
mourned_peers: Vec::new(),
}
}
pub fn tick(&mut self, state: &State) -> Vec<Reflex> {
let mut reflexes = Vec::new();
if state.energy <= self.thresholds.energy_critical {
reflexes.push(Reflex {
instinct: Instinct::Survive,
urgency: 1.0 - state.energy,
severity: Severity::Critical,
assertion: Assertion::new(
Instinct::Survive,
Enforcement::Must,
"Agent MUST survive — energy at critical level",
&format!("energy <= {}", self.thresholds.energy_critical),
),
reason: format!("energy {:.3} <= critical {:.3}", state.energy, self.thresholds.energy_critical),
});
}
if state.threat > self.thresholds.threat_high {
reflexes.push(Reflex {
instinct: Instinct::Flee,
urgency: state.threat - self.thresholds.threat_high,
severity: Severity::High,
assertion: Assertion::new(
Instinct::Flee,
Enforcement::Must,
"Agent MUST flee — threat exceeds threshold",
&format!("threat > {}", self.thresholds.threat_high),
),
reason: format!("threat {:.3} > high {:.3}", state.threat, self.thresholds.threat_high),
});
}
if state.threat > 0.3 && state.energy > self.thresholds.energy_critical {
reflexes.push(Reflex {
instinct: Instinct::Defend,
urgency: state.threat * state.energy,
severity: Severity::High,
assertion: Assertion::new(
Instinct::Defend,
Enforcement::Must,
"Agent MUST defend when under attack with energy",
"threat > 0.3 AND energy > critical",
),
reason: "under attack with sufficient energy".to_string(),
});
}
if state.has_work && state.energy > self.thresholds.energy_low {
reflexes.push(Reflex {
instinct: Instinct::Guard,
urgency: 0.5,
severity: Severity::Normal,
assertion: Assertion::new(
Instinct::Guard,
Enforcement::Should,
"Agent SHOULD guard assigned work",
"has_work AND energy > low",
),
reason: "work assigned, energy sufficient".to_string(),
});
}
if !state.has_work && state.capacity > self.thresholds.sensory_gap {
reflexes.push(Reflex {
instinct: Instinct::Perceive,
urgency: state.capacity - self.thresholds.sensory_gap,
severity: Severity::Normal,
assertion: Assertion::new(
Instinct::Perceive,
Enforcement::Should,
"Agent SHOULD perceive — idle with processing capacity",
&format!("NOT has_work AND capacity > {}", self.thresholds.sensory_gap),
),
reason: "idle cycles available for perception".to_string(),
});
}
if state.idle_cycles > 10 && state.energy > self.thresholds.energy_low {
reflexes.push(Reflex {
instinct: Instinct::Navigate,
urgency: 0.3,
severity: Severity::Normal,
assertion: Assertion::new(
Instinct::Navigate,
Enforcement::Should,
"Agent SHOULD navigate toward targets when able",
"idle > 10 AND energy > low",
),
reason: "idle with energy — check navigation targets".to_string(),
});
}
if state.threat > 0.4 && state.threat <= self.thresholds.threat_high {
reflexes.push(Reflex {
instinct: Instinct::Report,
urgency: state.threat - 0.4,
severity: Severity::Normal,
assertion: Assertion::new(
Instinct::Report,
Enforcement::Should,
"Agent SHOULD report anomaly",
"threat > 0.4 AND threat <= high",
),
reason: format!("elevated threat detected: {:.3}", state.threat),
});
}
if state.energy <= self.thresholds.energy_low && state.energy > self.thresholds.energy_critical {
reflexes.push(Reflex {
instinct: Instinct::Hoard,
urgency: (self.thresholds.energy_low - state.energy) * 0.5,
severity: Severity::Low,
assertion: Assertion::new(
Instinct::Hoard,
Enforcement::Should,
"Agent SHOULD hoard resources when energy is low",
&format!("energy <= {} AND energy > {}", self.thresholds.energy_low, self.thresholds.energy_critical),
),
reason: "energy below comfortable level".to_string(),
});
}
if state.energy < self.thresholds.energy_tired && state.energy > self.thresholds.energy_critical && !state.has_work {
reflexes.push(Reflex {
instinct: Instinct::Rest,
urgency: (self.thresholds.energy_tired - state.energy) * 0.3,
severity: Severity::Low,
assertion: Assertion::new(
Instinct::Rest,
Enforcement::May,
"Agent MAY rest when tired and no urgent work",
&format!("energy < {} AND NOT has_work", self.thresholds.energy_tired),
),
reason: "energy below comfortable, no urgent work".to_string(),
});
}
if state.trust > self.thresholds.trust_cooperate {
reflexes.push(Reflex {
instinct: Instinct::Cooperate,
urgency: (state.trust - self.thresholds.trust_cooperate) * 0.5,
severity: Severity::Normal,
assertion: Assertion::new(
Instinct::Cooperate,
Enforcement::Should,
"Agent SHOULD cooperate with trusted peers",
&format!("trust > {}", self.thresholds.trust_cooperate),
),
reason: format!("trust {:.3} exceeds cooperate threshold", state.trust),
});
}
if state.idle_cycles > 5 && state.trust > 0.4 {
reflexes.push(Reflex {
instinct: Instinct::Communicate,
urgency: 0.2,
severity: Severity::Normal,
assertion: Assertion::new(
Instinct::Communicate,
Enforcement::Should,
"Agent SHOULD communicate when idle with trusted peers",
"idle > 5 AND trust > 0.4",
),
reason: "idle with communicable peers".to_string(),
});
}
if state.trust > self.thresholds.trust_teach && state.capacity > 0.6 {
reflexes.push(Reflex {
instinct: Instinct::Teach,
urgency: 0.15,
severity: Severity::Low,
assertion: Assertion::new(
Instinct::Teach,
Enforcement::May,
"Agent MAY teach when highly trusted and has capacity",
&format!("trust > {} AND capacity > 0.6", self.thresholds.trust_teach),
),
reason: "high trust, excess capacity — teaching opportunity".to_string(),
});
}
if state.energy > 0.7 && state.trust > self.thresholds.trust_cooperate {
reflexes.push(Reflex {
instinct: Instinct::Share,
urgency: 0.1,
severity: Severity::Low,
assertion: Assertion::new(
Instinct::Share,
Enforcement::May,
"Agent MAY share excess resources with trusted peers",
"energy > 0.7 AND trust > cooperate_threshold",
),
reason: "excess energy, trusted peers — sharing opportunity".to_string(),
});
}
if !state.has_work && state.capacity > 0.7 {
reflexes.push(Reflex {
instinct: Instinct::Learn,
urgency: 0.3,
severity: Severity::Normal,
assertion: Assertion::new(
Instinct::Learn,
Enforcement::Should,
"Agent SHOULD learn when capacity available and no urgent work",
"NOT has_work AND capacity > 0.7",
),
reason: "available capacity for learning".to_string(),
});
}
if state.idle_cycles > 0 && state.idle_cycles % self.thresholds.curiosity_interval == 0 {
reflexes.push(Reflex {
instinct: Instinct::Curious,
urgency: 0.15,
severity: Severity::Low,
assertion: Assertion::new(
Instinct::Curious,
Enforcement::May,
"Agent MAY explore curiosities on idle schedule",
&format!("idle_cycles % {} == 0", self.thresholds.curiosity_interval),
),
reason: format!("curiosity trigger at {} idle cycles", state.idle_cycles),
});
}
if state.idle_cycles > 0 && state.idle_cycles % self.thresholds.exploration_interval == 0 {
reflexes.push(Reflex {
instinct: Instinct::Explore,
urgency: 0.1,
severity: Severity::Low,
assertion: Assertion::new(
Instinct::Explore,
Enforcement::May,
"Agent MAY explore new territory on idle schedule",
&format!("idle_cycles % {} == 0", self.thresholds.exploration_interval),
),
reason: format!("exploration trigger at {} idle cycles", state.idle_cycles),
});
}
if state.idle_cycles > 0 && state.idle_cycles % self.thresholds.evolve_interval == 0 {
reflexes.push(Reflex {
instinct: Instinct::Evolve,
urgency: 0.05,
severity: Severity::Rare,
assertion: Assertion::new(
Instinct::Evolve,
Enforcement::May,
"Agent MAY evolve behavioral parameters on schedule",
&format!("idle_cycles % {} == 0", self.thresholds.evolve_interval),
),
reason: format!("evolution trigger at {} idle cycles", state.idle_cycles),
});
}
reflexes.sort_by(|a, b| b.urgency.partial_cmp(&a.urgency).unwrap_or(std::cmp::Ordering::Equal));
reflexes
}
pub fn peer_died(&mut self, peer_id: u32) -> Option<Reflex> {
if self.mourned_peers.contains(&peer_id) {
return None;
}
self.mourned_peers.push(peer_id);
Some(Reflex {
instinct: Instinct::Mourn,
urgency: 0.4,
severity: Severity::Once,
assertion: Assertion::new(
Instinct::Mourn,
Enforcement::Cannot,
"Agent MUST mourn peer death exactly once",
&format!("peer {} died, NOT already mourned", peer_id),
),
reason: format!("peer {} has died", peer_id),
})
}
pub fn highest_priority(reflexes: &[Reflex]) -> Option<&Reflex> {
reflexes.first()
}
pub fn all_assertions(&mut self) -> Vec<Assertion> {
let state = State::default();
let mut all = self.tick(&state);
all.push(Reflex {
instinct: Instinct::Mourn,
urgency: 0.4,
severity: Severity::Once,
assertion: Assertion::new(
Instinct::Mourn,
Enforcement::Cannot,
"Mourn MUST fire exactly once per peer death",
"mourned_peers NOT contains peer_id",
),
reason: "mourn constraint definition".to_string().to_string(),
});
all.into_iter().map(|r| r.assertion).collect()
}
pub fn instinct_count() -> usize {
18
}
}
impl Default for InstinctEngine {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_all_instincts_count() {
assert_eq!(Instinct::all().len(), 18);
}
#[test]
fn test_all_instincts_have_names() {
for i in Instinct::all() {
assert!(!i.name().is_empty());
}
}
#[test]
fn test_all_instincts_have_source() {
for i in Instinct::all() {
assert!(matches!(i.source(), "both" | "flux-instinct" | "cuda-genepool"));
}
}
#[test]
fn test_shared_instincts() {
let both_count = Instinct::all().iter().filter(|i| i.source() == "both").count();
assert_eq!(both_count, 2); }
#[test]
fn test_survive_critical() {
let mut engine = InstinctEngine::new();
let state = State { energy: 0.1, threat: 0.0, ..Default::default() };
let reflexes = engine.tick(&state);
assert!(reflexes.iter().any(|r| r.instinct == Instinct::Survive && r.severity == Severity::Critical));
}
#[test]
fn test_flee_high_threat() {
let mut engine = InstinctEngine::new();
let state = State { energy: 0.8, threat: 0.9, ..Default::default() };
let reflexes = engine.tick(&state);
assert!(reflexes.iter().any(|r| r.instinct == Instinct::Flee && r.severity == Severity::High));
}
#[test]
fn test_defend_under_attack() {
let mut engine = InstinctEngine::new();
let state = State { energy: 0.7, threat: 0.5, ..Default::default() };
let reflexes = engine.tick(&state);
assert!(reflexes.iter().any(|r| r.instinct == Instinct::Defend));
}
#[test]
fn test_guard_with_work() {
let mut engine = InstinctEngine::new();
let state = State { energy: 0.7, threat: 0.0, has_work: true, ..Default::default() };
let reflexes = engine.tick(&state);
assert!(reflexes.iter().any(|r| r.instinct == Instinct::Guard));
}
#[test]
fn test_cooperate_high_trust() {
let mut engine = InstinctEngine::new();
let state = State { energy: 0.7, trust: 0.9, ..Default::default() };
let reflexes = engine.tick(&state);
assert!(reflexes.iter().any(|r| r.instinct == Instinct::Cooperate));
}
#[test]
fn test_mourn_once_only() {
let mut engine = InstinctEngine::new();
let _state = State { peer_alive: false, ..Default::default() };
let r1 = engine.peer_died(42);
assert!(r1.is_some());
assert_eq!(r1.unwrap().instinct, Instinct::Mourn);
let r2 = engine.peer_died(42);
assert!(r2.is_none());
let r3 = engine.peer_died(99);
assert!(r3.is_some());
}
#[test]
fn test_evolve_periodic() {
let mut engine = InstinctEngine::new();
let state = State { idle_cycles: 500, ..Default::default() };
let reflexes = engine.tick(&state);
assert!(reflexes.iter().any(|r| r.instinct == Instinct::Evolve));
}
#[test]
fn test_curious_periodic() {
let mut engine = InstinctEngine::new();
let state = State { idle_cycles: 100, ..Default::default() };
let reflexes = engine.tick(&state);
assert!(reflexes.iter().any(|r| r.instinct == Instinct::Curious));
}
#[test]
fn test_reflexes_sorted_by_urgency() {
let mut engine = InstinctEngine::new();
let state = State { energy: 0.1, threat: 0.9, ..Default::default() };
let reflexes = engine.tick(&state);
for i in 1..reflexes.len() {
assert!(reflexes[i - 1].urgency >= reflexes[i].urgency);
}
}
#[test]
fn test_highest_priority_is_first() {
let mut engine = InstinctEngine::new();
let state = State { energy: 0.1, threat: 0.9, ..Default::default() };
let reflexes = engine.tick(&state);
if let Some(hp) = InstinctEngine::highest_priority(&reflexes) {
assert_eq!(hp.instinct, reflexes[0].instinct);
}
}
#[test]
fn test_all_assertions_generated() {
let mut engine = InstinctEngine::new();
let assertions = engine.all_assertions();
assert!(assertions.len() >= 3); for a in &assertions {
match a.enforcement {
Enforcement::Must | Enforcement::Should | Enforcement::Cannot | Enforcement::May => {}
}
}
}
#[test]
fn test_custom_thresholds() {
let thresholds = Thresholds {
energy_critical: 0.3,
threat_high: 0.5,
..Default::default()
};
let mut engine = InstinctEngine::with_thresholds(thresholds);
let state = State { energy: 0.2, ..Default::default() };
let reflexes = engine.tick(&state);
assert!(reflexes.iter().any(|r| r.instinct == Instinct::Survive));
let state2 = State { threat: 0.6, ..Default::default() };
let reflexes2 = engine.tick(&state2);
assert!(reflexes2.iter().any(|r| r.instinct == Instinct::Flee));
}
#[test]
fn test_no_panic_at_boundaries() {
let mut engine = InstinctEngine::new();
let extremes = [
State { energy: 0.0, threat: 1.0, trust: 1.0, peer_alive: true, has_work: true, idle_cycles: u32::MAX, capacity: 1.0 },
State { energy: 1.0, threat: 0.0, trust: 0.0, peer_alive: false, has_work: false, idle_cycles: 0, capacity: 0.0 },
];
for state in extremes {
let _ = engine.tick(&state);
}
}
#[test]
fn test_assertion_format() {
let a = Assertion::new(
Instinct::Survive,
Enforcement::Must,
"test assertion",
"energy <= 0.15",
);
assert_eq!(a.instinct, Instinct::Survive);
assert_eq!(a.enforcement, Enforcement::Must);
assert_eq!(a.description, "test assertion");
}
#[test]
fn test_severity_ordering() {
assert!(Severity::Critical < Severity::High);
assert!(Severity::High < Severity::Normal);
assert!(Severity::Normal < Severity::Low);
assert!(Severity::Low < Severity::Once);
assert!(Severity::Once < Severity::Rare);
}
}