291 lines
11 KiB
C#
291 lines
11 KiB
C#
|
|
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
|
|||
|
|
}
|