// minimal ripoff of tracery by kate compton (tracery.io) use std::collections::BTreeMap; use macroquad::rand::*; pub struct Grammar { pub grammar: BTreeMap>, } impl Grammar { pub fn new() -> Self { let mut grammar = BTreeMap::new(); grammar.insert(String::from("origin"), vec![]); Self { grammar } } pub fn from(rules: impl IntoIterator)>) -> Self { let mut grammar = BTreeMap::new(); for (token, rule) in rules { grammar.insert(token, rule); } Self { grammar } } pub fn insert(&mut self, key: String, val: String) { let entry = self.grammar.entry(key).or_insert_with(Vec::new); entry.push(val); } pub fn flatten(&self) -> Option { self.flatten_with_rule(&"origin") } pub fn flatten_with_rule(&self, rule: &str) -> Option { let rule = self.grammar.get(rule)?; if rule.is_empty() { return None; } let idx = gen_range(0, rule.len()); let rule = &rule[idx]; let mut output = String::from(""); for (i, piece) in rule.split('#').enumerate() { if i % 2 == 0 { output += piece; } else { output += &self .flatten_with_rule(&piece) .unwrap_or_else(|| String::from(piece)); } } Some(output) } } #[cfg(test)] mod test { use super::*; use macroquad::rand::srand; #[test] fn test_grammar() -> Result<(), ()> { use std::time::*; let now = Instant::now(); std::thread::sleep(Duration::from_millis(150)); srand(now.elapsed().as_secs_f64().to_bits()); let mut grammar = Grammar::new(); grammar.insert("origin".into(), "egg".into()); grammar.insert("origin".into(), "sandwich".into()); grammar.insert("origin".into(), "#sandwich#".into()); grammar.insert("sandwich".into(), "i love sandwiches".into()); let output = grammar.flatten().ok_or(())?; assert!(output == "egg" || output == "sandwich" || output == "i love sandwiches"); Ok(()) } }