优化取名逻辑
This commit is contained in:
303
DouyinApi.Services/Naming/BaziCalculator.cs
Normal file
303
DouyinApi.Services/Naming/BaziCalculator.cs
Normal file
@@ -0,0 +1,303 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DouyinApi.Services.Naming;
|
||||
|
||||
internal sealed class BaziCalculator
|
||||
{
|
||||
private static readonly string[] HeavenlyStems =
|
||||
{
|
||||
"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"
|
||||
};
|
||||
|
||||
private static readonly string[] EarthlyBranches =
|
||||
{
|
||||
"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"
|
||||
};
|
||||
|
||||
private static readonly int[] MonthNumberToBranchIndex =
|
||||
{
|
||||
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1
|
||||
};
|
||||
|
||||
private static readonly string[] ElementOrder = { "木", "火", "土", "金", "水" };
|
||||
|
||||
private static readonly Dictionary<string, string> StemElements = new()
|
||||
{
|
||||
["甲"] = "木",
|
||||
["乙"] = "木",
|
||||
["丙"] = "火",
|
||||
["丁"] = "火",
|
||||
["戊"] = "土",
|
||||
["己"] = "土",
|
||||
["庚"] = "金",
|
||||
["辛"] = "金",
|
||||
["壬"] = "水",
|
||||
["癸"] = "水"
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, string> BranchElements = new()
|
||||
{
|
||||
["子"] = "水",
|
||||
["丑"] = "土",
|
||||
["寅"] = "木",
|
||||
["卯"] = "木",
|
||||
["辰"] = "土",
|
||||
["巳"] = "火",
|
||||
["午"] = "火",
|
||||
["未"] = "土",
|
||||
["申"] = "金",
|
||||
["酉"] = "金",
|
||||
["戌"] = "土",
|
||||
["亥"] = "水"
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, int> HourStemBaseIndex = new()
|
||||
{
|
||||
["甲"] = 0,
|
||||
["己"] = 0,
|
||||
["乙"] = 2,
|
||||
["庚"] = 2,
|
||||
["丙"] = 4,
|
||||
["辛"] = 4,
|
||||
["丁"] = 6,
|
||||
["壬"] = 6,
|
||||
["戊"] = 8,
|
||||
["癸"] = 8
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, int> MonthStemBaseIndex = new()
|
||||
{
|
||||
["甲"] = 2,
|
||||
["己"] = 2,
|
||||
["乙"] = 4,
|
||||
["庚"] = 4,
|
||||
["丙"] = 6,
|
||||
["辛"] = 6,
|
||||
["丁"] = 8,
|
||||
["壬"] = 8,
|
||||
["戊"] = 0,
|
||||
["癸"] = 0
|
||||
};
|
||||
|
||||
private static readonly DateTime DayBaseDate = new(1900, 1, 31);
|
||||
private const int DayBaseIndex = 0; // 1900-01-31 对应甲子日
|
||||
|
||||
public BaziProfile Calculate(DateTime birthDateTime)
|
||||
{
|
||||
var solarYear = birthDateTime.Year;
|
||||
var currentYearSpring = new DateTime(birthDateTime.Year, 2, 4);
|
||||
if (birthDateTime < currentYearSpring)
|
||||
{
|
||||
solarYear -= 1;
|
||||
}
|
||||
|
||||
var yearStemIndex = Mod(solarYear - 4, HeavenlyStems.Length);
|
||||
var yearBranchIndex = Mod(solarYear - 4, EarthlyBranches.Length);
|
||||
|
||||
var monthNumber = ResolveMonthNumber(birthDateTime, solarYear);
|
||||
var monthStemBase = MonthStemBaseIndex[HeavenlyStems[yearStemIndex]];
|
||||
var monthStemIndex = Mod(monthStemBase + monthNumber - 1, HeavenlyStems.Length);
|
||||
var monthBranchIndex = MonthNumberToBranchIndex[monthNumber - 1];
|
||||
|
||||
var dayCycleIndex = Mod(DayBaseIndex + (int)(birthDateTime.Date - DayBaseDate).TotalDays, 60);
|
||||
var dayStemIndex = Mod(dayCycleIndex, HeavenlyStems.Length);
|
||||
var dayBranchIndex = Mod(dayCycleIndex, EarthlyBranches.Length);
|
||||
|
||||
var hourBranchIndex = GetHourBranchIndex(birthDateTime.Hour);
|
||||
var hourStemIndex = Mod(HourStemBaseIndex[HeavenlyStems[dayStemIndex]] + hourBranchIndex, HeavenlyStems.Length);
|
||||
|
||||
var yearStem = HeavenlyStems[yearStemIndex];
|
||||
var yearBranch = EarthlyBranches[yearBranchIndex];
|
||||
var monthStem = HeavenlyStems[monthStemIndex];
|
||||
var monthBranch = EarthlyBranches[monthBranchIndex];
|
||||
var dayStem = HeavenlyStems[dayStemIndex];
|
||||
var dayBranch = EarthlyBranches[dayBranchIndex];
|
||||
var hourStem = HeavenlyStems[hourStemIndex];
|
||||
var hourBranch = EarthlyBranches[hourBranchIndex];
|
||||
|
||||
var elementCounts = ElementOrder.ToDictionary(element => element, _ => 0);
|
||||
IncrementElement(elementCounts, StemElements[yearStem]);
|
||||
IncrementElement(elementCounts, BranchElements[yearBranch]);
|
||||
IncrementElement(elementCounts, StemElements[monthStem]);
|
||||
IncrementElement(elementCounts, BranchElements[monthBranch]);
|
||||
IncrementElement(elementCounts, StemElements[dayStem]);
|
||||
IncrementElement(elementCounts, BranchElements[dayBranch]);
|
||||
IncrementElement(elementCounts, StemElements[hourStem]);
|
||||
IncrementElement(elementCounts, BranchElements[hourBranch]);
|
||||
|
||||
var counts = ElementOrder
|
||||
.Select(element => new KeyValuePair<string, int>(element, elementCounts[element]))
|
||||
.ToList();
|
||||
|
||||
var max = counts.Max(pair => pair.Value);
|
||||
var min = counts.Min(pair => pair.Value);
|
||||
|
||||
var isBalanced = max - min <= 1;
|
||||
var weakElements = isBalanced
|
||||
? Array.Empty<string>()
|
||||
: counts.Where(pair => pair.Value == min).Select(pair => pair.Key).ToArray();
|
||||
var strongElements = counts.Where(pair => pair.Value == max).Select(pair => pair.Key).ToArray();
|
||||
|
||||
return new BaziProfile(
|
||||
yearStem,
|
||||
yearBranch,
|
||||
monthStem,
|
||||
monthBranch,
|
||||
dayStem,
|
||||
dayBranch,
|
||||
hourStem,
|
||||
hourBranch,
|
||||
elementCounts,
|
||||
ElementOrder,
|
||||
weakElements,
|
||||
strongElements,
|
||||
isBalanced);
|
||||
}
|
||||
|
||||
private static int ResolveMonthNumber(DateTime dt, int solarYear)
|
||||
{
|
||||
var springStart = new DateTime(solarYear, 2, 4);
|
||||
var jingZhe = new DateTime(solarYear, 3, 6);
|
||||
var qingMing = new DateTime(solarYear, 4, 5);
|
||||
var liXia = new DateTime(solarYear, 5, 5);
|
||||
var mangZhong = new DateTime(solarYear, 6, 6);
|
||||
var xiaoShu = new DateTime(solarYear, 7, 7);
|
||||
var liQiu = new DateTime(solarYear, 8, 8);
|
||||
var baiLu = new DateTime(solarYear, 9, 8);
|
||||
var hanLu = new DateTime(solarYear, 10, 8);
|
||||
var liDong = new DateTime(solarYear, 11, 7);
|
||||
var daXue = new DateTime(solarYear, 12, 7);
|
||||
var xiaoHan = new DateTime(solarYear + 1, 1, 6);
|
||||
var nextSpring = new DateTime(solarYear + 1, 2, 4);
|
||||
|
||||
if (dt >= springStart && dt < jingZhe)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (dt >= jingZhe && dt < qingMing)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
if (dt >= qingMing && dt < liXia)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
if (dt >= liXia && dt < mangZhong)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
if (dt >= mangZhong && dt < xiaoShu)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
if (dt >= xiaoShu && dt < liQiu)
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
if (dt >= liQiu && dt < baiLu)
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
if (dt >= baiLu && dt < hanLu)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
if (dt >= hanLu && dt < liDong)
|
||||
{
|
||||
return 9;
|
||||
}
|
||||
if (dt >= liDong && dt < daXue)
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
if (dt >= daXue && dt < xiaoHan)
|
||||
{
|
||||
return 11;
|
||||
}
|
||||
if (dt >= xiaoHan && dt < nextSpring)
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
return 11;
|
||||
}
|
||||
|
||||
private static int GetHourBranchIndex(int hour)
|
||||
{
|
||||
return ((hour + 1) / 2) % EarthlyBranches.Length;
|
||||
}
|
||||
|
||||
private static void IncrementElement(IDictionary<string, int> container, string element)
|
||||
{
|
||||
if (container.TryGetValue(element, out var value))
|
||||
{
|
||||
container[element] = value + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int Mod(int value, int modulus)
|
||||
{
|
||||
var result = value % modulus;
|
||||
return result < 0 ? result + modulus : result;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class BaziProfile
|
||||
{
|
||||
public BaziProfile(
|
||||
string yearStem,
|
||||
string yearBranch,
|
||||
string monthStem,
|
||||
string monthBranch,
|
||||
string dayStem,
|
||||
string dayBranch,
|
||||
string hourStem,
|
||||
string hourBranch,
|
||||
IReadOnlyDictionary<string, int> elementCounts,
|
||||
IReadOnlyList<string> elementOrder,
|
||||
IReadOnlyList<string> weakElements,
|
||||
IReadOnlyList<string> strongElements,
|
||||
bool isBalanced)
|
||||
{
|
||||
YearStem = yearStem;
|
||||
YearBranch = yearBranch;
|
||||
MonthStem = monthStem;
|
||||
MonthBranch = monthBranch;
|
||||
DayStem = dayStem;
|
||||
DayBranch = dayBranch;
|
||||
HourStem = hourStem;
|
||||
HourBranch = hourBranch;
|
||||
ElementCounts = elementCounts;
|
||||
ElementOrder = elementOrder;
|
||||
WeakElements = weakElements;
|
||||
StrongElements = strongElements;
|
||||
IsBalanced = isBalanced;
|
||||
}
|
||||
|
||||
public string YearStem { get; }
|
||||
|
||||
public string YearBranch { get; }
|
||||
|
||||
public string MonthStem { get; }
|
||||
|
||||
public string MonthBranch { get; }
|
||||
|
||||
public string DayStem { get; }
|
||||
|
||||
public string DayBranch { get; }
|
||||
|
||||
public string HourStem { get; }
|
||||
|
||||
public string HourBranch { get; }
|
||||
|
||||
public IReadOnlyDictionary<string, int> ElementCounts { get; }
|
||||
|
||||
public IReadOnlyList<string> ElementOrder { get; }
|
||||
|
||||
public IReadOnlyList<string> WeakElements { get; }
|
||||
|
||||
public IReadOnlyList<string> StrongElements { get; }
|
||||
|
||||
public bool IsBalanced { get; }
|
||||
}
|
||||
Reference in New Issue
Block a user