use std::collections::HashMap;
use std::fs;
use std::path::Path;
#[derive(Clone, Debug)]
pub struct ImplicitPattern {
pub pattern: Vec<u32>, pub strength: f32, pub frequency: u32, pub last_activation_ms: u64,
pub performance_metric: f32, }
#[derive(Clone, Debug)]
pub struct SkillNode {
pub name: String,
pub mastery_level: f32, pub error_rate: f32, pub last_practiced_ms: u64,
pub practice_count: u32,
}
pub struct ImplicitMemory {
pub patterns: HashMap<u64, ImplicitPattern>, pub skills: HashMap<String, SkillNode>,
pub habits: Vec<(String, f32)>, pub conditioning: HashMap<String, f32>, }
impl Default for ImplicitMemory {
fn default() -> Self {
Self::new()
}
}
impl ImplicitMemory {
pub fn new() -> Self {
Self {
patterns: HashMap::new(),
skills: HashMap::new(),
habits: Vec::new(),
conditioning: HashMap::new(),
}
}
pub fn learn_pattern(&mut self, pattern: Vec<u32>, success: bool) {
let hash = Self::hash_pattern(&pattern);
let strength_delta = if success { 0.1 } else { -0.05 };
self.patterns
.entry(hash)
.and_modify(|p| {
p.strength = (p.strength + strength_delta).clamp(0.0, 1.0);
p.frequency += 1;
p.performance_metric =
p.performance_metric * 0.8 + if success { 1.0 } else { 0.0 } * 0.2;
p.last_activation_ms = Self::now_ms();
})
.or_insert_with(|| ImplicitPattern {
pattern: pattern.clone(),
strength: 0.5,
frequency: 1,
last_activation_ms: Self::now_ms(),
performance_metric: if success { 1.0 } else { 0.0 },
});
}
pub fn practice_skill(&mut self, skill: &str, error_occurred: bool) {
self.skills
.entry(skill.to_string())
.and_modify(|s| {
s.practice_count += 1;
let improvement = 0.02;
if !error_occurred {
s.mastery_level = (s.mastery_level + improvement).min(1.0);
s.error_rate = (s.error_rate * 0.95).max(0.0);
} else {
s.error_rate = (s.error_rate + 0.05).min(1.0);
}
s.last_practiced_ms = Self::now_ms();
})
.or_insert_with(|| SkillNode {
name: skill.to_string(),
mastery_level: if !error_occurred { 0.2 } else { 0.05 },
error_rate: if error_occurred { 0.5 } else { 0.1 },
last_practiced_ms: Self::now_ms(),
practice_count: 1,
});
}
pub fn form_habit(&mut self, trigger: &str, strength_delta: f32) {
let pos = self.habits.iter().position(|(t, _)| t == trigger);
if let Some(idx) = pos {
let new_strength = (self.habits[idx].1 + strength_delta).clamp(0.0, 1.0);
self.habits[idx].1 = new_strength;
} else {
self.habits
.push((trigger.to_string(), strength_delta.clamp(0.0, 1.0)));
}
}
pub fn condition_response(&mut self, stimulus: &str, response_strength: f32) {
self.conditioning
.entry(stimulus.to_string())
.and_modify(|s| *s = (*s * 0.7 + response_strength * 0.3).clamp(0.0, 1.0))
.or_insert(response_strength.clamp(0.0, 1.0));
}
pub fn strongest_patterns(&self, k: usize) -> Vec<(u64, ImplicitPattern)> {
let mut sorted: Vec<_> = self.patterns.iter().map(|(h, p)| (*h, p.clone())).collect();
sorted.sort_by(|a, b| b.1.strength.total_cmp(&a.1.strength));
sorted.into_iter().take(k).collect()
}
pub fn skill_ranking(&self) -> Vec<(String, SkillNode)> {
let mut sorted: Vec<_> = self
.skills
.iter()
.map(|(n, s)| (n.clone(), s.clone()))
.collect();
sorted.sort_by(|a, b| b.1.mastery_level.total_cmp(&a.1.mastery_level));
sorted
}
pub fn decay(&mut self) {
let now = Self::now_ms();
const DECAY_THRESHOLD_MS: u64 = 604_800_000;
self.patterns.retain(|_, p| {
let age = now.saturating_sub(p.last_activation_ms);
if age > DECAY_THRESHOLD_MS {
p.strength *= 0.5; }
p.strength > 0.05 });
self.skills.retain(|_, s| {
let age = now.saturating_sub(s.last_practiced_ms);
if age > DECAY_THRESHOLD_MS {
s.mastery_level *= 0.7;
}
s.mastery_level > 0.05
});
self.habits.retain(|(_, strength)| *strength > 0.05);
}
fn hash_pattern(pattern: &[u32]) -> u64 {
let mut hash: u64 = 0xcbf29ce484222325;
for &val in pattern {
hash = hash.wrapping_mul(0x100000001b3) ^ (val as u64);
}
hash
}
fn now_ms() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64
}
pub fn save(&self, dir: &Path) -> Result<(), String> {
let path = dir.join("implicit_memory.bin");
let mut data = Vec::new();
data.extend_from_slice(b"IMPL");
data.push(1);
let pattern_count = self.patterns.len() as u32;
data.extend_from_slice(&pattern_count.to_le_bytes());
for (hash, pattern) in &self.patterns {
data.extend_from_slice(&hash.to_le_bytes());
data.extend_from_slice(&pattern.strength.to_le_bytes());
data.extend_from_slice(&pattern.frequency.to_le_bytes());
data.extend_from_slice(&pattern.last_activation_ms.to_le_bytes());
data.extend_from_slice(&pattern.performance_metric.to_le_bytes());
let seq_len = pattern.pattern.len() as u16;
data.extend_from_slice(&seq_len.to_le_bytes());
for &val in &pattern.pattern {
data.extend_from_slice(&val.to_le_bytes());
}
}
let skill_count = self.skills.len() as u32;
data.extend_from_slice(&skill_count.to_le_bytes());
for (name, skill) in &self.skills {
let name_bytes = name.as_bytes();
data.push(name_bytes.len() as u8);
data.extend_from_slice(name_bytes);
data.extend_from_slice(&skill.mastery_level.to_le_bytes());
data.extend_from_slice(&skill.error_rate.to_le_bytes());
data.extend_from_slice(&skill.last_practiced_ms.to_le_bytes());
data.extend_from_slice(&skill.practice_count.to_le_bytes());
}
let tmp_path = dir.join("implicit_memory.bin.tmp");
fs::write(&tmp_path, data).map_err(|e| e.to_string())?;
fs::rename(&tmp_path, &path).map_err(|e| e.to_string())
}
pub fn load(dir: &Path) -> Result<Self, String> {
let path = dir.join("implicit_memory.bin");
if !path.exists() {
return Ok(Self::new());
}
let data = fs::read(&path).map_err(|e| e.to_string())?;
if data.len() < 9 || &data[0..4] != b"IMPL" {
return Ok(Self::new());
}
let mut idx = 5;
let mut patterns = HashMap::new();
let mut skills = HashMap::new();
if idx + 4 <= data.len() {
let pattern_count =
u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]])
as usize;
idx += 4;
for _ in 0..pattern_count {
if idx + 32 > data.len() {
break;
}
let hash = u64::from_le_bytes([
data[idx],
data[idx + 1],
data[idx + 2],
data[idx + 3],
data[idx + 4],
data[idx + 5],
data[idx + 6],
data[idx + 7],
]);
idx += 8;
let strength =
f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
idx += 4;
let frequency =
u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
idx += 4;
let last_activation_ms = u64::from_le_bytes([
data[idx],
data[idx + 1],
data[idx + 2],
data[idx + 3],
data[idx + 4],
data[idx + 5],
data[idx + 6],
data[idx + 7],
]);
idx += 8;
let performance_metric =
f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
idx += 4;
if idx + 2 <= data.len() {
let seq_len = u16::from_le_bytes([data[idx], data[idx + 1]]) as usize;
idx += 2;
let mut pattern = Vec::new();
for _ in 0..seq_len {
if idx + 4 > data.len() {
break;
}
let val = u32::from_le_bytes([
data[idx],
data[idx + 1],
data[idx + 2],
data[idx + 3],
]);
pattern.push(val);
idx += 4;
}
patterns.insert(
hash,
ImplicitPattern {
pattern,
strength,
frequency,
last_activation_ms,
performance_metric,
},
);
}
}
}
if idx + 4 <= data.len() {
let skill_count =
u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]])
as usize;
idx += 4;
for _ in 0..skill_count {
if idx >= data.len() {
break;
}
let name_len = data[idx] as usize;
idx += 1;
if idx + name_len > data.len() {
break;
}
let name = String::from_utf8_lossy(&data[idx..idx + name_len]).to_string();
idx += name_len;
if idx + 20 > data.len() {
break;
}
let mastery_level =
f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
idx += 4;
let error_rate =
f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
idx += 4;
let last_practiced_ms = u64::from_le_bytes([
data[idx],
data[idx + 1],
data[idx + 2],
data[idx + 3],
data[idx + 4],
data[idx + 5],
data[idx + 6],
data[idx + 7],
]);
idx += 8;
let practice_count =
u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
idx += 4;
skills.insert(
name.clone(),
SkillNode {
name,
mastery_level,
error_rate,
last_practiced_ms,
practice_count,
},
);
}
}
Ok(Self {
patterns,
skills,
habits: Vec::new(),
conditioning: HashMap::new(),
})
}
pub fn load_or_init(dir: &Path) -> Self {
Self::load(dir).unwrap_or_else(|_| Self::new())
}
}