yum-mirror/UwwwuPP

A rehost of leonetienne's UwwwuPP, a simple text filter to uwu-ify English text.

git clone https://git.yummers.dev/yum-mirror/UwwwuPP

LeonetienneIncweased chance for stuttab5eac77

master
15.0 KiB359 linesraw
1#ifndef UWWWU_LIBUWU_H
2#define UWWWU_LIBUWU_H
3
4#include <StringTools.h>
5#include <CharTools.h>
6#include <string>
7#include <sstream>
8#include <functional>
9#include <random>
10#include "Util.h"
11
12// This validator will only replace findings, if they are a complete word, and not just part of a word.
13static inline auto ValidatorFindingIsCompleteWord(const std::string& original, const std::string& finding, const std::size_t index) -> bool {
14    // Quick-accept: Original-string length matches finding-string length
15    if (original.length() == finding.length())
16        return true;
17
18    bool lastCharBreaksWord = true; // Default is true, because this value stays in case there is no last/next char.
19    bool nextCharBreaksWord = true; // In this case, "no character" would imply the word to be broken.
20
21    // Assign surrounding chars, if possible
22    if (index > 0)
23        lastCharBreaksWord = !CharTools::IsLetter(original[index - 1]);
24    if (index + finding.length() < original.length())
25        nextCharBreaksWord = !CharTools::IsLetter(original[index + finding.length()]);
26
27    // If both the last and the next character are word-breaking, replace.
28    if (lastCharBreaksWord && nextCharBreaksWord)
29        return true;
30        // Else: don't
31    else
32        return false;
33}
34
35
36//! Will make a boring string look sooper dooper kawaii and cute :3
37static inline std::string MakeUwu(std::string boringString) {
38    // Easy ones first
39    // none, lol
40
41    // Slightly more complex... Multichar replacements, but we have to keep capitalization...
42    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "th", "tw");
43    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "ove", "uv");
44    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "have", "haf");
45    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "tr", "tw");
46    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "up", "uwp");
47
48    // Let's do some language adjustments
49    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "twank you", "you're twe best <3333 xoxo", ValidatorFindingIsCompleteWord);
50    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "good", "sooper dooper", ValidatorFindingIsCompleteWord);
51    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "suwper", "sooper dooper", ValidatorFindingIsCompleteWord);
52    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "well", "sooper dooper", ValidatorFindingIsCompleteWord);
53    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "emacs", "vim", ValidatorFindingIsCompleteWord);
54    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "twanks", "you're twe best :33 xoxo", ValidatorFindingIsCompleteWord);
55    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "hello", "hiiiiiii", ValidatorFindingIsCompleteWord);
56    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "dear", "hiiiiiii", ValidatorFindingIsCompleteWord);
57
58    // Let's extend some phonetics
59    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "hi", "hiiiiiii");
60    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "ay", "aaay");
61    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "ey", "eeey");
62
63    // Replace N with Ny, but only if succeeded by a vowel, and not (preceded by an o and succeeded by an "e{nonletter}"): "one" has such a niche pronunciation...
64    boringString = Util::ConditionalReplaceButKeepSigns(
65            boringString,
66            "n",
67            "ny",
68            [](const std::string& original, const std::string& finding, const std::size_t index) {
69                // Don't replace, if we are on the last char
70                if (index + finding.length() == original.length())
71                    return false;
72
73                const char nextChar = CharTools::MakeLower(original[index + finding.length()]);
74                const bool haveLastchar = index > 0; // Do we even have a last char?
75                const char lastChar = haveLastchar ? CharTools::MakeLower(original[index - 1]) : '\0';
76
77                // Apply the complex "one\b"-rule:
78                // (don't replace if 'n' is preceded by 'o' and succeeded by 'e', which is succeeded by a word break)
79                {
80                    bool nextNextCharIsNotLetter = false;
81                    char nextNextChar;
82
83                    // How much length is left including `nextChar`?
84                    const std::size_t sizeLeft = original.length() - (index + finding.length());
85
86                    // We have room to pick the nextNext char...
87                    if (sizeLeft > 1)
88                    {
89                        nextNextChar = CharTools::MakeLower(original[index + finding.length() + 1]);
90                        nextNextCharIsNotLetter = !CharTools::IsLetter(nextNextChar);
91                    }
92
93                    const bool nextNextCharBreaksWord = (sizeLeft == 1) || (nextNextCharIsNotLetter);
94
95                    // Don't replace if:
96                    // (lastChar == o) && (nextChar == e) && (nextNextCharBreaksWord)
97                    if ((haveLastchar) && (lastChar == 'o') && (nextChar == 'e') && (nextNextCharBreaksWord))
98                        return false;
99                }
100
101                // Is this a vowel?
102                if (CharTools::IsVowel(nextChar))
103                    return true;
104
105                // Else, don't replace
106                return false;
107            }
108    );
109
110    // Replace R with W, but only if not succeeded by a non-vowel, and if it's not the first character of a word
111    boringString = Util::ConditionalReplaceButKeepSigns(
112            boringString,
113            "r",
114            "w",
115            [](const std::string& original, const std::string& finding, const std::size_t index) {
116                // Don't replace, if we are on the last char
117                if (index + finding.length() == original.length())
118                    return false;
119
120                // Don't replace if we're at index 0
121                if (index == 0)
122                    return false;
123
124                const char nextChar = CharTools::MakeLower(original[index + finding.length()]);
125                const char lastChar = CharTools::MakeLower(original[index - 1]);
126
127                // Is this a non-vowel?
128                if (!CharTools::IsVowel(nextChar))
129                    return false;
130
131                // Don't replace if the last char is not a letter
132                if (!CharTools::IsLetter(lastChar))
133                    return false;
134
135                // Else, replace
136                return true;
137            }
138    );
139
140    // Replace C with W, but only if succeeded and preceeded by a vowel
141    boringString = Util::ConditionalReplaceButKeepSigns(
142            boringString,
143            "c",
144            "w",
145            [](const std::string& original, const std::string& finding, const std::size_t index) {
146                // Don't replace, if we are on the last char
147                if (index + finding.length() == original.length())
148                    return false;
149
150                // Don't replace if we're at index 0
151                if (index == 0)
152                    return false;
153
154                const char nextChar = CharTools::MakeLower(original[index + finding.length()]);
155                const char lastChar = CharTools::MakeLower(original[index - 1]);
156
157                // Don't replace, if the next char is not a vowel
158                if (!CharTools::IsVowel(nextChar))
159                    return false;
160
161                // Don't replace if the last char is not a vowel
162                if (!CharTools::IsVowel(lastChar))
163                    return false;
164
165                // Else, replace
166                return true;
167            }
168    );
169
170    // Replace L with W, but only if not followed or preceded by another L, and if it's not the first character of a word
171    boringString = Util::ConditionalReplaceButKeepSigns(
172            boringString,
173            "l",
174            "w",
175            [](const std::string& original, const std::string& finding, const std::size_t index) {
176                // Our segment has to be at least two characters long
177                if (original.length() < finding.length() + 2)
178                    return false;
179
180                // Don't replace if we're at index o
181                if (index == 0)
182                    return false;
183
184                const char lastChar = CharTools::MakeLower(original[index - 1]);
185                const char nextChar = CharTools::MakeLower(original[index + finding.length()]);
186
187                // Don't replace if the last char is not a letter
188                if (!CharTools::IsLetter(lastChar))
189                    return false;
190
191                return (lastChar != 'l') && (nextChar != 'l');
192            }
193    );
194
195    // Replace LL with WW, but only if followed by a vowel
196    boringString = Util::ConditionalReplaceButKeepSigns(
197            boringString,
198            "ll",
199            "ww",
200            [](const std::string& original, const std::string& finding, const std::size_t index) {
201                // Don't replace, if we are on the last char
202                if (index + finding.length() == original.length())
203                    return false;
204
205                const char nextChar = CharTools::MakeLower(original[index + finding.length()]);
206
207                return CharTools::IsVowel(nextChar);
208            }
209    );
210
211    // Replace ER with A, but only if it's the last two letters of a word
212    boringString = Util::ConditionalReplaceButKeepSigns(
213            boringString,
214            "er",
215            "a",
216            [](const std::string& original, const std::string& finding, const std::size_t index) {
217                // Replace if we're at the end of this line/segment
218                if (index + finding.length() == original.length())
219                    return true;
220
221                // Fetch the next char
222                const char nextChar = CharTools::MakeLower(original[index + finding.length()]);
223
224                // Replace if the next char is not a letter
225                return !CharTools::IsLetter(nextChar);
226            }
227    );
228
229    // Replace R with W, but only (if it's preceeded by a vowel,
230    // or preceeded by another 'r',
231    // or if it's the first character of a word)
232    // and if it's not the last character of a word
233    boringString = Util::ConditionalReplaceButKeepSigns(
234            boringString,
235            "r",
236            "w",
237            [](const std::string& original, const std::string& finding, const std::size_t index) {
238                // Don't replace if it's the last character
239                if (index + finding.length() == original.length())
240                    return false;
241
242                // Do blindly replace if it's the first character
243                if (index == 0)
244                    return true;
245
246                // Fetch the last character
247                const char lastChar = CharTools::MakeLower(original[index - 1]);
248
249                // Fetch the next char
250                const char nextChar = CharTools::MakeLower(original[index + finding.length()]);
251
252                // Don't replace, if the last char is not a letter
253                if (!CharTools::IsLetter(lastChar))
254                  return false;
255
256                // Don't replace, if the next char is not a letter
257                if (!CharTools::IsLetter(nextChar))
258                  return false;
259
260                // Replace, if the last character is an 'r' aswell
261                if (lastChar == 'r')
262                  return true;
263
264                // Replace, if the last character is a vowel.
265                return CharTools::IsVowel(lastChar);
266            }
267    );
268
269    // Replace y with y-y (imitates shy stuttering), but only sometimes (random change),
270    // and if it is the first character of a word,
271    // and if it is followed by a vowel
272    boringString = Util::ConditionalReplaceButKeepSigns(
273            boringString,
274            "y",
275            "y-y",
276            [boringString](const std::string& original, const std::string& finding, const std::size_t index) {
277                // Don't replace, if we're at the end of our string
278                if (index + finding.length() == original.length())
279                    return false;
280
281                // This is a bit tricky, because we can't abort if we're on the first char
282                // but we can can't not check the previous char either. Running big risk
283                // of causing a segfault. So we have to not check the previous character,
284                // if we are on the first char.
285
286                // Are we on the first char?
287                const bool isFirstChar = index == 0;
288
289                // Fetch the last character
290                const char lastChar = isFirstChar ? '\0' : CharTools::MakeLower(original[index - 1]);
291
292                // Fetch the next character
293                const char nextChar = CharTools::MakeLower(original[index + finding.length()]);
294
295                // Don't replace, if the last char is a letter
296                if ((!isFirstChar) && (CharTools::IsLetter(lastChar)))
297                    return false;
298
299                // Don't replace, if the next char is not a vowel
300                if (!CharTools::IsVowel(nextChar))
301                    return false;
302
303                // Initialize deterministic prng
304                // Can't initialize one for all scopes, they don't seem to play
305                // nicely with being passed to lambda functions...
306                static std::mt19937 rng(std::hash<std::string>()(boringString)); // Seed rng based on string
307
308                // Chance (out of a 100) for the mutation to take place
309                // if all preconditions are met
310                constexpr int chance = 40;
311
312                // Roll the dice, baby!
313                return (rng() % 100) < chance;
314            }
315    );
316
317    // Replace random punctuation with uwwwwu cute symbols
318    // About evewy fifteenth symbol
319    std::stringstream ss;
320    for (const char c : boringString)
321    {
322        // Initialize deterministic prng
323        std::mt19937 rng(std::hash<std::string>()(boringString)); // Seed rng based on string
324
325        if ((c == '.') && (rng() % 15 == 0))
326        {
327            ss << " <3333 ^.^ ";
328        }
329        else if ((c == '!') && (rng() % 15 == 0))
330        {
331            ss << "!! Thadws impowtant! <3 ";
332        }
333        else if ((c == ',') && (rng() % 15 == 0))
334        {
335            ss << " <3 aaaaaand ";
336        }
337        else if ((c == '?') && (rng() % 15 == 0))
338        {
339            ss << "?? now tell me! >:( ";
340        }
341        else
342            ss << c;
343    }
344    boringString = ss.str();
345
346    // Also replace some ascii-"emojis'
347    boringString = StringTools::Replace(boringString, ":)", "UwU :D");
348    boringString = StringTools::Replace(boringString, ":D", ":3");
349    boringString = StringTools::Replace(boringString, ":-)", "UwwwU :3");
350    boringString = StringTools::Replace(boringString, "^^", "^.^ UwU");
351
352    // Some language replacement should happen after these more complex rules
353    boringString = Util::ConditionalReplaceButKeepSigns(boringString, "c++", "c++ (rust is hella cutewr btw ^^)");
354
355
356    return boringString;
357}
358
359#endif //UWWWU_LIBUWU_H