commit a0666b94a080309df03f2846fb6da094d6a194d9 Author: annieversary Date: Thu Aug 26 14:04:31 2021 +0200 get something working diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16d5636 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +.DS_Store diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..27fd3d0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "incantata" +version = "0.1.0" +edition = "2018" + +[lib] +name = "incantata" +path = "src/lib.rs" + +[[bin]] +name = "incantata" +path = "src/bin.rs" + +[dependencies] +rand = "0.8.4" diff --git a/src/bin.rs b/src/bin.rs new file mode 100644 index 0000000..f318b4e --- /dev/null +++ b/src/bin.rs @@ -0,0 +1,24 @@ +use incantata::*; + +fn main() { + let s = Structure { + onset: 1, + onset_dict: CONSONANTS.chars().collect(), + nucleus: 1, + nucleus_dict: VOCALS + .chars() + .cycle() + .take(VOCALS.len() * 5) + .chain(VOCALS_ACCENTS.chars()) + .collect(), + coda: 0, + coda_dict: CONSONANTS.chars().collect(), + + min_len: 4, + suggested_len: 15, + }; + + for _ in 0..10 { + println!("{}", incantata(&s)); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..eec5a1a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,104 @@ +use rand::{thread_rng, Rng}; + +pub const CONSONANTS: &'static str = "bcdfghjklmnpqrstvwxyz"; + +pub const VOCALS: &'static str = "aeiou"; +pub const VOCALS_ACCENTS: &'static str = "aeiouàèìòùáéíóúäëïöü"; + +// TODO replace Vec with HashMap, where the value is the probability + +pub struct Structure { + pub onset: usize, + pub onset_dict: Vec, + + pub nucleus: usize, + pub nucleus_dict: Vec, + + pub coda: usize, + pub coda_dict: Vec, + + pub min_len: usize, + pub suggested_len: usize, +} + +pub fn incantata(structure: &Structure) -> String { + let mut rng = thread_rng(); + + let len = rng.gen_range(structure.min_len..structure.suggested_len); + + let mut s = String::new(); + while s.len() < len { + let syl = syllable(structure); + s.push_str(&syl); + } + + s +} + +enum State { + Onset(usize), + Nucleus(usize), + Coda(usize), +} + +fn syllable(structure: &Structure) -> String { + let mut rng = thread_rng(); + + let mut state = State::Onset(0); + + let mut s = String::new(); + 'syl: loop { + match state { + State::Onset(n) => { + if n >= structure.onset || rng.gen_bool(0.3) { + state = State::Nucleus(0); + } else { + state = State::Onset(n + 1); + s.push(structure.onset_dict[rng.gen_range(0..structure.onset_dict.len())]); + } + } + State::Nucleus(n) => { + // nucleus is one or more + if n >= structure.nucleus || (n > 0 && rng.gen_bool(0.3)) { + state = State::Coda(0); + } else { + state = State::Nucleus(n + 1); + s.push(structure.nucleus_dict[rng.gen_range(0..structure.nucleus_dict.len())]); + } + } + State::Coda(n) => { + if n >= structure.coda || rng.gen_bool(0.3) { + break 'syl; + } else { + state = State::Coda(n + 1); + s.push(structure.coda_dict[rng.gen_range(0..structure.coda_dict.len())]); + } + } + } + } + s +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let s = Structure { + onset: 1, + onset_dict: CONSONANTS.chars().collect(), + nucleus: 1, + nucleus_dict: VOCALS.chars().collect(), + coda: 1, + coda_dict: CONSONANTS.chars().collect(), + min_len: 4, + suggested_len: 10, + }; + + for _ in 0..100 { + let r = incantata(&s); + assert!(r.len() >= 4); + } + } +}