witty_phrase_generator/
lib.rs1use rand::prelude::{ ThreadRng, thread_rng, IteratorRandom };
2use rand::Rng;
3use rand::seq::SliceRandom;
4
5use std::cell::RefCell;
6
7pub struct WPGen {
11 rng: RefCell<ThreadRng>,
12 words_intensifiers: Vec<&'static str>,
13 words_adjectives: Vec<&'static str>,
14 words_nouns: Vec<&'static str>,
15}
16
17impl WPGen {
18 pub fn new() -> WPGen {
19 let words_intensifiers = include_str!("intensifiers.txt");
20 let words_adjectives = include_str!("adjectives.txt") ;
21 let words_nouns = include_str!("nouns.txt") ;
22
23 let words_intensifiers = words_intensifiers.lines().collect::<Vec<&'static str>>();
24 let words_adjectives = words_adjectives .lines().collect::<Vec<&'static str>>();
25 let words_nouns = words_nouns .lines().collect::<Vec<&'static str>>();
26
27 WPGen {
28 rng: RefCell::new(thread_rng()),
29 words_intensifiers,
30 words_adjectives ,
31 words_nouns ,
32 }
33 }
34
35 fn create_format(words: usize) -> Vec<usize> {
36 let mut ret = vec![0; words+1];
40 let mut n = 1;
41
42 if words > 3 { ret[4] = 3 }
43 if words > 2 { ret[n] = 1; n += 1; }
44 if words > 1 { ret[n] = 2; n += 1; }
45 if words > 0 { ret[n] = 3; }
46
47 ret
48 }
49
50 fn generate_backtracking(&self,
51 len_min: usize,
52 len_max: usize,
53 dep: usize,
54 dict: &[Vec<&&'static str>; 4],
55 format: &Vec<usize>,
56 ) -> Option<Vec<&'static str>> {
57 let pool = &dict[format[dep]];
58
59 let mut upper_bound = 0;
70 while upper_bound < pool.len() && pool[upper_bound].len() <= len_max { upper_bound += 1; }
71
72
73 let pool = pool[0..upper_bound]
74 .choose_multiple(&mut *self.rng.borrow_mut(), upper_bound)
75 .collect::<Vec<&&&str>>();
76
77 for selected in pool {
78 assert!(selected.len() <= len_max);
79
80 if dep >= format.len()-1 { if selected.len() < len_min { continue } return Some(vec![selected])
83 }
84
85 if let Some(mut suf) = self.generate_backtracking(
86 (len_min as i32 - selected.len() as i32).max(0) as usize,
87 len_max - selected.len(),
88 dep+1, dict, format) {
89 suf.push(selected);
90 return Some(suf);
91 }
92 };
93 None
94 }
95
96 pub fn generic(&self,
110 words: usize,
111 count: usize,
112 len_min: Option<usize>,
113 len_max: Option<usize>,
114 word_len_max: Option<usize>,
115 start_char: Option<char>
116 ) -> Option<Vec<Vec<&'static str>>> {
117
118 let len_min = len_min.unwrap_or(0);
119 let len_max = len_max.unwrap_or(usize::MAX);
120 let word_len_max = word_len_max.unwrap_or(len_max / (words-1));
121
122 if len_max < len_min { return None }
123 if words > 4 || words == 0 { return None }
124
125 let words_intensifiers = self.words_intensifiers.iter().map(|x| x).collect::<Vec<&&'static str>>();
127 let words_adjectives = self.words_adjectives .iter().map(|x| x).collect::<Vec<&&'static str>>();
128 let words_nouns = self.words_nouns .iter().map(|x| x).collect::<Vec<&&'static str>>();
129
130 let mut dict = [Vec::new(), words_intensifiers, words_adjectives, words_nouns];
132
133 for list in &mut dict {
134 if let Some(c) = start_char {
136 list.retain(|s| s.chars().nth(0).expect("empty word found!") == c);
137 }
138 list.retain(|s| s.len() <= word_len_max); list.shuffle(&mut *self.rng.borrow_mut()); list.sort_by(|a, b: &&&str| a.len().cmp(&b.len())); }
142
143 let mut ret = vec![vec![""; words]; count];
144
145 for i in 0..count {
146 if let Some(mut vec) = self.generate_backtracking(len_min, len_max, 1, &dict, &WPGen::create_format(words)) {
147 vec.reverse();
148 ret[i] = vec;
149 } else {
150 return None;
151 }
152 }
153 Some(ret)
154 }
155
156 pub fn with_phrasewise_alliteration(&self,
169 words: usize,
170 count: usize,
171 len_min: Option<usize>,
172 len_max: Option<usize>,
173 word_len_max: Option<usize>,
174 ) -> Option<Vec<Vec<&'static str>>> {
175
176 let mut ret = Vec::new();
177 ret.reserve_exact(count);
178 for _ in 0..count {
179 ret.append(&mut loop {
180 let char = (*self.rng.borrow_mut()).gen_range(b'a'..b'z'+1) as char;
181 if let Some(p) = self.generic(words, 1, len_min, len_max, word_len_max, Some(char)) {
182 break p
183 }
184 });
185 }
186 Some(ret)
187 }
188
189 pub fn with_words(&self, words: usize) -> Option<Vec<&'static str>> {
193 let mut ret = vec![""; words];
194 let mut n = 0;
195
196 if words > 3 { ret[3] = self.words_nouns .iter().choose(&mut *self.rng.borrow_mut())?; }
197
198 if words > 2 { ret[n] = self.words_intensifiers.iter().choose(&mut *self.rng.borrow_mut())?; n += 1; }
199 if words > 1 { ret[n] = self.words_adjectives .iter().choose(&mut *self.rng.borrow_mut())?; n += 1; }
200 if words > 0 { ret[n] = self.words_nouns .iter().choose(&mut *self.rng.borrow_mut())?; }
201
202 Some(ret)
203 }
204}
205