The world's most clever kitty cat

Clean Up

bwc9876.dev d72b857f fdcc15bc

verified
+50 -45
+50 -45
src/brain.rs
··· 104 104 (FORCE_REPLIES) || roll <= chance 105 105 } 106 106 107 - fn extract_final_token(msg: &str) -> Option<Token> { 107 + fn extract_final_word(msg: &str) -> Option<String> { 108 108 msg.split_ascii_whitespace() 109 109 .last() 110 - .map(Self::normalize_token) 110 + .and_then(Self::normalize_token) 111 111 } 112 112 113 - fn random_token(&self, rand: &mut fastrand::Rng) -> Option<Token> { 113 + fn random_token(&self, rand: &mut fastrand::Rng) -> Option<&Token> { 114 114 let len = self.0.len(); 115 115 if len == 0 { 116 116 None 117 117 } else { 118 118 let i = rand.usize(..len); 119 - self.0.keys().nth(i).cloned() 119 + self.0.keys().nth(i) 120 120 } 121 121 } 122 122 123 123 pub fn ingest(&mut self, msg: &str) -> bool { 124 - let mut learned_new_word = false; 125 - // This is a silly way to do windows rust ppl :sob: 126 - let _ = Self::parse(msg) 124 + // Using reduce instead of .any here to prevent short circuting 125 + Self::parse(msg) 127 126 .map_windows(|[from, to]| { 128 127 if let Some(edge) = self.0.get_mut(from) { 129 128 edge.increment_token(to); 129 + false 130 130 } else { 131 131 let new = Edges(HashMap::from_iter([(to.clone(), 1)]), 1); 132 132 self.0.insert(from.clone(), new); 133 - learned_new_word = true; 133 + true 134 134 } 135 135 }) 136 - .collect::<Vec<_>>(); 137 - 138 - learned_new_word 136 + .reduce(|acc, c| acc || c) 137 + .unwrap_or_default() 139 138 } 140 139 141 140 pub fn merge_from(&mut self, other: Self) { ··· 148 147 } 149 148 } 150 149 150 + fn next_from(&self, tok: &Token, rand: &mut fastrand::Rng, allow_end: bool) -> Option<&Token> { 151 + // Get the edges for the current token 152 + // If we have that token, sample its edges 153 + // Otherwise, if we don't know that token, and allow_end is false, try to pick a random token instead 154 + self.0 155 + .get(tok) 156 + .and_then(|edges| edges.sample(rand, allow_end)) 157 + .or_else(|| { 158 + if allow_end { 159 + None 160 + } else { 161 + self.random_token(rand) 162 + } 163 + }) 164 + } 165 + 151 166 pub fn respond( 152 167 &self, 153 168 msg: &str, ··· 165 180 return None; 166 181 } 167 182 168 - // Get our final token, or a random one if the message has nothing, or don't reply at all 169 - // if we have no tokens at all. 170 - let last_token = Self::extract_final_token(msg).or_else(|| self.random_token(&mut rng))?; 171 - let mut current_token = &last_token; 183 + // Get the final token 184 + let last_token = Self::extract_final_word(msg); 185 + 186 + let mut current_token = if let Some(t) = last_token { 187 + // We found a word at the end of the previous message 188 + &Some(t) 189 + } else { 190 + // We couldn't find a word at the end of the last message, pick a random one 191 + // If we *still* don't have a token, return early 192 + self.random_token(&mut rng)? 193 + }; 172 194 173 195 let mut chain = Vec::with_capacity(MAX_TOKENS); 174 - let mut has_triggered_typing = false; 175 - 176 - while current_token.is_some() && chain.len() <= MAX_TOKENS { 177 - if let Some(edges) = self.0.get(current_token) { 178 - let next = edges.sample(&mut rng, chain.len() > 2); 179 196 180 - if let Some(ref tok) = next { 181 - if let Some(s) = tok { 182 - // Is this a non-ending token? If so, push it to our chain! 183 - chain.push(s.clone()); 184 - if !has_triggered_typing && let Some(typ) = typing_oneshot.take() { 185 - typ.send(true).ok(); 186 - } 187 - current_token = tok; 188 - } else { 189 - // If we reached an end token, stop chaining 190 - break; 191 - } 192 - } else { 193 - // If we failed to sample any tokens, we can't continue the chain 194 - break; 195 - } 196 - } else { 197 - // If we don't know the current word, we can't continue the chain 198 - break; 197 + while let Some(next @ Some(s)) = self.next_from(current_token, &mut rng, !chain.is_empty()) 198 + && chain.len() <= MAX_TOKENS 199 + { 200 + chain.push(s.clone()); 201 + if let Some(typ) = typing_oneshot.take() { 202 + typ.send(true).ok(); 199 203 } 204 + current_token = next; 200 205 } 201 206 202 207 if let Some(typ) = typing_oneshot.take() { ··· 308 313 } 309 314 310 315 #[test] 311 - fn at_least_2_tokens() { 316 + fn at_least_1_token() { 312 317 let mut brain = Brain::default(); 313 318 brain.ingest("hello world"); 314 - brain.ingest("hello"); 315 - brain.ingest("hello"); 316 - brain.ingest("hello"); 319 + for _ in 0..100 { 320 + brain.ingest("hello"); 321 + } 317 322 318 323 for _ in 0..100 { 319 324 // I'm too lazy to mock lazyrand LOL!! ··· 331 336 } 332 337 333 338 #[test] 334 - fn none_on_end() { 339 + fn random_on_end() { 335 340 let mut brain = Brain::default(); 336 341 brain.ingest("world hello"); 337 342 338 343 let reply = brain.respond("hello", false, false, None); 339 - assert_eq!(reply, None); 344 + assert!(reply.is_some()); 340 345 } 341 346 342 347 #[test]