feat(robot): gender-agree Cyrillic handle adjectives
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 49s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m12s

Cyrillic adjective+noun handles paired a masculine adjective with any noun, so a
feminine noun read ungrammatically ("Вольный Комета"). Carry masculine + feminine
adjective forms (cyrAdjective) and tag each noun's gender (cyrNoun); cyrillicNick
now renders the adjective in the agreeing form ("Рыжая Комета", "Дикий Волк"), and a
few feminine nouns (Комета/Звезда/Молния/Пантера/Рысь/Буря/Сова/Акула) are added for variety.
This commit is contained in:
Ilia Denisov
2026-06-16 15:51:00 +02:00
parent 6d66545062
commit 14b5f61af9
+45 -20
View File
@@ -122,23 +122,28 @@ func chineseName() string {
return family + given return family + given
} }
// latinNick builds a Latin handle from the adjective and noun stem pools. // latinNick builds a Latin handle from the adjective and noun stem pools. English
// adjectives do not inflect, so any pairing reads naturally.
func latinNick() string { func latinNick() string {
return composeNick(adjLatin, nounLatin) return assembleHandle(adjLatin[rand.IntN(len(adjLatin))], nounLatin[rand.IntN(len(nounLatin))])
} }
// cyrillicNick builds a Cyrillic handle from the adjective and noun stem pools. // cyrillicNick builds a Cyrillic handle, agreeing the adjective's form with the noun's
// grammatical gender ("Дикий Волк", "Дикая Рысь").
func cyrillicNick() string { func cyrillicNick() string {
return composeNick(adjCyr, nounCyr) noun := nounCyr[rand.IntN(len(nounCyr))]
adj := adjCyr[rand.IntN(len(adjCyr))]
form := adj.m
if noun.female {
form = adj.f
}
return assembleHandle(form, noun.word)
} }
// composeNick assembles a handle from an adjective and noun pool: a bare noun, an // assembleHandle joins an adjective and noun into a handle: a bare noun, the two joined
// adjective and noun joined by "_" or "." or camel-cased, optionally followed by a // by "_" or "." or camel-cased, optionally followed by a trailing number. It keeps at
// trailing number. It keeps at most one separator so the result, the number aside, // most one separator so the result, the number aside, reads like a name a human could pick.
// reads like a name a human could pick. func assembleHandle(adj, noun string) string {
func composeNick(adjectives, nouns []string) string {
adj := adjectives[rand.IntN(len(adjectives))]
noun := nouns[rand.IntN(len(nouns))]
var base string var base string
switch rand.IntN(4) { switch rand.IntN(4) {
case 0: case 0:
@@ -242,17 +247,37 @@ var nounLatin = []string{
"Phantom", "Comet", "Nova", "Blade", "Arrow", "Storm", "Wizard", "Ninja", "Phantom", "Comet", "Nova", "Blade", "Arrow", "Storm", "Wizard", "Ninja",
} }
// adjCyr and nounCyr are the adjective and noun stems for Cyrillic handles. // cyrAdjective is a Russian adjective in its masculine and feminine forms, so a handle's
var adjCyr = []string{ // stem can agree with the gender of its noun.
"Тёмный", "Ночной", "Снежный", "Лунный", "Звёздный", "Огненный", "Ледяной", "Стальной", "Дикий", "Тихий", type cyrAdjective struct{ m, f string }
"Быстрый", "Гордый", "Грозный", "Хитрый", "Смелый", "Весёлый", "Рыжий", "Серый", "Золотой", "Дерзкий",
"Лютый", "Бравый", "Мудрый", "Шальной", "Вольный", "Яростный", "Седой", "Жгучий", // cyrNoun is a Russian noun for a handle, tagged feminine so the paired adjective agrees.
type cyrNoun struct {
word string
female bool
} }
var nounCyr = []string{ // adjCyr and nounCyr are the adjective and noun stems for Cyrillic handles; cyrillicNick
"Волк", "Лис", "Ворон", "Тигр", "Орёл", "Дракон", "Сокол", "Ястреб", "Лев", "Медведь", // renders the adjective in the form that agrees with the chosen noun's gender.
"Барс", "Кот", "Кит", "Странник", "Охотник", "Рыцарь", "Маг", "Призрак", "Воин", "Комета", var adjCyr = []cyrAdjective{
"Клинок", "Ветер", "Гром", "Шторм", "Пилот", "Капитан", "Мастер", "Енот", {"Тёмный", "Тёмная"}, {"Ночной", "Ночная"}, {"Снежный", "Снежная"}, {"Лунный", "Лунная"},
{"Звёздный", "Звёздная"}, {"Огненный", "Огненная"}, {"Ледяной", "Ледяная"}, {"Стальной", "Стальная"},
{"Дикий", "Дикая"}, {"Тихий", "Тихая"}, {"Быстрый", "Быстрая"}, {"Гордый", "Гордая"},
{"Грозный", "Грозная"}, {"Хитрый", "Хитрая"}, {"Смелый", "Смелая"}, {"Весёлый", "Весёлая"},
{"Рыжий", "Рыжая"}, {"Серый", "Серая"}, {"Золотой", "Золотая"}, {"Дерзкий", "Дерзкая"},
{"Лютый", "Лютая"}, {"Бравый", "Бравая"}, {"Мудрый", "Мудрая"}, {"Шальной", "Шальная"},
{"Вольный", "Вольная"}, {"Яростный", "Яростная"}, {"Седой", "Седая"}, {"Жгучий", "Жгучая"},
}
var nounCyr = []cyrNoun{
{"Волк", false}, {"Лис", false}, {"Ворон", false}, {"Тигр", false}, {"Орёл", false},
{"Дракон", false}, {"Сокол", false}, {"Ястреб", false}, {"Лев", false}, {"Медведь", false},
{"Барс", false}, {"Кот", false}, {"Кит", false}, {"Странник", false}, {"Охотник", false},
{"Рыцарь", false}, {"Маг", false}, {"Призрак", false}, {"Воин", false}, {"Клинок", false},
{"Ветер", false}, {"Гром", false}, {"Шторм", false}, {"Пилот", false}, {"Капитан", false},
{"Мастер", false}, {"Енот", false},
{"Комета", true}, {"Звезда", true}, {"Молния", true}, {"Пантера", true}, {"Рысь", true},
{"Буря", true}, {"Сова", true}, {"Акула", true},
} }
// firstNamesRUWide is a broad pool of Russian first names — full and colloquial forms // firstNamesRUWide is a broad pool of Russian first names — full and colloquial forms