use serde::{Deserialize, Serialize};
const DOPAMINE_DECAY: f32 = 0.95;
const CORTISOL_DECAY: f32 = 0.90;
const ACETYLCHOLINE_DECAY: f32 = 0.99;
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct NeuroModulators {
pub dopamine: f32,
pub cortisol: f32,
pub acetylcholine: f32,
pub tempo: f32,
pub mining_dopamine: f32, }
impl Default for NeuroModulators {
fn default() -> Self {
Self {
dopamine: 0.0,
cortisol: 0.0,
acetylcholine: 0.0,
tempo: 0.0,
mining_dopamine: 0.0, }
}
}
impl NeuroModulators {
pub fn from_telemetry(gpu_temp: f32, power_w: f32, hashrate_mh: f32, gpu_clock_mhz: f32) -> Self {
let dopamine = (hashrate_mh / 0.0105).clamp(0.3, 1.0);
let heat_stress: f32 = if gpu_temp > 1.0 {
((gpu_temp - 83.0) / 10.0).clamp(0.0, 1.0)
} else {
0.0
};
let power_stress = ((power_w - 400.0) / 50.0).clamp(0.0, 1.0);
let cortisol = heat_stress.max(power_stress).max(0.0);
let vddcr_dev = (1.05_f32 - 1.0_f32).abs(); let acetylcholine = (1.0_f32 - vddcr_dev * 2.0_f32).clamp(0.4_f32, 1.0_f32);
let tempo = (gpu_clock_mhz / 2640.0).clamp(0.5, 2.0);
Self {
dopamine,
cortisol,
acetylcholine,
tempo,
mining_dopamine: 0.0, }
}
pub fn decay(&mut self) {
self.dopamine = (self.dopamine * DOPAMINE_DECAY).max(0.0);
self.cortisol = (self.cortisol * CORTISOL_DECAY).max(0.0);
self.acetylcholine = (self.acetylcholine * ACETYLCHOLINE_DECAY).max(0.0);
self.mining_dopamine = (self.mining_dopamine * DOPAMINE_DECAY).max(0.0);
}
pub fn add_reward(&mut self, amount: f32) {
self.dopamine = (self.dopamine + amount).min(1.0);
}
pub fn add_stress(&mut self, amount: f32) {
self.cortisol = (self.cortisol + amount).min(1.0);
}
pub fn boost_focus(&mut self, amount: f32) {
self.acetylcholine = (self.acetylcholine + amount).min(1.0);
}
pub fn set_tempo(&mut self, tempo: f32) {
self.tempo = tempo.clamp(0.0, 2.0);
}
pub fn add_mining_reward(&mut self, amount: f32) {
self.mining_dopamine = (self.mining_dopamine + amount).min(1.0);
}
pub fn is_stressed(&self) -> bool {
self.cortisol > 0.7
}
pub fn is_rewarded(&self) -> bool {
self.dopamine >= 0.5
}
pub fn is_focused(&self) -> bool {
self.acetylcholine > 0.6
}
pub fn is_mining_rewarded(&self) -> bool {
self.mining_dopamine > 0.3
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_modulators_default() {
let mods = NeuroModulators::default();
assert_eq!(mods.dopamine, 0.0);
assert_eq!(mods.cortisol, 0.0);
assert_eq!(mods.acetylcholine, 0.0);
assert_eq!(mods.tempo, 0.0);
}
#[test]
fn test_from_telemetry() {
let mods = NeuroModulators::from_telemetry(75.0, 300.0, 0.05, 2640.0);
assert!(mods.dopamine > 0.0);
assert!(mods.acetylcholine > 0.0);
assert_eq!(mods.tempo, 1.0);
}
#[test]
fn test_decay() {
let mut mods = NeuroModulators::default();
mods.dopamine = 1.0;
mods.cortisol = 1.0;
mods.acetylcholine = 1.0;
mods.decay();
assert!(mods.dopamine < 1.0);
assert!(mods.cortisol < 1.0);
assert!(mods.acetylcholine < 1.0);
}
#[test]
fn test_reward_and_stress() {
let mut mods = NeuroModulators::default();
mods.add_reward(0.5);
assert_eq!(mods.dopamine, 0.5);
assert!(mods.is_rewarded());
mods.add_stress(0.8);
assert_eq!(mods.cortisol, 0.8);
assert!(mods.is_stressed());
mods.boost_focus(0.7);
assert_eq!(mods.acetylcholine, 0.7);
assert!(mods.is_focused());
}
#[test]
fn test_clamping() {
let mut mods = NeuroModulators::default();
mods.add_reward(2.0);
assert_eq!(mods.dopamine, 1.0);
mods.add_stress(2.0);
assert_eq!(mods.cortisol, 1.0);
mods.boost_focus(2.0);
assert_eq!(mods.acetylcholine, 1.0);
mods.set_tempo(3.0);
assert_eq!(mods.tempo, 2.0);
mods.set_tempo(-1.0);
assert_eq!(mods.tempo, 0.0);
}
}