use crate::rng::Rng;
use super::handle::{
maybe_sep, push_bday, push_hex3, push_letter_digit, push_suffix, push_year2, push_year4,
push_zpad3, push_zpad4, HandleInput, NICKNAME_SEPS,
};
pub const NOUNS: &[&str] = &[
"wolf",
"fox",
"hawk",
"raven",
"cobra",
"viper",
"tiger",
"lynx",
"bear",
"owl",
"crow",
"shark",
"panda",
"falcon",
"orca",
"mantis",
"cat",
"lion",
"dragon",
"panther",
"jaguar",
"eagle",
"serpent",
"scorpion",
"spider",
"bat",
"rhino",
"badger",
"otter",
"ferret",
"hyena",
"jackal",
"moose",
"bison",
"coyote",
"crane",
"heron",
"parrot",
"beetle",
"hornet",
"wasp",
"gecko",
"iguana",
"chameleon",
"pelican",
"dove",
"wren",
"finch",
"stag",
"elk",
"ram",
"goat",
"seal",
"walrus",
"squid",
"octopus",
"starling",
"swift",
"ghost",
"shadow",
"phantom",
"ninja",
"pirate",
"knight",
"scout",
"ranger",
"nomad",
"rebel",
"rogue",
"hunter",
"pilot",
"hero",
"wizard",
"sage",
"samurai",
"viking",
"gladiator",
"titan",
"sentinel",
"warden",
"oracle",
"sorcerer",
"paladin",
"monk",
"druid",
"shaman",
"bandit",
"mercenary",
"sniper",
"trooper",
"captain",
"marshal",
"reaper",
"specter",
"wraith",
"golem",
"sphinx",
"griffin",
"minotaur",
"chimera",
"phoenix",
"hydra",
"kraken",
"leviathan",
"behemoth",
"valkyrie",
"amazon",
"centurion",
"corsair",
"buccaneer",
"ronin",
"shogun",
"duelist",
"berserker",
"templar",
"crusader",
"storm",
"frost",
"blaze",
"flame",
"ember",
"spark",
"thunder",
"nova",
"comet",
"void",
"flux",
"pulse",
"dusk",
"dawn",
"rain",
"ice",
"mist",
"ash",
"dust",
"gale",
"breeze",
"torrent",
"inferno",
"glacier",
"aurora",
"nebula",
"quasar",
"eclipse",
"solstice",
"zenith",
"nadir",
"vortex",
"cascade",
"rapids",
"tundra",
"mesa",
"canyon",
"reef",
"coral",
"delta",
"summit",
"ridge",
"crest",
"peak",
"vale",
"grove",
"meadow",
"blade",
"shield",
"arrow",
"bolt",
"cipher",
"nexus",
"vector",
"apex",
"onyx",
"helix",
"prism",
"glitch",
"drift",
"surge",
"orbit",
"marble",
"crystal",
"shard",
"relic",
"totem",
"sigil",
"glyph",
"rune",
"scroll",
"compass",
"anvil",
"forge",
"hammer",
"dagger",
"lance",
"spear",
"helm",
"crown",
"throne",
"beacon",
"lantern",
"torch",
"mirror",
"lens",
"prong",
"fang",
"claw",
"talon",
"horn",
"tusk",
"spine",
"shell",
"pixel",
"neon",
"cyber",
"chrome",
"synth",
"matrix",
"grid",
"node",
"circuit",
"warp",
"quantum",
"photon",
"plasma",
"laser",
"mech",
"droid",
"probe",
"signal",
"relay",
"ace",
"maverick",
"legend",
"prodigy",
"enigma",
"mystic",
"prophet",
"vagabond",
"outlaw",
"misfit",
"drifter",
"loner",
"stranger",
"wanderer",
"boss",
"noob",
"pwnr",
"snax",
"clutch",
"carry",
"aggro",
"tank",
"healer",
"caster",
"spawn",
"camper",
"rusher",
"flanker",
"tryhard",
"admin",
"root",
"daemon",
"kernel",
"sudo",
"sysop",
"hacker",
"glider",
"runner",
"rider",
"racer",
"jumper",
"seeker",
"finder",
"maker",
"builder",
"keeper",
"breaker",
"bender",
"shifter",
"spinner",
"pain",
"fury",
"rage",
"havoc",
"chaos",
"doom",
"fate",
"bane",
"jinx",
"hex",
"luck",
"karma",
"zen",
"guru",
"sensei",
"master",
"chief",
"boss",
"king",
"duke",
"baron",
"count",
];
pub const TOPICS: &[&str] = &[
"dev", "code", "coder", "byte", "node", "stack", "sys", "ops", "hack", "data", "core", "bit",
"net", "web", "cloud", "log", "api", "git", "sql", "cli", "ssh", "vim", "nix", "rust", "lua",
"cpp", "bash", "art", "ink", "pixel", "craft", "beats", "music", "draws", "film", "photo", "lens", "sketch",
"paint", "studio", "edit", "mix", "vibe", "sound", "audio", "visual", "game", "plays", "gamer", "quest", "arena", "spawn", "loot", "raid", "tank", "healer", "mage",
"rogue", "dps", "pvp", "pve", "mmr", "rank", "frag", "combo", "rush", "grind", "farm", "craft",
"forge", "guild", "party", "boss", "level", "score", "streak",
"pro", "work", "lab", "hub", "hq", "eng", "tech", "build", "maker", "smith", "works", "studio",
"forge", "shop", "den", "nest", "lair", "vault", "bunker", "bot", "box", "zone", "crew", "clan", "squad", "base", "camp", "pack", "tribe", "gang", "mob",
"fleet", "army", "legion", "order", "cult", "ring", "circle",
"live", "stream", "cast", "show", "feed", "clip", "reel", "vlog", "pod",
"qwerty", "qwer", "zaq", "wasd", "asd", "asdf", "zxc", "qaz", "wsx",
"xxx", "xx", "xxxx", "xxy", "zzz", "zz", "zzzz", "aaa", "sss", "ddd", "qqq", "www", "eee",
];
pub const ADJECTIVES: &[&str] = &[
"mega", "ultra", "super", "hyper", "turbo", "epic", "max", "prime", "omega", "alpha", "sigma",
"delta", "zero", "infinite", "absolute", "supreme", "grand", "mighty",
"dark", "wild", "bold", "chill", "cool", "swift", "slick", "keen", "fierce", "savage", "noble",
"grand", "grim", "stern", "stoic", "witty", "sly", "sharp", "bright", "vivid", "neon",
"golden", "silver", "iron", "steel", "copper", "jade", "ruby", "amber", "ivory",
"tiny", "true", "real", "lone", "lazy", "slim", "fast", "loud", "calm", "deep", "tall", "vast",
"wide", "long", "flat", "raw", "pure", "clean", "fresh", "crisp", "hot", "cold", "warm", "dry",
"wet", "xo", "lil", "big", "old", "new", "top", "low", "mid", "high", "mini", "nano", "proto",
"retro", "neo", "meta", "anti", "semi", "dual", "tri", "chill", "cozy", "hazy", "misty", "dusty", "rusty", "foggy", "smoky", "stormy", "cloudy",
"sunny", "lunar", "solar", "astral", "cosmic", "arctic", "tropic",
"toxic", "chaotic", "random", "sneaky", "silent", "stealth", "rapid", "instant", "lethal",
"brutal", "insane", "crazy", "mental", "wicked", "vicious",
"noob", "pro", "based", "cringe", "elite", "godlike", "op", "broken", "cursed", "blessed",
"epic", "rare", "common", "exotic", "mythic",
];
pub const PREFIXES: &[&str] = &[
"the", "real", "its", "hey", "hi", "im", "just", "not", "only", "my", "mr", "ms", "dr", "sir",
"captain", "official", "og", "el", "la", "don", "lord", "lady", "king", "queen", "prince",
"duke", "chief", "general", "agent", "saint",
];
pub const SUFFIXES: &[&str] = &[
"dev", "pro", "hq", "io", "app", "tech", "code", "ops", "eng", "lab", "hub", "box", "net",
"one", "go", "run", "now", "ai", "js", "py", "web", "rs", "ml", "xyz",
"gg", "tv", "fm", "live", "vip", "lol", "wtf", "btw", "ftw", "irl", "afk", "brb", "omg", "tbh", "xd",
"xx", "xxx", "xxy", "zz", "zzz", "qq", "aaa", "sss",
];
const KEYBOARD_BARE: &[&str] = &[
"xx", "xxx", "xd", "zz", "zzz", "qq", "qqq", "aaa", "sss", "qwerty", "wasd", "asdf", "zxc",
"qaz", "owo", "uwu", "xoxo", "yolo", "kek", "pog", "gg", "ez",
];
const KEYBOARD_SEP: &[&str] = &[
"_xx", "_xxx", "_xd", "_zz", "_zzz", "_qq", "_qqq", "_aaa", "_sss", "_qwerty", "_wasd",
"_asdf", "_zxc", "_qaz", "_owo", "_uwu", "_xoxo", "_yolo", "_kek", "_pog", "_gg", "_ez",
];
const ICONIC: &[&str] = &[
"001", "007", "069", "100", "101", "111", "123", "228", "256", "303", "313", "322", "333",
"360", "404", "420", "444", "500", "512", "555", "616", "666", "699", "707", "777", "800",
"808", "888", "900", "911", "999", "1337",
];
pub struct Nickname {
pub full: String,
pub(super) base_len: usize,
pub word1: &'static str,
pub word2: &'static str,
pub sep: u8,
}
impl Nickname {
pub fn base(&self) -> &str {
&self.full[..self.base_len]
}
}
#[inline]
fn nick_length_tier(tag: u64) -> u8 {
let v = (tag >> 52) % 100;
match v {
0..=4 => 0, 5..=29 => 1, 30..=69 => 2, 70..=94 => 3, _ => 4, }
}
fn pick_any_word(rng: &mut Rng) -> &'static str {
let total = NOUNS.len() + ADJECTIVES.len() + TOPICS.len();
let i = rng.urange(0, total - 1);
if i < NOUNS.len() {
NOUNS[i]
} else if i < NOUNS.len() + ADJECTIVES.len() {
ADJECTIVES[i - NOUNS.len()]
} else {
TOPICS[i - NOUNS.len() - ADJECTIVES.len()]
}
}
fn nick_sep(rng: &mut Rng) -> (u8, &'static str) {
let s = maybe_sep(rng, NICKNAME_SEPS);
let b = if s.is_empty() { 0 } else { s.as_bytes()[0] };
(b, s)
}
pub fn build_nickname(tag: u64, rng: &mut Rng) -> Nickname {
let tier = nick_length_tier(tag);
let noun = NOUNS[rng.urange(0, NOUNS.len() - 1)];
let (word1, word2, sep_byte, sep_s, word3) = if tier <= 1 {
let w = if tier == 0 { pick_any_word(rng) } else { noun };
(w, w, 0u8, "", None)
} else if tier <= 3 {
let w = rng.urange(0, 49);
match w {
0..=19 => {
let adj = ADJECTIVES[rng.urange(0, ADJECTIVES.len() - 1)];
let (sb, ss) = nick_sep(rng);
(adj, noun, sb, ss, None)
}
20..=32 => {
let topic = TOPICS[rng.urange(0, TOPICS.len() - 1)];
let (sb, ss) = nick_sep(rng);
(noun, topic, sb, ss, None)
}
33..=40 => {
let noun2 = NOUNS[rng.urange(0, NOUNS.len() - 1)];
let (sb, ss) = nick_sep(rng);
(noun, noun2, sb, ss, None)
}
_ => {
let adj = ADJECTIVES[rng.urange(0, ADJECTIVES.len() - 1)];
let topic = TOPICS[rng.urange(0, TOPICS.len() - 1)];
let (sb, ss) = nick_sep(rng);
(adj, topic, sb, ss, None)
}
}
} else {
let adj = ADJECTIVES[rng.urange(0, ADJECTIVES.len() - 1)];
let topic = TOPICS[rng.urange(0, TOPICS.len() - 1)];
let (sb, ss) = nick_sep(rng);
(adj, noun, sb, ss, Some(topic))
};
let mut full = String::with_capacity(word1.len() + 1 + word2.len() + 8 + 6);
full.push_str(word1);
if !std::ptr::eq(word1, word2) {
full.push_str(sep_s);
full.push_str(word2);
}
if let Some(w3) = word3 {
full.push_str(sep_s);
full.push_str(w3);
}
let base_len = full.len();
let tw = rng.urange(0, 99);
match tw {
0..=17 => {
let y = tag % 100;
if y < 10 {
full.push('0');
}
full.push_str(itoa::Buffer::new().format(y));
}
18..=21 => {
full.push_str(itoa::Buffer::new().format(tag % 50 + 1975));
}
22..=44 => {
full.push_str(itoa::Buffer::new().format(tag % 9000 + 1));
}
45..=54 => {
let d = tag % 28 + 1;
let m = (tag >> 5) % 12 + 1;
let mut ib = itoa::Buffer::new();
if d < 10 {
full.push('0');
}
full.push_str(ib.format(d));
if m < 10 {
full.push('0');
}
full.push_str(ib.format(m));
}
55..=69 => {
full.push_str(itoa::Buffer::new().format(tag % 90 + 10));
}
70..=79 => {
full.push_str(itoa::Buffer::new().format(tag % 900 + 100));
}
80..=84 => {
full.push_str(ICONIC[(tag % ICONIC.len() as u64) as usize]);
}
85..=89 => {
let v = tag % 999 + 1;
if v < 10 {
full.push_str("00");
} else if v < 100 {
full.push('0');
}
full.push_str(itoa::Buffer::new().format(v));
}
90..=94 => {
full.push_str(KEYBOARD_BARE[(tag % KEYBOARD_BARE.len() as u64) as usize]);
}
_ => {
let h3 = tag & 0xFFF;
const HEX: &[u8; 16] = b"0123456789abcdef";
full.push(HEX[((h3 >> 8) & 0xF) as usize] as char);
full.push(HEX[((h3 >> 4) & 0xF) as usize] as char);
full.push(HEX[(h3 & 0xF) as usize] as char);
}
}
Nickname { full, base_len, word1, word2, sep: sep_byte }
}
pub fn gen_nickname(buf: &mut String, h: &HandleInput<'_>, rng: &mut Rng) {
match nick_length_tier(h.tag) {
0 => {
let word = pick_any_word(rng);
buf.push_str(word);
buf.push_str(itoa::Buffer::new().format(h.tag % 90 + 10));
}
1 => {
let noun = NOUNS[rng.urange(0, NOUNS.len() - 1)];
buf.push_str(noun);
push_nick_suffix(buf, h, rng);
}
2 => {
let noun = NOUNS[rng.urange(0, NOUNS.len() - 1)];
let w = rng.urange(0, 99);
match w {
0..=19 => {
let sfx = SUFFIXES[(h.tag % SUFFIXES.len() as u64) as usize];
buf.push_str(noun);
buf.push_str(sfx);
}
20..=44 => {
let adj = ADJECTIVES[rng.urange(0, ADJECTIVES.len() - 1)];
buf.push_str(adj);
buf.push_str(maybe_sep(rng, h.seps));
buf.push_str(noun);
}
45..=64 => {
let topic = TOPICS[rng.urange(0, TOPICS.len() - 1)];
buf.push_str(noun);
buf.push_str(maybe_sep(rng, h.seps));
buf.push_str(topic);
}
65..=79 => {
let noun2 = NOUNS[rng.urange(0, NOUNS.len() - 1)];
buf.push_str(noun);
buf.push_str(maybe_sep(rng, h.seps));
buf.push_str(noun2);
}
_ => {
buf.push_str(noun);
}
}
push_nick_suffix(buf, h, rng);
}
3 => {
let noun = NOUNS[rng.urange(0, NOUNS.len() - 1)];
let w = rng.urange(0, 99);
match w {
0..=29 => {
let prefix = PREFIXES[(h.tag % PREFIXES.len() as u64) as usize];
buf.push_str(prefix);
buf.push_str(maybe_sep(rng, h.seps));
let adj = ADJECTIVES[rng.urange(0, ADJECTIVES.len() - 1)];
buf.push_str(adj);
buf.push_str(maybe_sep(rng, h.seps));
buf.push_str(noun);
}
30..=69 => {
let adj = ADJECTIVES[rng.urange(0, ADJECTIVES.len() - 1)];
buf.push_str(adj);
buf.push_str(maybe_sep(rng, h.seps));
buf.push_str(noun);
}
_ => {
let topic = TOPICS[rng.urange(0, TOPICS.len() - 1)];
buf.push_str(noun);
buf.push_str(maybe_sep(rng, h.seps));
buf.push_str(topic);
}
}
push_nick_suffix(buf, h, rng);
}
_ => {
let noun = NOUNS[rng.urange(0, NOUNS.len() - 1)];
let adj = ADJECTIVES[rng.urange(0, ADJECTIVES.len() - 1)];
let topic = TOPICS[rng.urange(0, TOPICS.len() - 1)];
let sep = maybe_sep(rng, h.seps);
buf.push_str(adj);
buf.push_str(sep);
buf.push_str(noun);
buf.push_str(sep);
buf.push_str(topic);
push_nick_suffix(buf, h, rng);
}
}
}
fn push_nick_suffix(buf: &mut String, h: &HandleInput<'_>, rng: &mut Rng) {
let w = rng.urange(0, 99);
match w {
0..=7 => push_year2(buf, h), 8..=16 => buf.push_str(itoa::Buffer::new().format(h.tag % 9000 + 1)), 17..=19 => push_year4(buf, h), 20..=24 => push_bday(buf, h), 25..=29 => buf.push_str(itoa::Buffer::new().format(h.tag % 900 + 100)), 30..=34 => buf.push_str(itoa::Buffer::new().format(h.tag % 90 + 10)), 35..=39 => push_zpad3(buf, h.tag),
40..=49 => {
buf.push('_');
buf.push_str(itoa::Buffer::new().format(h.tag % 9000 + 1));
} 50..=54 => {
buf.push('_');
push_zpad3(buf, h.tag);
} 55..=59 => {
buf.push('_');
buf.push_str(itoa::Buffer::new().format(h.tag % 900 + 100));
} 60..=64 => {
buf.push('_');
buf.push_str(itoa::Buffer::new().format(h.tag % 90 + 10));
} 65..=69 => {
buf.push('_');
push_zpad4(buf, h.tag);
} 70..=74 => {
buf.push('_');
push_year2(buf, h);
}
75..=79 => push_nick_iconic(buf, h.tag), 80..=84 => push_nick_keyboard(buf, h.tag), 85..=89 => push_letter_digit(buf, h.tag), 90..=94 => push_hex3(buf, h.tag), _ => push_suffix(buf, h, rng), }
}
fn push_nick_iconic(buf: &mut String, tag: u64) {
buf.push_str(ICONIC[(tag % ICONIC.len() as u64) as usize]);
}
fn push_nick_keyboard(buf: &mut String, tag: u64) {
buf.push_str(KEYBOARD_SEP[(tag % KEYBOARD_SEP.len() as u64) as usize]);
}
pub fn mutate_nickname(buf: &mut String, nick: &Nickname, h: &HandleInput<'_>, rng: &mut Rng) {
let w = rng.urange(0, 99);
match w {
0..=34 => {
buf.push_str(nick.base());
push_nick_suffix(buf, h, rng);
}
35..=54 => {
let prefix = PREFIXES[rng.urange(0, PREFIXES.len() - 1)];
buf.push_str(prefix);
buf.push_str(maybe_sep(rng, h.seps));
buf.push_str(nick.base());
push_nick_suffix(buf, h, rng);
}
55..=74 => {
buf.push_str(nick.word1);
buf.push_str(maybe_sep(rng, h.seps));
buf.push_str(nick.word2);
push_nick_suffix(buf, h, rng);
}
75..=89 => {
buf.push_str(nick.base());
buf.push_str(maybe_sep(rng, h.seps));
let suffix = SUFFIXES[(h.tag % SUFFIXES.len() as u64) as usize];
buf.push_str(suffix);
push_nick_suffix(buf, h, rng);
}
_ => {
let c1 = nick.word1.as_bytes()[0] as char;
buf.push(c1);
buf.push_str(nick.word2);
push_nick_suffix(buf, h, rng);
}
}
}