Files
Api/DouyinApi.Services/Naming/FallbackNameGenerator.cs

291 lines
11 KiB
C#
Raw Normal View History

2025-11-05 17:26:45 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using DouyinApi.Model.Naming;
#nullable enable
namespace DouyinApi.Services.Naming;
internal sealed class FallbackNameGenerator
{
private const int TargetCount = 5;
private static readonly string[] ElementOrder = { "木", "火", "土", "金", "水" };
private readonly CharacterLibrary _library = new();
public IReadOnlyList<NamingSuggestion> Generate(NamingRequest request, BaziProfile profile)
{
var surname = request.Surname;
var isSingle = string.Equals(request.NameLength, "single", StringComparison.OrdinalIgnoreCase);
var gender = NormalizeGender(request.Gender);
var results = new List<NamingSuggestion>();
var usedCharacters = new HashSet<string>(StringComparer.Ordinal);
var usedNames = new HashSet<string>(StringComparer.Ordinal);
var sequence = BuildElementSequence(profile);
var attempts = 0;
while (results.Count < TargetCount && attempts < 80)
{
var primaryElement = sequence[attempts % sequence.Count];
var first = _library.Pick(primaryElement, gender, usedCharacters);
if (first == null)
{
attempts++;
continue;
}
if (isSingle)
{
var fullName = surname + first.Character;
if (usedNames.Add(fullName))
{
results.Add(new NamingSuggestion
{
Name = fullName,
Meaning = first.Meaning,
ElementReason = BuildSingleReason(profile, first)
});
usedCharacters.Add(first.Character);
}
attempts++;
continue;
}
var tempUsed = new HashSet<string>(usedCharacters, StringComparer.Ordinal)
{
first.Character
};
var secondaryElement = sequence[(attempts + 1) % sequence.Count];
var second = _library.Pick(secondaryElement, gender, tempUsed);
if (second == null)
{
attempts++;
continue;
}
var givenName = first.Character + second.Character;
var full = surname + givenName;
if (usedNames.Add(full))
{
results.Add(new NamingSuggestion
{
Name = full,
Meaning = $"{first.Meaning}{second.Meaning}",
ElementReason = BuildDoubleReason(profile, first, second)
});
usedCharacters.Add(first.Character);
usedCharacters.Add(second.Character);
}
attempts++;
}
return results;
}
private static string NormalizeGender(string gender)
{
return string.Equals(gender, "female", StringComparison.OrdinalIgnoreCase) ? "female" : "male";
}
private static List<string> BuildElementSequence(BaziProfile profile)
{
var sequence = new List<string>();
if (profile.WeakElements.Count > 0)
{
sequence.AddRange(profile.WeakElements);
}
foreach (var element in ElementOrder)
{
if (!sequence.Contains(element))
{
sequence.Add(element);
}
}
return sequence;
}
private static string BuildSingleReason(BaziProfile profile, CharacterProfile profileChar)
{
if (!profile.IsBalanced && profile.WeakElements.Contains(profileChar.Element))
{
return $"命局中{profileChar.Element}势偏弱,选用同属性的“{profileChar.Character}”以补足生机。";
}
return $"“{profileChar.Character}”属{profileChar.Element},与命局相协,凸显个人气韵。";
}
private static string BuildDoubleReason(BaziProfile profile, CharacterProfile first, CharacterProfile second)
{
var clauses = new List<string>();
if (!profile.IsBalanced && profile.WeakElements.Contains(first.Element))
{
clauses.Add($"命局中{first.Element}势弱,首取“{first.Character}”以增添此行之力");
}
else
{
clauses.Add($"“{first.Character}”属{first.Element},奠定名字的核心气场");
}
if (first.Element == second.Element)
{
clauses.Add($"再以同为{second.Element}属性的“{second.Character}”相辅,巩固能量");
}
else if (!profile.IsBalanced && profile.WeakElements.Contains(second.Element))
{
clauses.Add($"辅以{second.Element}属性“{second.Character}”同步补益");
}
else
{
clauses.Add($"叠加{second.Element}属性“{second.Character}”,使五行流转更趋和谐");
}
return string.Join("", clauses) + "。";
}
}
internal sealed class CharacterLibrary
{
private readonly Dictionary<string, List<CharacterProfile>> _profiles;
public CharacterLibrary()
{
_profiles = new Dictionary<string, List<CharacterProfile>>(StringComparer.Ordinal)
{
["木"] = new List<CharacterProfile>
{
new("林", "木", 8, "林木森郁,生机盎然", GenderAffinity.Neutral),
new("杉", "木", 7, "杉木挺拔,坚韧自持", GenderAffinity.Male),
new("柏", "木", 9, "柏树常青,守正不渝", GenderAffinity.Male),
new("桐", "木", 10, "梧桐清雅,自带高洁", GenderAffinity.Neutral),
new("柯", "木", 9, "柯木坚劲,胸怀正义", GenderAffinity.Male),
new("梓", "木", 11, "梓木葱郁,延续家风", GenderAffinity.Neutral),
new("芸", "木", 10, "芸草幽香,温婉灵动", GenderAffinity.Female),
new("茜", "木", 9, "茜草明艳,朝气蓬勃", GenderAffinity.Female)
},
["火"] = new List<CharacterProfile>
{
new("炜", "火", 9, "炜火明亮,奋发向上", GenderAffinity.Male),
new("炎", "火", 8, "炎光熠熠,激情澎湃", GenderAffinity.Male),
new("晗", "火", 11, "晨晗初照,温暖柔和", GenderAffinity.Female),
new("晟", "火", 11, "晟意光盛,事业昌隆", GenderAffinity.Male),
new("旭", "火", 6, "旭日东升,胸怀朝阳", GenderAffinity.Neutral),
new("烁", "火", 10, "烁光璀璨,灵动敏捷", GenderAffinity.Neutral),
new("炫", "火", 9, "炫彩熠熠,才华外露", GenderAffinity.Neutral)
},
["土"] = new List<CharacterProfile>
{
new("坤", "土", 8, "坤厚载物,包容从容", GenderAffinity.Neutral),
new("均", "土", 7, "均衡端稳,踏实可靠", GenderAffinity.Neutral),
new("垣", "土", 9, "垣墙坚实,守护安宁", GenderAffinity.Neutral),
new("城", "土", 9, "城池稳固,守正不移", GenderAffinity.Male),
new("岳", "土", 8, "山岳巍峨,志向高远", GenderAffinity.Male),
new("培", "土", 11, "培土厚植,涵养成长", GenderAffinity.Neutral),
new("堇", "土", 12, "堇色沉静,内敛温润", GenderAffinity.Female),
new("坦", "土", 8, "坦荡诚笃,胸怀光明", GenderAffinity.Male)
},
["金"] = new List<CharacterProfile>
{
new("钧", "金", 9, "钧衡公正,持中守衡", GenderAffinity.Neutral),
new("铭", "金", 11, "铭记初心,彰显才华", GenderAffinity.Neutral),
new("锐", "金", 11, "锋芒锐利,果敢决断", GenderAffinity.Male),
new("钦", "金", 9, "钦敬谦和,端方稳重", GenderAffinity.Neutral),
new("钥", "金", 9, "钥启智慧,开拓视野", GenderAffinity.Neutral),
new("铠", "金", 12, "铠甲护身,坚毅勇敢", GenderAffinity.Male),
new("钰", "金", 10, "钰石珍贵,坚贞明亮", GenderAffinity.Female)
},
["水"] = new List<CharacterProfile>
{
new("泽", "水", 9, "泽润万物,胸怀宽广", GenderAffinity.Neutral),
new("泓", "水", 8, "泓水清澈,心境澄明", GenderAffinity.Neutral),
new("润", "水", 10, "润泽柔润,涵养细腻", GenderAffinity.Female),
new("涵", "水", 11, "涵容自守,温婉内敛", GenderAffinity.Female),
new("沐", "水", 7, "沐浴春风,清新怡然", GenderAffinity.Neutral),
new("泊", "水", 8, "泊舟安然,沉稳淡泊", GenderAffinity.Male),
new("洵", "水", 9, "洵美诚笃,信义笃实", GenderAffinity.Male),
new("沁", "水", 8, "沁香流淌,气质清雅", GenderAffinity.Female)
}
};
}
public CharacterProfile? Pick(string element, string gender, HashSet<string> usedCharacters)
{
if (!_profiles.TryGetValue(element, out var candidates))
{
return null;
}
// 优先匹配性别偏好
foreach (var profile in candidates)
{
if (usedCharacters.Contains(profile.Character))
{
continue;
}
if (profile.Affinity == GenderAffinity.Neutral ||
(profile.Affinity == GenderAffinity.Male && gender == "male") ||
(profile.Affinity == GenderAffinity.Female && gender == "female"))
{
if (profile.StrokeCount <= 12)
{
return profile;
}
}
}
// 兜底选择中性字
foreach (var profile in candidates)
{
if (usedCharacters.Contains(profile.Character))
{
continue;
}
if (profile.Affinity == GenderAffinity.Neutral && profile.StrokeCount <= 12)
{
return profile;
}
}
return null;
}
}
internal sealed class CharacterProfile
{
public CharacterProfile(string character, string element, int strokeCount, string meaning, GenderAffinity affinity)
{
Character = character;
Element = element;
StrokeCount = strokeCount;
Meaning = meaning;
Affinity = affinity;
}
public string Character { get; }
public string Element { get; }
public int StrokeCount { get; }
public string Meaning { get; }
public GenderAffinity Affinity { get; }
}
internal enum GenderAffinity
{
Neutral,
Male,
Female
}