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

3 years ago
  1. // minimal ripoff of tracery by kate compton (tracery.io)
  2. use std::collections::BTreeMap;
  3. use macroquad::rand::*;
  4. pub struct Grammar {
  5. pub grammar: BTreeMap<String, Vec<String>>,
  6. }
  7. impl Grammar {
  8. pub fn new() -> Self {
  9. let mut grammar = BTreeMap::new();
  10. grammar.insert(String::from("origin"), vec![]);
  11. Self { grammar }
  12. }
  13. pub fn from(rules: impl IntoIterator<Item = (String, Vec<String>)>) -> Self {
  14. let mut grammar = BTreeMap::new();
  15. for (token, rule) in rules {
  16. grammar.insert(token, rule);
  17. }
  18. Self { grammar }
  19. }
  20. pub fn insert(&mut self, key: String, val: String) {
  21. let entry = self.grammar.entry(key).or_insert_with(Vec::new);
  22. entry.push(val);
  23. }
  24. pub fn flatten(&self) -> Option<String> {
  25. self.flatten_with_rule(&"origin")
  26. }
  27. pub fn flatten_with_rule(&self, rule: &str) -> Option<String> {
  28. let rule = self.grammar.get(rule)?;
  29. if rule.is_empty() {
  30. return None;
  31. }
  32. let idx = gen_range(0, rule.len());
  33. let rule = &rule[idx];
  34. let mut output = String::from("");
  35. for (i, piece) in rule.split('#').enumerate() {
  36. if i % 2 == 0 {
  37. output += piece;
  38. } else {
  39. output += &self
  40. .flatten_with_rule(&piece)
  41. .unwrap_or_else(|| String::from(piece));
  42. }
  43. }
  44. Some(output)
  45. }
  46. }
  47. #[cfg(test)]
  48. mod test {
  49. use super::*;
  50. use macroquad::rand::srand;
  51. #[test]
  52. fn test_grammar() -> Result<(), ()> {
  53. use std::time::*;
  54. let now = Instant::now();
  55. std::thread::sleep(Duration::from_millis(150));
  56. srand(now.elapsed().as_secs_f64().to_bits());
  57. let mut grammar = Grammar::new();
  58. grammar.insert("origin".into(), "egg".into());
  59. grammar.insert("origin".into(), "sandwich".into());
  60. grammar.insert("origin".into(), "#sandwich#".into());
  61. grammar.insert("sandwich".into(), "i love sandwiches".into());
  62. let output = grammar.flatten().ok_or(())?;
  63. assert!(output == "egg" || output == "sandwich" || output == "i love sandwiches");
  64. Ok(())
  65. }
  66. }