1use std::collections::HashMap;
5use std::fs;
6use std::path::Path;
7
8#[derive(Clone, Debug)]
9pub struct ImplicitPattern {
10 pub pattern: Vec<u32>, pub strength: f32, pub frequency: u32, pub last_activation_ms: u64,
14 pub performance_metric: f32, }
16
17#[derive(Clone, Debug)]
18pub struct SkillNode {
19 pub name: String,
20 pub mastery_level: f32, pub error_rate: f32, pub last_practiced_ms: u64,
23 pub practice_count: u32,
24}
25
26pub struct ImplicitMemory {
27 pub patterns: HashMap<u64, ImplicitPattern>, pub skills: HashMap<String, SkillNode>,
29 pub habits: Vec<(String, f32)>, pub conditioning: HashMap<String, f32>, }
32
33impl Default for ImplicitMemory {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl ImplicitMemory {
40 pub fn new() -> Self {
41 Self {
42 patterns: HashMap::new(),
43 skills: HashMap::new(),
44 habits: Vec::new(),
45 conditioning: HashMap::new(),
46 }
47 }
48
49 pub fn learn_pattern(&mut self, pattern: Vec<u32>, success: bool) {
51 let hash = Self::hash_pattern(&pattern);
52 let strength_delta = if success { 0.1 } else { -0.05 };
53
54 self.patterns
55 .entry(hash)
56 .and_modify(|p| {
57 p.strength = (p.strength + strength_delta).clamp(0.0, 1.0);
58 p.frequency += 1;
59 p.performance_metric =
60 p.performance_metric * 0.8 + if success { 1.0 } else { 0.0 } * 0.2;
61 p.last_activation_ms = Self::now_ms();
62 })
63 .or_insert_with(|| ImplicitPattern {
64 pattern: pattern.clone(),
65 strength: 0.5,
66 frequency: 1,
67 last_activation_ms: Self::now_ms(),
68 performance_metric: if success { 1.0 } else { 0.0 },
69 });
70 }
71
72 pub fn practice_skill(&mut self, skill: &str, error_occurred: bool) {
74 self.skills
75 .entry(skill.to_string())
76 .and_modify(|s| {
77 s.practice_count += 1;
78 let improvement = 0.02;
79 if !error_occurred {
80 s.mastery_level = (s.mastery_level + improvement).min(1.0);
81 s.error_rate = (s.error_rate * 0.95).max(0.0);
82 } else {
83 s.error_rate = (s.error_rate + 0.05).min(1.0);
84 }
85 s.last_practiced_ms = Self::now_ms();
86 })
87 .or_insert_with(|| SkillNode {
88 name: skill.to_string(),
89 mastery_level: if !error_occurred { 0.2 } else { 0.05 },
90 error_rate: if error_occurred { 0.5 } else { 0.1 },
91 last_practiced_ms: Self::now_ms(),
92 practice_count: 1,
93 });
94 }
95
96 pub fn form_habit(&mut self, trigger: &str, strength_delta: f32) {
98 let pos = self.habits.iter().position(|(t, _)| t == trigger);
99 if let Some(idx) = pos {
100 let new_strength = (self.habits[idx].1 + strength_delta).clamp(0.0, 1.0);
101 self.habits[idx].1 = new_strength;
102 } else {
103 self.habits
104 .push((trigger.to_string(), strength_delta.clamp(0.0, 1.0)));
105 }
106 }
107
108 pub fn condition_response(&mut self, stimulus: &str, response_strength: f32) {
110 self.conditioning
111 .entry(stimulus.to_string())
112 .and_modify(|s| *s = (*s * 0.7 + response_strength * 0.3).clamp(0.0, 1.0))
113 .or_insert(response_strength.clamp(0.0, 1.0));
114 }
115
116 pub fn strongest_patterns(&self, k: usize) -> Vec<(u64, ImplicitPattern)> {
118 let mut sorted: Vec<_> = self.patterns.iter().map(|(h, p)| (*h, p.clone())).collect();
119 sorted.sort_by(|a, b| b.1.strength.total_cmp(&a.1.strength));
120 sorted.into_iter().take(k).collect()
121 }
122
123 pub fn skill_ranking(&self) -> Vec<(String, SkillNode)> {
125 let mut sorted: Vec<_> = self
126 .skills
127 .iter()
128 .map(|(n, s)| (n.clone(), s.clone()))
129 .collect();
130 sorted.sort_by(|a, b| b.1.mastery_level.total_cmp(&a.1.mastery_level));
131 sorted
132 }
133
134 pub fn decay(&mut self) {
136 let now = Self::now_ms();
137 const DECAY_THRESHOLD_MS: u64 = 604_800_000; self.patterns.retain(|_, p| {
141 let age = now.saturating_sub(p.last_activation_ms);
142 if age > DECAY_THRESHOLD_MS {
143 p.strength *= 0.5; }
145 p.strength > 0.05 });
147
148 self.skills.retain(|_, s| {
150 let age = now.saturating_sub(s.last_practiced_ms);
151 if age > DECAY_THRESHOLD_MS {
152 s.mastery_level *= 0.7;
153 }
154 s.mastery_level > 0.05
155 });
156
157 self.habits.retain(|(_, strength)| *strength > 0.05);
159 }
160
161 fn hash_pattern(pattern: &[u32]) -> u64 {
162 let mut hash: u64 = 0xcbf29ce484222325;
163 for &val in pattern {
164 hash = hash.wrapping_mul(0x100000001b3) ^ (val as u64);
165 }
166 hash
167 }
168
169 fn now_ms() -> u64 {
170 std::time::SystemTime::now()
171 .duration_since(std::time::UNIX_EPOCH)
172 .unwrap_or_default()
173 .as_millis() as u64
174 }
175
176 pub fn save(&self, dir: &Path) -> Result<(), String> {
177 let path = dir.join("implicit_memory.bin");
178 let mut data = Vec::new();
179
180 data.extend_from_slice(b"IMPL");
182 data.push(1);
183
184 let pattern_count = self.patterns.len() as u32;
186 data.extend_from_slice(&pattern_count.to_le_bytes());
187 for (hash, pattern) in &self.patterns {
188 data.extend_from_slice(&hash.to_le_bytes());
189 data.extend_from_slice(&pattern.strength.to_le_bytes());
190 data.extend_from_slice(&pattern.frequency.to_le_bytes());
191 data.extend_from_slice(&pattern.last_activation_ms.to_le_bytes());
192 data.extend_from_slice(&pattern.performance_metric.to_le_bytes());
193
194 let seq_len = pattern.pattern.len() as u16;
195 data.extend_from_slice(&seq_len.to_le_bytes());
196 for &val in &pattern.pattern {
197 data.extend_from_slice(&val.to_le_bytes());
198 }
199 }
200
201 let skill_count = self.skills.len() as u32;
203 data.extend_from_slice(&skill_count.to_le_bytes());
204 for (name, skill) in &self.skills {
205 let name_bytes = name.as_bytes();
206 data.push(name_bytes.len() as u8);
207 data.extend_from_slice(name_bytes);
208 data.extend_from_slice(&skill.mastery_level.to_le_bytes());
209 data.extend_from_slice(&skill.error_rate.to_le_bytes());
210 data.extend_from_slice(&skill.last_practiced_ms.to_le_bytes());
211 data.extend_from_slice(&skill.practice_count.to_le_bytes());
212 }
213
214 let tmp_path = dir.join("implicit_memory.bin.tmp");
215 fs::write(&tmp_path, data).map_err(|e| e.to_string())?;
216 fs::rename(&tmp_path, &path).map_err(|e| e.to_string())
217 }
218
219 pub fn load(dir: &Path) -> Result<Self, String> {
220 let path = dir.join("implicit_memory.bin");
221 if !path.exists() {
222 return Ok(Self::new());
223 }
224
225 let data = fs::read(&path).map_err(|e| e.to_string())?;
226 if data.len() < 9 || &data[0..4] != b"IMPL" {
227 return Ok(Self::new());
228 }
229
230 let mut idx = 5;
231 let mut patterns = HashMap::new();
232 let mut skills = HashMap::new();
233
234 if idx + 4 <= data.len() {
236 let pattern_count =
237 u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]])
238 as usize;
239 idx += 4;
240
241 for _ in 0..pattern_count {
242 if idx + 32 > data.len() {
243 break;
244 }
245
246 let hash = u64::from_le_bytes([
247 data[idx],
248 data[idx + 1],
249 data[idx + 2],
250 data[idx + 3],
251 data[idx + 4],
252 data[idx + 5],
253 data[idx + 6],
254 data[idx + 7],
255 ]);
256 idx += 8;
257
258 let strength =
259 f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
260 idx += 4;
261
262 let frequency =
263 u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
264 idx += 4;
265
266 let last_activation_ms = u64::from_le_bytes([
267 data[idx],
268 data[idx + 1],
269 data[idx + 2],
270 data[idx + 3],
271 data[idx + 4],
272 data[idx + 5],
273 data[idx + 6],
274 data[idx + 7],
275 ]);
276 idx += 8;
277
278 let performance_metric =
279 f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
280 idx += 4;
281
282 if idx + 2 <= data.len() {
283 let seq_len = u16::from_le_bytes([data[idx], data[idx + 1]]) as usize;
284 idx += 2;
285
286 let mut pattern = Vec::new();
287 for _ in 0..seq_len {
288 if idx + 4 > data.len() {
289 break;
290 }
291 let val = u32::from_le_bytes([
292 data[idx],
293 data[idx + 1],
294 data[idx + 2],
295 data[idx + 3],
296 ]);
297 pattern.push(val);
298 idx += 4;
299 }
300
301 patterns.insert(
302 hash,
303 ImplicitPattern {
304 pattern,
305 strength,
306 frequency,
307 last_activation_ms,
308 performance_metric,
309 },
310 );
311 }
312 }
313 }
314
315 if idx + 4 <= data.len() {
317 let skill_count =
318 u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]])
319 as usize;
320 idx += 4;
321
322 for _ in 0..skill_count {
323 if idx >= data.len() {
324 break;
325 }
326 let name_len = data[idx] as usize;
327 idx += 1;
328
329 if idx + name_len > data.len() {
330 break;
331 }
332 let name = String::from_utf8_lossy(&data[idx..idx + name_len]).to_string();
333 idx += name_len;
334
335 if idx + 20 > data.len() {
336 break;
337 }
338 let mastery_level =
339 f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
340 idx += 4;
341
342 let error_rate =
343 f32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
344 idx += 4;
345
346 let last_practiced_ms = u64::from_le_bytes([
347 data[idx],
348 data[idx + 1],
349 data[idx + 2],
350 data[idx + 3],
351 data[idx + 4],
352 data[idx + 5],
353 data[idx + 6],
354 data[idx + 7],
355 ]);
356 idx += 8;
357
358 let practice_count =
359 u32::from_le_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]);
360 idx += 4;
361
362 skills.insert(
363 name.clone(),
364 SkillNode {
365 name,
366 mastery_level,
367 error_rate,
368 last_practiced_ms,
369 practice_count,
370 },
371 );
372 }
373 }
374
375 Ok(Self {
376 patterns,
377 skills,
378 habits: Vec::new(),
379 conditioning: HashMap::new(),
380 })
381 }
382
383 pub fn load_or_init(dir: &Path) -> Self {
384 Self::load(dir).unwrap_or_else(|_| Self::new())
385 }
386}