You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

75 lines
2.2 KiB

// minimal ripoff of tracery by kate compton (tracery.io)
use std::collections::BTreeMap;
use macroquad::rand::*;
pub struct Grammar {
pub grammar: BTreeMap<String, Vec<String>>,
}
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<Item = (String, Vec<String>)>) -> 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<String> {
self.flatten_with_rule(&"origin")
}
pub fn flatten_with_rule(&self, rule: &str) -> Option<String> {
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(())
}
}