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
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(())
|
|
}
|
|
}
|