129 static nng inst{auto_probe_tag{}};
140 auto call_seed =
static_cast<std::uint64_t
>(_engine());
141 effolkronium::random_local call_engine;
142 call_engine.
seed(
static_cast<std::mt19937::result_type
>(
143 (call_seed ^ (call_seed >> 32U))));
144 auto result = solver(name, call_engine);
145 result._seed = call_seed;
160 std::uint64_t call_seed)
const
162 effolkronium::random_local call_engine;
163 call_engine.
seed(
static_cast<std::mt19937::result_type
>(
164 (call_seed ^ (call_seed >> 32U))));
165 auto result = solver(name, call_engine);
166 result._seed = call_seed;
182 _engine.seed(seed_value);
192 _engine.seed(std::random_device{}());
202 return !_wordlists.empty();
211 void load(
const std::filesystem::path& resource_path)
213 if (std::filesystem::exists(resource_path) &&
214 std::filesystem::is_directory(resource_path))
216 for (
const auto& entry :
217 std::filesystem::recursive_directory_iterator(resource_path))
219 if (entry.is_regular_file() &&
220 (entry.path().extension() ==
".words"))
230 using word_container = std::vector<std::wstring>;
233 using generator_fn = std::wstring (*)(
const std::wstring&,
234 effolkronium::random_local&);
237 using generators = std::vector<generator_fn>;
241 static const inline std::wstring _vowels{
242 L
"aeiouáàâãäåæçèéêëìíîïðñòóôõöøšùúûüýÿ"};
246 static const inline std::map<wchar_t, wchar_t> _leet_map{
247 {L
'o', L
'0'}, {L
'O', L
'0'}, {L
'i', L
'1'}, {L
'I', L
'1'}, {L
's', L
'2'},
248 {L
'S', L
'2'}, {L
'e', L
'3'}, {L
'E', L
'3'}, {L
'a', L
'4'}, {L
'A', L
'4'},
249 {L
'g', L
'6'}, {L
'G', L
'6'}, {L
't', L
'7'}, {L
'T', L
'7'}, {L
'b', L
'8'},
250 {L
'B', L
'8'}, {L
'q', L
'9'}, {L
'Q', L
'9'}};
253 std::vector<word_container> _wordlists;
256 std::mt19937_64 _engine{std::random_device{}()};
259 struct auto_probe_tag {};
262 explicit nng(auto_probe_tag )
264 for (
const auto& candidate : {
"resources",
"../resources",
265 "nickname-generator/resources"})
267 const std::filesystem::path p{candidate};
268 if (std::filesystem::exists(p) && std::filesystem::is_directory(p))
277 static std::wstring xfy(
const std::wstring& nickname,
278 effolkronium::random_local& engine)
281 std::wstring x_nickname{nickname};
284 switch (engine.get(0, 2))
287 x_nickname.push_back(L
'X');
290 x_nickname.insert(0, 1, L
'X');
293 x_nickname.push_back(L
'X');
294 x_nickname.insert(0, 1, L
'X');
302 static std::wstring reverse(
303 const std::wstring& nickname,
304 [[maybe_unused]] effolkronium::random_local& engine)
307 std::wstring reverse_nickname{nickname};
309 std::ranges::reverse(reverse_nickname);
311 return reverse_nickname;
316 static std::wstring yfy(
317 const std::wstring& nickname,
318 [[maybe_unused]] effolkronium::random_local& engine)
321 std::wstring nicknamy{nickname};
323 if (_vowels.contains(nickname.back()))
325 nicknamy.back() = L
'y';
329 nicknamy.push_back(L
'y');
336 static std::wstring numify(
const std::wstring& nickname,
337 effolkronium::random_local& engine)
340 std::wstring nickname_with_number{nickname};
343 static constexpr int max_digit{9};
346 nickname_with_number.push_back(
347 L
'0' + engine.get(1, max_digit));
350 for (
auto i = engine.get(0, 3); i > 0;
353 nickname_with_number.push_back(L
'0');
356 return nickname_with_number;
360 static std::wstring tracefy(
361 const std::wstring& nickname,
362 [[maybe_unused]] effolkronium::random_local& engine)
364 return nickname + L
"-";
369 static std::wstring ingify(
370 const std::wstring& nickname,
371 [[maybe_unused]] effolkronium::random_local& engine)
374 std::wstring nicknaming{nickname};
376 if (_vowels.contains(nickname.back()))
378 nicknaming.back() = L
'i';
382 nicknaming.push_back(L
'i');
385 nicknaming.append(L
"ng");
392 static std::wstring duovowel(
393 const std::wstring& nickname,
394 [[maybe_unused]] effolkronium::random_local& engine)
396 static const std::wstring simple_vowels = L
"aeio";
399 std::wstring nicknamee{nickname};
403 for (
const auto& vowel : simple_vowels)
405 if (
auto it = std::ranges::find(nicknamee, vowel);
406 it != nicknamee.end())
408 nicknamee.insert(it, *it);
417 static std::wstring oneleet(
const std::wstring& nickname,
418 effolkronium::random_local& engine)
421 std::wstring leet_nickname{nickname};
424 std::vector<std::size_t> candidate_positions;
426 for (std::size_t i = 0; i < leet_nickname.size(); ++i)
428 if (_leet_map.contains(leet_nickname[i]))
430 candidate_positions.push_back(i);
435 if (!candidate_positions.empty())
437 auto pos = *engine.get(candidate_positions);
438 leet_nickname[pos] = _leet_map.at(leet_nickname[pos]);
441 return leet_nickname;
446 static std::wstring allleet(
const std::wstring& nickname,
447 effolkronium::random_local& engine)
450 std::wstring leet_nickname{nickname};
453 static constexpr std::size_t max_replacements{2};
456 std::vector<std::size_t> candidate_positions;
458 for (std::size_t i = 0; i < leet_nickname.size(); ++i)
460 if (_leet_map.contains(leet_nickname[i]))
462 candidate_positions.push_back(i);
467 engine.shuffle(candidate_positions);
469 for (std::size_t n = 0;
470 n < std::min(max_replacements, candidate_positions.size()); ++n)
472 auto pos = candidate_positions[n];
473 leet_nickname[pos] = _leet_map.at(leet_nickname[pos]);
476 return leet_nickname;
481 static nickname leetify(nickname nickname,
482 effolkronium::random_local& engine,
483 bool force =
false,
int depth = 0)
487 static constexpr int max_depth{7};
490 if (force || engine.get<
bool>())
494 if (engine.get<
bool>())
497 static const generators possible_generators{
505 auto before = nickname._internal_string;
508 nickname._internal_string =
510 possible_generators))(nickname, engine);
514 if (depth < max_depth)
516 return leetify(nickname, engine,
517 nickname._internal_string == before,
524 static const generators possible_generators{
534 nickname._internal_string =
535 (*engine.get(possible_generators))(
544 static std::wstring snake_case(
const std::wstring& name)
547 result.reserve(name.size() + 4);
549 for (
auto&& [i, ch] : name | std::views::enumerate)
551 if (i > 0 && iswupper(ch) != 0)
553 result.push_back(L
'_');
555 result.push_back(ch);
562 static std::wstring upper_case(
563 const std::wstring& name,
564 [[maybe_unused]] effolkronium::random_local& engine)
567 std::wstring upper_name{name};
570 std::ranges::for_each(
572 [](
wchar_t& character) { character = std::towupper(character); });
578 static std::wstring lower_case(
579 const std::wstring& name,
580 [[maybe_unused]] effolkronium::random_local& engine)
583 std::wstring lower_name{name};
586 std::ranges::for_each(
588 [](
wchar_t& character) { character = std::towlower(character); });
594 static std::wstring title_case(
595 const std::wstring& name,
596 [[maybe_unused]] effolkronium::random_local& engine)
603 static std::wstring sentence_case(
const std::wstring& name,
604 effolkronium::random_local& engine)
612 std::wstring sentence_name{lower_case(name, engine)};
615 sentence_name.at(0) = std::towupper(sentence_name.at(0));
617 return sentence_name;
621 static std::wstring camel_case(
622 const std::wstring& name,
623 [[maybe_unused]] effolkronium::random_local& engine)
631 std::wstring camel_name{name};
634 camel_name.at(0) = std::towlower(camel_name.at(0));
640 static std::wstring reverse_sentence_case(
const std::wstring& name,
641 effolkronium::random_local& engine)
649 std::wstring rsentence_name{lower_case(name, engine)};
652 rsentence_name.back() = std::towupper(rsentence_name.back());
654 return rsentence_name;
658 static std::wstring bathtub_case(
const std::wstring& name,
659 effolkronium::random_local& engine)
667 std::wstring bathtub_name{lower_case(name, engine)};
670 bathtub_name.at(0) = std::towupper(bathtub_name.at(0));
673 bathtub_name.back() = std::towupper(bathtub_name.back());
679 static std::wstring winding_case(
const std::wstring& name,
680 effolkronium::random_local& engine)
683 std::wstring winding_name{lower_case(name, engine)};
686 for (
auto&& [i, ch] : winding_name | std::views::enumerate)
690 ch = std::towupper(ch);
698 static std::wstring random_case(
const std::wstring& name,
699 effolkronium::random_local& engine)
702 std::wstring random_name{lower_case(name, engine)};
705 std::ranges::for_each(random_name, [&engine](
wchar_t& character) {
706 if (engine.get<
bool>())
708 character = std::towupper(character);
717 static std::wstring random_single_case(
const std::wstring& name,
718 effolkronium::random_local& engine)
721 std::wstring random_name{lower_case(name, engine)};
724 auto random_char{engine.get(random_name)};
726 *random_char = std::towupper(*random_char);
732 static nickname format(nickname nickname,
733 effolkronium::random_local& engine)
737 static constexpr double snake_case_probability{0.01};
739 if (engine.get<
bool>(snake_case_probability))
741 nickname._internal_string = snake_case(nickname);
746 static const generators possible_generators = {
768 reverse_sentence_case,
769 reverse_sentence_case,
778 nickname._internal_string = (*engine.get(
779 possible_generators))(nickname, engine);
786 [[nodiscard]]
static std::vector<std::wstring> split_name(
787 const std::wstring& name)
789 std::vector<std::wstring> parts;
791 for (
auto part : name | std::views::split(L
' '))
793 std::wstring s(std::ranges::begin(part),
794 std::ranges::end(part));
797 parts.push_back(std::move(s));
805 static std::wstring first_name(
806 const std::wstring& name,
807 [[maybe_unused]] effolkronium::random_local& engine)
809 return *(split_name(name).cbegin());
813 static std::wstring last_name(
814 const std::wstring& name,
815 [[maybe_unused]] effolkronium::random_local& engine)
817 return *(split_name(name).crbegin());
821 static std::wstring any_name(
const std::wstring& name,
822 effolkronium::random_local& engine)
825 std::vector<std::wstring> names_list{split_name(name)};
827 return *engine.get(names_list);
831 static std::wstring initials(
832 const std::wstring& name,
833 [[maybe_unused]] effolkronium::random_local& engine)
836 std::wstring nickname;
839 for (
const auto& part : split_name(name))
841 nickname.push_back(part.front());
848 static std::wstring mix_two(
const std::wstring& name,
849 effolkronium::random_local& engine)
852 std::wstring nickname;
855 auto names_list{split_name(name)};
858 if (names_list.size() > 2)
860 names_list.erase(names_list.begin(),
861 names_list.end() - 2);
865 for (
const auto& name : names_list)
869 nickname.append(name);
873 nickname.append(name.substr(
874 0, engine.get<std::size_t>(
883 static std::wstring first_plus_initial(
884 const std::wstring& name,
885 [[maybe_unused]] effolkronium::random_local& engine)
888 const std::vector<std::wstring> names_list{split_name(name)};
890 if (names_list.size() < 2)
892 return names_list.front();
895 return names_list.front() + names_list.back().front();
899 static std::wstring initial_plus_last(
900 const std::wstring& name,
901 [[maybe_unused]] effolkronium::random_local& engine)
904 const auto names_list{split_name(name)};
906 if (names_list.size() < 2)
908 return names_list.front();
911 return names_list.front().front() + names_list.back();
915 static std::wstring reduce_single_name(
const std::wstring& name,
916 effolkronium::random_local& engine)
919 std::wstring single_name{any_name(name, engine)};
921 if (single_name.size() > 3)
926 std::remove_if(single_name.begin() + 1, single_name.end() - 1,
927 [&single_name](
const wchar_t& character) {
928 return (_vowels.contains(character) &&
929 (single_name.size() > 3));
931 single_name.end() - 1);
939 [[nodiscard]] nickname solver(
const std::wstring& name,
940 effolkronium::random_local& engine)
const
943 std::wstring original;
949 static constexpr double name_related_probability{0.25};
954 const bool use_name =
956 (_wordlists.empty() ||
957 engine.get<
bool>(name_related_probability));
965 static const generators possible_generators{
980 possible_generators))(name, engine);
983 else if (!_wordlists.empty())
986 const word_container& drawn_wordlist =
987 *engine.get(_wordlists);
991 *engine.get(drawn_wordlist);
995 throw(std::invalid_argument(
996 "Received no name and word lists are empty"));
999 return format(leetify({nick, original}, engine), engine);
1003 void parse_file(
const std::filesystem::path& file)
1006 std::wifstream tentative_file{file};
1009 if (tentative_file.is_open())
1012 const wchar_t delimiter{
'\n'};
1015 std::wstring file_line;
1018 word_container words_read{std::vector<std::wstring>()};
1021 while (std::getline(tentative_file, file_line, delimiter))
1025 if (!file_line.empty() && file_line.back() == L
'\r')
1027 file_line.pop_back();
1031 if (!file_line.empty())
1033 words_read.push_back(file_line);
1038 if (!words_read.empty())
1040 _wordlists.push_back(std::move(words_read));
1045 friend struct ::nng_test_access;