package gamemanagement import ( "strings" "unicode" ) const ( robotNicknameLanguageArabic = "arabic" robotNicknameLanguageEnglish = "english" ) var defaultRobotAvatarSources = []string{ "https://randomuser.me/api/portraits/men/0.jpg", "https://randomuser.me/api/portraits/men/1.jpg", "https://randomuser.me/api/portraits/men/2.jpg", "https://randomuser.me/api/portraits/men/3.jpg", "https://randomuser.me/api/portraits/men/4.jpg", "https://randomuser.me/api/portraits/men/5.jpg", "https://randomuser.me/api/portraits/men/6.jpg", "https://randomuser.me/api/portraits/men/7.jpg", "https://randomuser.me/api/portraits/men/8.jpg", "https://randomuser.me/api/portraits/men/9.jpg", "https://randomuser.me/api/portraits/men/10.jpg", "https://randomuser.me/api/portraits/men/11.jpg", "https://randomuser.me/api/portraits/men/12.jpg", "https://randomuser.me/api/portraits/men/13.jpg", "https://randomuser.me/api/portraits/men/14.jpg", "https://randomuser.me/api/portraits/men/15.jpg", "https://randomuser.me/api/portraits/men/16.jpg", "https://randomuser.me/api/portraits/men/17.jpg", "https://randomuser.me/api/portraits/men/18.jpg", "https://randomuser.me/api/portraits/men/19.jpg", "https://randomuser.me/api/portraits/men/20.jpg", "https://randomuser.me/api/portraits/men/21.jpg", "https://randomuser.me/api/portraits/men/22.jpg", "https://randomuser.me/api/portraits/men/23.jpg", "https://randomuser.me/api/portraits/men/24.jpg", "https://randomuser.me/api/portraits/men/25.jpg", "https://randomuser.me/api/portraits/men/26.jpg", "https://randomuser.me/api/portraits/men/27.jpg", "https://randomuser.me/api/portraits/men/28.jpg", "https://randomuser.me/api/portraits/men/29.jpg", "https://randomuser.me/api/portraits/men/30.jpg", "https://randomuser.me/api/portraits/men/31.jpg", "https://randomuser.me/api/portraits/men/32.jpg", "https://randomuser.me/api/portraits/men/33.jpg", "https://randomuser.me/api/portraits/men/34.jpg", "https://randomuser.me/api/portraits/men/35.jpg", "https://randomuser.me/api/portraits/men/36.jpg", "https://randomuser.me/api/portraits/men/37.jpg", "https://randomuser.me/api/portraits/men/38.jpg", "https://randomuser.me/api/portraits/men/39.jpg", "https://randomuser.me/api/portraits/men/40.jpg", "https://randomuser.me/api/portraits/men/41.jpg", "https://randomuser.me/api/portraits/men/42.jpg", "https://randomuser.me/api/portraits/men/43.jpg", "https://randomuser.me/api/portraits/men/44.jpg", "https://randomuser.me/api/portraits/men/45.jpg", "https://randomuser.me/api/portraits/men/46.jpg", "https://randomuser.me/api/portraits/men/47.jpg", "https://randomuser.me/api/portraits/men/48.jpg", "https://randomuser.me/api/portraits/men/49.jpg", "https://randomuser.me/api/portraits/men/50.jpg", "https://randomuser.me/api/portraits/men/51.jpg", "https://randomuser.me/api/portraits/men/52.jpg", "https://randomuser.me/api/portraits/men/53.jpg", "https://randomuser.me/api/portraits/men/54.jpg", "https://randomuser.me/api/portraits/men/55.jpg", "https://randomuser.me/api/portraits/men/56.jpg", "https://randomuser.me/api/portraits/men/57.jpg", "https://randomuser.me/api/portraits/men/58.jpg", "https://randomuser.me/api/portraits/men/59.jpg", "https://randomuser.me/api/portraits/men/60.jpg", "https://randomuser.me/api/portraits/men/61.jpg", "https://randomuser.me/api/portraits/men/62.jpg", "https://randomuser.me/api/portraits/men/63.jpg", "https://randomuser.me/api/portraits/men/64.jpg", "https://randomuser.me/api/portraits/men/65.jpg", "https://randomuser.me/api/portraits/men/66.jpg", "https://randomuser.me/api/portraits/men/67.jpg", "https://randomuser.me/api/portraits/men/68.jpg", "https://randomuser.me/api/portraits/men/69.jpg", "https://randomuser.me/api/portraits/men/70.jpg", "https://randomuser.me/api/portraits/men/71.jpg", "https://randomuser.me/api/portraits/men/72.jpg", "https://randomuser.me/api/portraits/men/73.jpg", "https://randomuser.me/api/portraits/men/74.jpg", "https://randomuser.me/api/portraits/men/75.jpg", "https://randomuser.me/api/portraits/men/76.jpg", "https://randomuser.me/api/portraits/men/77.jpg", "https://randomuser.me/api/portraits/men/78.jpg", "https://randomuser.me/api/portraits/men/79.jpg", "https://randomuser.me/api/portraits/men/80.jpg", "https://randomuser.me/api/portraits/men/81.jpg", "https://randomuser.me/api/portraits/men/82.jpg", "https://randomuser.me/api/portraits/men/83.jpg", "https://randomuser.me/api/portraits/men/84.jpg", "https://randomuser.me/api/portraits/men/85.jpg", "https://randomuser.me/api/portraits/men/86.jpg", "https://randomuser.me/api/portraits/men/87.jpg", "https://randomuser.me/api/portraits/men/88.jpg", "https://randomuser.me/api/portraits/men/89.jpg", "https://randomuser.me/api/portraits/men/90.jpg", "https://randomuser.me/api/portraits/men/91.jpg", "https://randomuser.me/api/portraits/men/92.jpg", "https://randomuser.me/api/portraits/men/93.jpg", "https://randomuser.me/api/portraits/men/94.jpg", "https://randomuser.me/api/portraits/men/95.jpg", "https://randomuser.me/api/portraits/men/96.jpg", "https://randomuser.me/api/portraits/men/97.jpg", "https://randomuser.me/api/portraits/men/98.jpg", "https://randomuser.me/api/portraits/men/99.jpg", "https://randomuser.me/api/portraits/women/0.jpg", "https://randomuser.me/api/portraits/women/1.jpg", "https://randomuser.me/api/portraits/women/2.jpg", "https://randomuser.me/api/portraits/women/3.jpg", "https://randomuser.me/api/portraits/women/4.jpg", "https://randomuser.me/api/portraits/women/5.jpg", "https://randomuser.me/api/portraits/women/6.jpg", "https://randomuser.me/api/portraits/women/7.jpg", "https://randomuser.me/api/portraits/women/8.jpg", "https://randomuser.me/api/portraits/women/9.jpg", "https://randomuser.me/api/portraits/women/10.jpg", "https://randomuser.me/api/portraits/women/11.jpg", "https://randomuser.me/api/portraits/women/12.jpg", "https://randomuser.me/api/portraits/women/13.jpg", "https://randomuser.me/api/portraits/women/14.jpg", "https://randomuser.me/api/portraits/women/15.jpg", "https://randomuser.me/api/portraits/women/16.jpg", "https://randomuser.me/api/portraits/women/17.jpg", "https://randomuser.me/api/portraits/women/18.jpg", "https://randomuser.me/api/portraits/women/19.jpg", "https://randomuser.me/api/portraits/women/20.jpg", "https://randomuser.me/api/portraits/women/21.jpg", "https://randomuser.me/api/portraits/women/22.jpg", "https://randomuser.me/api/portraits/women/23.jpg", "https://randomuser.me/api/portraits/women/24.jpg", "https://randomuser.me/api/portraits/women/25.jpg", "https://randomuser.me/api/portraits/women/26.jpg", "https://randomuser.me/api/portraits/women/27.jpg", "https://randomuser.me/api/portraits/women/28.jpg", "https://randomuser.me/api/portraits/women/29.jpg", "https://randomuser.me/api/portraits/women/30.jpg", "https://randomuser.me/api/portraits/women/31.jpg", "https://randomuser.me/api/portraits/women/32.jpg", "https://randomuser.me/api/portraits/women/33.jpg", "https://randomuser.me/api/portraits/women/34.jpg", "https://randomuser.me/api/portraits/women/35.jpg", "https://randomuser.me/api/portraits/women/36.jpg", "https://randomuser.me/api/portraits/women/37.jpg", "https://randomuser.me/api/portraits/women/38.jpg", "https://randomuser.me/api/portraits/women/39.jpg", "https://randomuser.me/api/portraits/women/40.jpg", "https://randomuser.me/api/portraits/women/41.jpg", "https://randomuser.me/api/portraits/women/42.jpg", "https://randomuser.me/api/portraits/women/43.jpg", "https://randomuser.me/api/portraits/women/44.jpg", "https://randomuser.me/api/portraits/women/45.jpg", "https://randomuser.me/api/portraits/women/46.jpg", "https://randomuser.me/api/portraits/women/47.jpg", "https://randomuser.me/api/portraits/women/48.jpg", "https://randomuser.me/api/portraits/women/49.jpg", "https://randomuser.me/api/portraits/women/50.jpg", "https://randomuser.me/api/portraits/women/51.jpg", "https://randomuser.me/api/portraits/women/52.jpg", "https://randomuser.me/api/portraits/women/53.jpg", "https://randomuser.me/api/portraits/women/54.jpg", "https://randomuser.me/api/portraits/women/55.jpg", "https://randomuser.me/api/portraits/women/56.jpg", "https://randomuser.me/api/portraits/women/57.jpg", "https://randomuser.me/api/portraits/women/58.jpg", "https://randomuser.me/api/portraits/women/59.jpg", "https://randomuser.me/api/portraits/women/60.jpg", "https://randomuser.me/api/portraits/women/61.jpg", "https://randomuser.me/api/portraits/women/62.jpg", "https://randomuser.me/api/portraits/women/63.jpg", "https://randomuser.me/api/portraits/women/64.jpg", "https://randomuser.me/api/portraits/women/65.jpg", "https://randomuser.me/api/portraits/women/66.jpg", "https://randomuser.me/api/portraits/women/67.jpg", "https://randomuser.me/api/portraits/women/68.jpg", "https://randomuser.me/api/portraits/women/69.jpg", "https://randomuser.me/api/portraits/women/70.jpg", "https://randomuser.me/api/portraits/women/71.jpg", "https://randomuser.me/api/portraits/women/72.jpg", "https://randomuser.me/api/portraits/women/73.jpg", "https://randomuser.me/api/portraits/women/74.jpg", "https://randomuser.me/api/portraits/women/75.jpg", "https://randomuser.me/api/portraits/women/76.jpg", "https://randomuser.me/api/portraits/women/77.jpg", "https://randomuser.me/api/portraits/women/78.jpg", "https://randomuser.me/api/portraits/women/79.jpg", "https://randomuser.me/api/portraits/women/80.jpg", "https://randomuser.me/api/portraits/women/81.jpg", "https://randomuser.me/api/portraits/women/82.jpg", "https://randomuser.me/api/portraits/women/83.jpg", "https://randomuser.me/api/portraits/women/84.jpg", "https://randomuser.me/api/portraits/women/85.jpg", "https://randomuser.me/api/portraits/women/86.jpg", "https://randomuser.me/api/portraits/women/87.jpg", "https://randomuser.me/api/portraits/women/88.jpg", "https://randomuser.me/api/portraits/women/89.jpg", "https://randomuser.me/api/portraits/women/90.jpg", "https://randomuser.me/api/portraits/women/91.jpg", "https://randomuser.me/api/portraits/women/92.jpg", "https://randomuser.me/api/portraits/women/93.jpg", "https://randomuser.me/api/portraits/women/94.jpg", "https://randomuser.me/api/portraits/women/95.jpg", "https://randomuser.me/api/portraits/women/96.jpg", "https://randomuser.me/api/portraits/women/97.jpg", "https://randomuser.me/api/portraits/women/98.jpg", "https://randomuser.me/api/portraits/women/99.jpg", } var defaultArabicRobotNicknames = []string{ "أمير", "سيف", "مالك", "فارس", "ليث", "بدر", "ماهر", "رامي", "كريم", "سامي", "عمر", "يوسف", "آدم", "ريان", "زيد", "نور", "ياسين", "أنس", "خالد", "طارق", "حازم", "نادر", "وسيم", "جواد", "مروان", "إياد", "نبيل", "هلال", "تميم", "سلمان", "راشد", "جاسم", "ماجد", "سلطان", "عادل", "هاني", "بلال", "يزن", "غيث", "قصي", "سليم", "ريان_الليل", "نجم", "ملك_اللعبة", "صقر", "ذيب", "أسد", "الزعيم", "القناص", "الأسطورة", "ظل", "برق", "رعد", "موج", "نمر", "شهاب", "فهد", "نايف", "نوف", "ليان", "ريم", "سارة", "مريم", "لينا", "هنا", "جود", "تالا", "دانا", "لارا", "ياسمين", "نوران", "رؤى", "سما", "ملك", "جنى", "آية", "ندى", "فرح", "أمل", "هدى", "سلمى", "ليلى", "رنا", "نورا", "ديما", "مايا", "ميس", "رهف", "شهد", "غلا", "لولو", "وردة", "قمر", "نجمة", "لؤلؤة", "الملكة", "أميرة", "روح", "حلم", "بسمة", } var defaultEnglishRobotNicknames = []string{ "Alex", "Jordan", "Taylor", "Morgan", "Riley", "Nova", "Phoenix", "Hunter", "Luna", "Mia", "Noah", "Liam", "Ava", "Emma", "Oliver", "Elijah", "James", "William", "Benjamin", "Lucas", "Mason", "Ethan", "Logan", "Jackson", "Levi", "Sebastian", "Mateo", "Jack", "Owen", "Theodore", "Aiden", "Samuel", "Joseph", "John", "David", "Wyatt", "Matthew", "Luke", "Asher", "Carter", "Julian", "Grayson", "Leo", "Jayden", "Gabriel", "Isaac", "Lincoln", "Anthony", "Hudson", "Dylan", "Ezra", "Thomas", "Charles", "Christopher", "Jaxon", "Maverick", "Josiah", "Isaiah", "Andrew", "Elias", "Joshua", "Nathan", "Caleb", "Ryan", "Adrian", "Miles", "Eli", "Nolan", "Christian", "Aaron", "Cameron", "Ezekiel", "Colton", "Luca", "Landon", "Hannah", "Sofia", "Isabella", "Amelia", "Olivia", "Charlotte", "Harper", "Evelyn", "Abigail", "Ella", "Scarlett", "Grace", "Chloe", "Victoria", "Aria", "Penelope", "Layla", "Zoey", "Nora", "Lily", "Ellie", "Violet", "Aurora", "Stella", "Hazel", } func normalizeRobotNicknameLanguage(value string) string { // 后台只开放英语和阿拉伯语两类昵称池;未知值回退英语,避免历史请求继续落到英阿混合池。 switch strings.ToLower(strings.TrimSpace(value)) { case "ar", "arabic", "arab": return robotNicknameLanguageArabic case "en", "english": return robotNicknameLanguageEnglish default: return robotNicknameLanguageEnglish } } func robotAccountLanguage(nicknameLanguage string) string { // 用户资料里的 language 跟昵称池保持一致,后续客户端或运营筛选机器人时不用再反推昵称字符集。 if normalizeRobotNicknameLanguage(nicknameLanguage) == robotNicknameLanguageArabic { return "ar" } return "en" } func robotNickname(index int, nicknameLanguage string) string { // 机器人昵称只从单一语言池取值,不能再拼随机 hex,否则会出现数字、下划线和英阿混排。 language := normalizeRobotNicknameLanguage(nicknameLanguage) if language == robotNicknameLanguageArabic { return robotNicknameFromPool(index, defaultArabicRobotNicknames, "لاعب", language) } return robotNicknameFromPool(index, defaultEnglishRobotNicknames, "Player", language) } func robotNicknameFromPool(index int, pool []string, fallback string, language string) string { // 前 100 个直接使用预设名;超过预设数量时用同语言两个预设名拼接,保证批量 200 个仍然不带数字后缀。 if index < 0 { index = 0 } if len(pool) == 0 { return fallback } base := sanitizeRobotNickname(pool[index%len(pool)], language) if base == "" { base = fallback } if index < len(pool) { return base } next := sanitizeRobotNickname(pool[(index%len(pool)+1)%len(pool)], language) if next == "" || next == base { next = sanitizeRobotNickname(pool[(index+index/len(pool)+2)%len(pool)], language) } if next == "" || next == base { return base } return base + next } func sanitizeRobotNickname(value string, language string) string { // 预设里可能有旧的分隔符或非目标语言字符;写库前统一剔除,保证“英语纯英语、阿拉伯语纯阿拉伯语”。 var builder strings.Builder for _, item := range strings.TrimSpace(value) { switch language { case robotNicknameLanguageArabic: if unicode.Is(unicode.Arabic, item) { builder.WriteRune(item) } default: if (item >= 'A' && item <= 'Z') || (item >= 'a' && item <= 'z') { builder.WriteRune(item) } } } return builder.String() } func robotGender(index int, requested string) string { requested = strings.TrimSpace(requested) if requested != "" { return requested } if index%2 == 0 { return "male" } return "female" } func robotAvatarSource(index int, requested []string) string { if source := pickString(requested, index); source != "" { return source } if len(defaultRobotAvatarSources) == 0 { return "" } return defaultRobotAvatarSources[index%len(defaultRobotAvatarSources)] }