use hashbrown::HashMap;
use rand::Rng;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
pub struct Character {
pub char: char,
pub category: Category,
pub valid_behind: CharRule,
pub push: CharPush,
pub allowed_in_row: AllowInRow,
pub weight: u8, }
impl Character {
pub fn new(
character: char,
category: Category,
valid_behind: CharRule,
push: CharPush,
allowed_in_row: AllowInRow,
weight: u8,
) -> Self {
return Self {
char: character,
category,
valid_behind,
push,
allowed_in_row,
weight,
};
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct CharMap {
pub map: HashMap<char, Character>,
}
impl CharMap {
pub fn default() -> Self {
let mut map = HashMap::new();
let list = vec![
('a', Category::Vowel, CharRule::Any, CharPush::None, AllowInRow::Single, u8::MAX - 3),
('e', Category::Vowel, CharRule::Category(Category::Consonant), CharPush::Single('a', Chance::BelowOrEquals(u8::MAX / 4)), AllowInRow::Double, u8::MAX),
('i', Category::Vowel, CharRule::Any, CharPush::Many(vec!['e', 'o', 'a'], Chance::BelowOrEquals(u8::MAX / 2), 1), AllowInRow::Single, u8::MAX - 9),
('o', Category::Vowel, CharRule::Category(Category::Consonant), CharPush::Many(vec!['u', 'i'], Chance::BelowOrEquals(u8::MAX / 3), 1), AllowInRow::Double, u8::MAX - 15),
('u', Category::Vowel, CharRule::Any, CharPush::Single('i', Chance::BelowOrEquals(u8::MAX / 4)), AllowInRow::Single, u8::MAX - 21),
('b', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 30),
('c', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::Single('h', Chance::Equals(u8::MAX / 2)), AllowInRow::Single, u8::MAX - 21),
('d', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 15),
('f', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 24),
('g', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 27),
('h', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Single, u8::MAX - 21),
('j', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 55),
('k', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Single, u8::MAX - 33),
('l', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 15),
('m', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 21),
('n', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 12),
('p', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 24),
('q', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::Single('u', Chance::Always), AllowInRow::Single, u8::MAX - 75),
('r', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 15),
('s', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 15),
('t', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 3),
('v', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Single, u8::MAX - 30),
('w', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Single, u8::MAX - 45),
('x', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Single, u8::MAX - 60),
('y', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Single, u8::MAX - 27),
('z', Category::Consonant, CharRule::Category(Category::Vowel), CharPush::None, AllowInRow::Double, u8::MAX - 75),
];
for i in list {
map.insert(i.0, Character::new(i.0, i.1, i.2, i.3, i.4, i.5));
}
return Self {
map,
};
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)]
pub enum Category {
Consonant,
Vowel,
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
pub enum CharRule {
Any,
Category(Category),
Single(char),
Many(Vec<char>),
Rules(Vec<CharRule>),
}
impl CharRule {
pub fn hit(&self, character: &Character) -> bool {
return match self {
Self::Any => true,
Self::Category(cat) => { *cat == character.category },
Self::Single(c) => {*c == character.char},
Self::Many(v) => { v.contains(&character.char) },
Self::Rules(rules) => {
let mut hits = false;
for i in rules.iter() {
if i.hit(character) {
hits = true;
break;
}
}
hits
},
};
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
pub enum CharPush {
None,
Single(char, Chance),
Many(Vec<char>, Chance, usize),
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)]
pub enum Chance {
Always,
Equals(u8),
BelowOrEquals(u8),
AboveOrEquals(u8),
}
impl Chance {
pub fn gen_num() -> u8 {
return rand::thread_rng().gen_range(0..=u8::MAX);
}
pub fn hit(&self, check: u8) -> bool {
return match self {
Self::Always => true,
Self::Equals(c) => { check == *c },
Self::BelowOrEquals(c) => { check <= *c },
Self::AboveOrEquals(c) => { check >= *c },
};
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)]
pub enum AllowInRow {
Single,
Double,
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
pub struct WordRules {
max_vowels_in_row: u8,
}
impl WordRules {
pub fn default() -> Self {
return Self {
max_vowels_in_row: 1,
};
}
}
pub fn generate_word(word_rules: &WordRules, char_map: &CharMap, keys: &[char], min_cycles: usize, max_cycles: usize) -> String {
let cycles = rand::thread_rng().gen_range(min_cycles..=max_cycles);
let mut word: Vec<char> = Vec::new();
for _ in 0..cycles {
let mut push_char: char = *choice(keys);
if word.len() > 0 {
let mut regenerate = true;
while regenerate {
regenerate = false;
let last_char: char = word[word.len() - 1];
let last_character = char_map.map.get(&last_char).unwrap();
let character = char_map.map.get(&push_char).unwrap();
if character.valid_behind.hit(last_character) == false {
regenerate = true;
}
if character.char == last_character.char {
if character.allowed_in_row == AllowInRow::Single {
regenerate = true;
}
else {
if word.len() > 1 {
if word[word.len() - 2] == character.char {
regenerate = true;
}
}
}
}
let check: u8 = rand::thread_rng().gen();
if check > character.weight {
regenerate = true;
}
if character.category == Category::Vowel {
let mut in_row: u8 = 0;
let iter_num: u8;
if word.len() >= word_rules.max_vowels_in_row as usize {
iter_num = word_rules.max_vowels_in_row;
}
else {
iter_num = word.len() as u8;
}
for i in 1..=iter_num as usize {
if char_map.map.get(&word[word.len() - i]).unwrap().category == Category::Vowel {
in_row += 1;
}
else {
break;
}
}
if in_row >= word_rules.max_vowels_in_row {
regenerate = true;
}
}
if regenerate {
push_char = *choice(keys);
}
}
}
let character = char_map.map.get(&push_char).unwrap();
let mut segment: Vec<char> = Vec::new();
segment.push(push_char);
match &character.push {
CharPush::None => (),
CharPush::Single(c, chance) => {
let check = Chance::gen_num();
if chance.hit(check) {
segment.push(*c);
}
},
CharPush::Many(vc, chance, spin_cycles) => {
for _ in 0..*spin_cycles {
let check = Chance::gen_num();
if chance.hit(check) {
segment.push(*choice(&vc));
}
}
},
};
for i in segment {
word.push(i);
}
}
return word.into_iter().map(|x| x.to_string()).collect::<Vec<String>>().concat();
}
fn choice<T>(array: &[T]) -> &T {
return &array[rand::thread_rng().gen_range(0..array.len())];
}