mingpan 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +118 -0
  3. package/dist/core/bazi/BaziCore.d.ts +112 -0
  4. package/dist/core/bazi/BaziCore.d.ts.map +1 -0
  5. package/dist/core/bazi/BaziCore.js +606 -0
  6. package/dist/core/bazi/BaziCore.js.map +1 -0
  7. package/dist/core/bazi/SolarTermCalculator.d.ts +46 -0
  8. package/dist/core/bazi/SolarTermCalculator.d.ts.map +1 -0
  9. package/dist/core/bazi/SolarTermCalculator.js +452 -0
  10. package/dist/core/bazi/SolarTermCalculator.js.map +1 -0
  11. package/dist/core/bazi/TrueSolarTime.d.ts +64 -0
  12. package/dist/core/bazi/TrueSolarTime.d.ts.map +1 -0
  13. package/dist/core/bazi/TrueSolarTime.js +210 -0
  14. package/dist/core/bazi/TrueSolarTime.js.map +1 -0
  15. package/dist/core/bazi/ephemerisData.d.ts +10 -0
  16. package/dist/core/bazi/ephemerisData.d.ts.map +1 -0
  17. package/dist/core/bazi/ephemerisData.js +88 -0
  18. package/dist/core/bazi/ephemerisData.js.map +1 -0
  19. package/dist/core/bazi/index.d.ts +8 -0
  20. package/dist/core/bazi/index.d.ts.map +1 -0
  21. package/dist/core/bazi/index.js +30 -0
  22. package/dist/core/bazi/index.js.map +1 -0
  23. package/dist/core/bazi/types.d.ts +116 -0
  24. package/dist/core/bazi/types.d.ts.map +1 -0
  25. package/dist/core/bazi/types.js +7 -0
  26. package/dist/core/bazi/types.js.map +1 -0
  27. package/dist/core/calendar/SolarTermsCalculator.d.ts +33 -0
  28. package/dist/core/calendar/SolarTermsCalculator.d.ts.map +1 -0
  29. package/dist/core/calendar/SolarTermsCalculator.js +111 -0
  30. package/dist/core/calendar/SolarTermsCalculator.js.map +1 -0
  31. package/dist/core/calendar/astronomicalCalendar.d.ts +74 -0
  32. package/dist/core/calendar/astronomicalCalendar.d.ts.map +1 -0
  33. package/dist/core/calendar/astronomicalCalendar.js +276 -0
  34. package/dist/core/calendar/astronomicalCalendar.js.map +1 -0
  35. package/dist/core/calendar/calendar.d.ts +81 -0
  36. package/dist/core/calendar/calendar.d.ts.map +1 -0
  37. package/dist/core/calendar/calendar.js +223 -0
  38. package/dist/core/calendar/calendar.js.map +1 -0
  39. package/dist/core/calendar/constants.d.ts +151 -0
  40. package/dist/core/calendar/constants.d.ts.map +1 -0
  41. package/dist/core/calendar/constants.js +97 -0
  42. package/dist/core/calendar/constants.js.map +1 -0
  43. package/dist/core/calendar/julian.d.ts +54 -0
  44. package/dist/core/calendar/julian.d.ts.map +1 -0
  45. package/dist/core/calendar/julian.js +142 -0
  46. package/dist/core/calendar/julian.js.map +1 -0
  47. package/dist/core/calendar/lunarCalendar.d.ts +79 -0
  48. package/dist/core/calendar/lunarCalendar.d.ts.map +1 -0
  49. package/dist/core/calendar/lunarCalendar.js +164 -0
  50. package/dist/core/calendar/lunarCalendar.js.map +1 -0
  51. package/dist/core/calendar/solarTerms.d.ts +105 -0
  52. package/dist/core/calendar/solarTerms.d.ts.map +1 -0
  53. package/dist/core/calendar/solarTerms.js +487 -0
  54. package/dist/core/calendar/solarTerms.js.map +1 -0
  55. package/dist/core/constants/bazi.d.ts +138 -0
  56. package/dist/core/constants/bazi.d.ts.map +1 -0
  57. package/dist/core/constants/bazi.js +332 -0
  58. package/dist/core/constants/bazi.js.map +1 -0
  59. package/dist/core/constants/index.d.ts +6 -0
  60. package/dist/core/constants/index.d.ts.map +1 -0
  61. package/dist/core/constants/index.js +37 -0
  62. package/dist/core/constants/index.js.map +1 -0
  63. package/dist/core/constants/positions.d.ts +27 -0
  64. package/dist/core/constants/positions.d.ts.map +1 -0
  65. package/dist/core/constants/positions.js +39 -0
  66. package/dist/core/constants/positions.js.map +1 -0
  67. package/dist/core/ziwei/BrightnessSystem.d.ts +53 -0
  68. package/dist/core/ziwei/BrightnessSystem.d.ts.map +1 -0
  69. package/dist/core/ziwei/BrightnessSystem.js +278 -0
  70. package/dist/core/ziwei/BrightnessSystem.js.map +1 -0
  71. package/dist/core/ziwei/IztroAdapter.d.ts +78 -0
  72. package/dist/core/ziwei/IztroAdapter.d.ts.map +1 -0
  73. package/dist/core/ziwei/IztroAdapter.js +314 -0
  74. package/dist/core/ziwei/IztroAdapter.js.map +1 -0
  75. package/dist/core/ziwei/MutagenCore.d.ts +81 -0
  76. package/dist/core/ziwei/MutagenCore.d.ts.map +1 -0
  77. package/dist/core/ziwei/MutagenCore.js +246 -0
  78. package/dist/core/ziwei/MutagenCore.js.map +1 -0
  79. package/dist/index.d.ts +13 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +1101 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/output/fortuneTextRenderer.d.ts +63 -0
  84. package/dist/output/fortuneTextRenderer.d.ts.map +1 -0
  85. package/dist/output/fortuneTextRenderer.js +384 -0
  86. package/dist/output/fortuneTextRenderer.js.map +1 -0
  87. package/dist/output/listTextRenderer.d.ts +298 -0
  88. package/dist/output/listTextRenderer.d.ts.map +1 -0
  89. package/dist/output/listTextRenderer.js +942 -0
  90. package/dist/output/listTextRenderer.js.map +1 -0
  91. package/dist/services/bazi/BaziService.d.ts +83 -0
  92. package/dist/services/bazi/BaziService.d.ts.map +1 -0
  93. package/dist/services/bazi/BaziService.js +635 -0
  94. package/dist/services/bazi/BaziService.js.map +1 -0
  95. package/dist/services/bazi/analyzers/ClimateAnalyzer.d.ts +31 -0
  96. package/dist/services/bazi/analyzers/ClimateAnalyzer.d.ts.map +1 -0
  97. package/dist/services/bazi/analyzers/ClimateAnalyzer.js +194 -0
  98. package/dist/services/bazi/analyzers/ClimateAnalyzer.js.map +1 -0
  99. package/dist/services/bazi/analyzers/HiddenStemsAnalyzer.d.ts +16 -0
  100. package/dist/services/bazi/analyzers/HiddenStemsAnalyzer.d.ts.map +1 -0
  101. package/dist/services/bazi/analyzers/HiddenStemsAnalyzer.js +60 -0
  102. package/dist/services/bazi/analyzers/HiddenStemsAnalyzer.js.map +1 -0
  103. package/dist/services/bazi/analyzers/NaYinAnalyzer.d.ts +33 -0
  104. package/dist/services/bazi/analyzers/NaYinAnalyzer.d.ts.map +1 -0
  105. package/dist/services/bazi/analyzers/NaYinAnalyzer.js +209 -0
  106. package/dist/services/bazi/analyzers/NaYinAnalyzer.js.map +1 -0
  107. package/dist/services/bazi/analyzers/PatternAnalyzer.d.ts +159 -0
  108. package/dist/services/bazi/analyzers/PatternAnalyzer.d.ts.map +1 -0
  109. package/dist/services/bazi/analyzers/PatternAnalyzer.js +1150 -0
  110. package/dist/services/bazi/analyzers/PatternAnalyzer.js.map +1 -0
  111. package/dist/services/bazi/analyzers/RelationsAnalyzer.d.ts +114 -0
  112. package/dist/services/bazi/analyzers/RelationsAnalyzer.d.ts.map +1 -0
  113. package/dist/services/bazi/analyzers/RelationsAnalyzer.js +704 -0
  114. package/dist/services/bazi/analyzers/RelationsAnalyzer.js.map +1 -0
  115. package/dist/services/bazi/analyzers/ShenShaAnalyzer.d.ts +236 -0
  116. package/dist/services/bazi/analyzers/ShenShaAnalyzer.d.ts.map +1 -0
  117. package/dist/services/bazi/analyzers/ShenShaAnalyzer.js +2079 -0
  118. package/dist/services/bazi/analyzers/ShenShaAnalyzer.js.map +1 -0
  119. package/dist/services/bazi/analyzers/StrengthAnalyzer.d.ts +181 -0
  120. package/dist/services/bazi/analyzers/StrengthAnalyzer.d.ts.map +1 -0
  121. package/dist/services/bazi/analyzers/StrengthAnalyzer.js +1119 -0
  122. package/dist/services/bazi/analyzers/StrengthAnalyzer.js.map +1 -0
  123. package/dist/services/bazi/analyzers/TenGodsAnalyzer.d.ts +78 -0
  124. package/dist/services/bazi/analyzers/TenGodsAnalyzer.d.ts.map +1 -0
  125. package/dist/services/bazi/analyzers/TenGodsAnalyzer.js +383 -0
  126. package/dist/services/bazi/analyzers/TenGodsAnalyzer.js.map +1 -0
  127. package/dist/services/bazi/analyzers/TraditionalAnalyzer.d.ts +46 -0
  128. package/dist/services/bazi/analyzers/TraditionalAnalyzer.d.ts.map +1 -0
  129. package/dist/services/bazi/analyzers/TraditionalAnalyzer.js +260 -0
  130. package/dist/services/bazi/analyzers/TraditionalAnalyzer.js.map +1 -0
  131. package/dist/services/bazi/analyzers/YongShenAnalyzer.d.ts +70 -0
  132. package/dist/services/bazi/analyzers/YongShenAnalyzer.d.ts.map +1 -0
  133. package/dist/services/bazi/analyzers/YongShenAnalyzer.js +858 -0
  134. package/dist/services/bazi/analyzers/YongShenAnalyzer.js.map +1 -0
  135. package/dist/services/bazi/calculators/DaYunCalculator.d.ts +71 -0
  136. package/dist/services/bazi/calculators/DaYunCalculator.d.ts.map +1 -0
  137. package/dist/services/bazi/calculators/DaYunCalculator.js +496 -0
  138. package/dist/services/bazi/calculators/DaYunCalculator.js.map +1 -0
  139. package/dist/services/bazi/calculators/LiuNianCalculator.d.ts +88 -0
  140. package/dist/services/bazi/calculators/LiuNianCalculator.d.ts.map +1 -0
  141. package/dist/services/bazi/calculators/LiuNianCalculator.js +546 -0
  142. package/dist/services/bazi/calculators/LiuNianCalculator.js.map +1 -0
  143. package/dist/services/bazi/calculators/LiuRiCalculator.d.ts +66 -0
  144. package/dist/services/bazi/calculators/LiuRiCalculator.d.ts.map +1 -0
  145. package/dist/services/bazi/calculators/LiuRiCalculator.js +497 -0
  146. package/dist/services/bazi/calculators/LiuRiCalculator.js.map +1 -0
  147. package/dist/services/bazi/calculators/LiuYueCalculator.d.ts +50 -0
  148. package/dist/services/bazi/calculators/LiuYueCalculator.d.ts.map +1 -0
  149. package/dist/services/bazi/calculators/LiuYueCalculator.js +338 -0
  150. package/dist/services/bazi/calculators/LiuYueCalculator.js.map +1 -0
  151. package/dist/services/bazi/calculators/LuckCycleCalculator.d.ts +123 -0
  152. package/dist/services/bazi/calculators/LuckCycleCalculator.d.ts.map +1 -0
  153. package/dist/services/bazi/calculators/LuckCycleCalculator.js +414 -0
  154. package/dist/services/bazi/calculators/LuckCycleCalculator.js.map +1 -0
  155. package/dist/services/bazi/index.d.ts +21 -0
  156. package/dist/services/bazi/index.d.ts.map +1 -0
  157. package/dist/services/bazi/index.js +83 -0
  158. package/dist/services/bazi/index.js.map +1 -0
  159. package/dist/services/bazi/types.d.ts +597 -0
  160. package/dist/services/bazi/types.d.ts.map +1 -0
  161. package/dist/services/bazi/types.js +123 -0
  162. package/dist/services/bazi/types.js.map +1 -0
  163. package/dist/services/ziwei/ZiweiService.d.ts +86 -0
  164. package/dist/services/ziwei/ZiweiService.d.ts.map +1 -0
  165. package/dist/services/ziwei/ZiweiService.js +343 -0
  166. package/dist/services/ziwei/ZiweiService.js.map +1 -0
  167. package/dist/services/ziwei/calculators/BrightnessCalculator.d.ts +38 -0
  168. package/dist/services/ziwei/calculators/BrightnessCalculator.d.ts.map +1 -0
  169. package/dist/services/ziwei/calculators/BrightnessCalculator.js +142 -0
  170. package/dist/services/ziwei/calculators/BrightnessCalculator.js.map +1 -0
  171. package/dist/services/ziwei/calculators/DailyCalculator.d.ts +27 -0
  172. package/dist/services/ziwei/calculators/DailyCalculator.d.ts.map +1 -0
  173. package/dist/services/ziwei/calculators/DailyCalculator.js +55 -0
  174. package/dist/services/ziwei/calculators/DailyCalculator.js.map +1 -0
  175. package/dist/services/ziwei/calculators/DecadeCalculator.d.ts +46 -0
  176. package/dist/services/ziwei/calculators/DecadeCalculator.d.ts.map +1 -0
  177. package/dist/services/ziwei/calculators/DecadeCalculator.js +181 -0
  178. package/dist/services/ziwei/calculators/DecadeCalculator.js.map +1 -0
  179. package/dist/services/ziwei/calculators/HoroscopeStarsCalculator.d.ts +28 -0
  180. package/dist/services/ziwei/calculators/HoroscopeStarsCalculator.d.ts.map +1 -0
  181. package/dist/services/ziwei/calculators/HoroscopeStarsCalculator.js +84 -0
  182. package/dist/services/ziwei/calculators/HoroscopeStarsCalculator.js.map +1 -0
  183. package/dist/services/ziwei/calculators/HourlyCalculator.d.ts +41 -0
  184. package/dist/services/ziwei/calculators/HourlyCalculator.d.ts.map +1 -0
  185. package/dist/services/ziwei/calculators/HourlyCalculator.js +139 -0
  186. package/dist/services/ziwei/calculators/HourlyCalculator.js.map +1 -0
  187. package/dist/services/ziwei/calculators/MinorLimitCalculator.d.ts +30 -0
  188. package/dist/services/ziwei/calculators/MinorLimitCalculator.d.ts.map +1 -0
  189. package/dist/services/ziwei/calculators/MinorLimitCalculator.js +90 -0
  190. package/dist/services/ziwei/calculators/MinorLimitCalculator.js.map +1 -0
  191. package/dist/services/ziwei/calculators/MonthlyCalculator.d.ts +39 -0
  192. package/dist/services/ziwei/calculators/MonthlyCalculator.d.ts.map +1 -0
  193. package/dist/services/ziwei/calculators/MonthlyCalculator.js +128 -0
  194. package/dist/services/ziwei/calculators/MonthlyCalculator.js.map +1 -0
  195. package/dist/services/ziwei/calculators/MutagenCalculator.d.ts +20 -0
  196. package/dist/services/ziwei/calculators/MutagenCalculator.d.ts.map +1 -0
  197. package/dist/services/ziwei/calculators/MutagenCalculator.js +121 -0
  198. package/dist/services/ziwei/calculators/MutagenCalculator.js.map +1 -0
  199. package/dist/services/ziwei/calculators/PalaceDynamicCalculator.d.ts +35 -0
  200. package/dist/services/ziwei/calculators/PalaceDynamicCalculator.d.ts.map +1 -0
  201. package/dist/services/ziwei/calculators/PalaceDynamicCalculator.js +55 -0
  202. package/dist/services/ziwei/calculators/PalaceDynamicCalculator.js.map +1 -0
  203. package/dist/services/ziwei/calculators/YearlyCalculator.d.ts +35 -0
  204. package/dist/services/ziwei/calculators/YearlyCalculator.d.ts.map +1 -0
  205. package/dist/services/ziwei/calculators/YearlyCalculator.js +119 -0
  206. package/dist/services/ziwei/calculators/YearlyCalculator.js.map +1 -0
  207. package/dist/services/ziwei/index.d.ts +15 -0
  208. package/dist/services/ziwei/index.d.ts.map +1 -0
  209. package/dist/services/ziwei/index.js +48 -0
  210. package/dist/services/ziwei/index.js.map +1 -0
  211. package/dist/services/ziwei/transformers/PalaceTransformer.d.ts +62 -0
  212. package/dist/services/ziwei/transformers/PalaceTransformer.d.ts.map +1 -0
  213. package/dist/services/ziwei/transformers/PalaceTransformer.js +153 -0
  214. package/dist/services/ziwei/transformers/PalaceTransformer.js.map +1 -0
  215. package/dist/services/ziwei/types.d.ts +185 -0
  216. package/dist/services/ziwei/types.d.ts.map +1 -0
  217. package/dist/services/ziwei/types.js +54 -0
  218. package/dist/services/ziwei/types.js.map +1 -0
  219. package/dist/shared/logger.d.ts +51 -0
  220. package/dist/shared/logger.d.ts.map +1 -0
  221. package/dist/shared/logger.js +136 -0
  222. package/dist/shared/logger.js.map +1 -0
  223. package/dist/shared/types.d.ts +91 -0
  224. package/dist/shared/types.d.ts.map +1 -0
  225. package/dist/shared/types.js +7 -0
  226. package/dist/shared/types.js.map +1 -0
  227. package/dist/utils/ageCalculator.d.ts +30 -0
  228. package/dist/utils/ageCalculator.d.ts.map +1 -0
  229. package/dist/utils/ageCalculator.js +51 -0
  230. package/dist/utils/ageCalculator.js.map +1 -0
  231. package/package.json +54 -0
@@ -0,0 +1,1119 @@
1
+ "use strict";
2
+ /**
3
+ * Day Master Strength Analyzer
4
+ * Quantitative analysis of day master strength using 八字日主旺衰分析的确定性量化算法
5
+ * Based on 八字旺衰量化算法研究.md specification
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.StrengthAnalyzer = void 0;
9
+ const bazi_1 = require("../../../core/constants/bazi");
10
+ const lunar_javascript_1 = require("lunar-javascript");
11
+ // Total energy pool for the BaZi chart
12
+ const TOTAL_ENERGY = 360;
13
+ const MONTH_BRANCH_ENERGY = 180; // 50% to month branch
14
+ const OTHER_POSITIONS_ENERGY = 180; // 50% to other 7 positions
15
+ // Position weights for non-month positions
16
+ const POSITION_WEIGHTS = {
17
+ dayBranch: 40, // 日支
18
+ monthStem: 35, // 月干
19
+ hourStem: 30, // 时干
20
+ hourBranch: 25, // 时支
21
+ yearStem: 25, // 年干
22
+ yearBranch: 25 // 年支
23
+ };
24
+ const STRENGTH_CATEGORIES = [
25
+ { min: -360, max: -150, strength: '衰极', description: '衰极' },
26
+ { min: -149, max: -80, strength: '身弱', description: '身弱' },
27
+ { min: -79, max: -30, strength: '偏弱', description: '偏弱' },
28
+ { min: -29, max: 29, strength: '中和', description: '中和' },
29
+ { min: 30, max: 79, strength: '偏强', description: '偏强' },
30
+ { min: 80, max: 149, strength: '身旺', description: '身旺' },
31
+ { min: 150, max: 360, strength: '旺极', description: '旺极' }
32
+ ];
33
+ // Month branch commanding elements table
34
+ const MONTH_COMMANDING_ELEMENTS = {
35
+ '寅': [{ element: '戊', days: 7 }, { element: '丙', days: 7 }, { element: '甲', days: 16 }],
36
+ '卯': [{ element: '甲', days: 10 }, { element: '乙', days: 20 }],
37
+ '辰': [{ element: '乙', days: 9 }, { element: '癸', days: 3 }, { element: '戊', days: 18 }],
38
+ '巳': [{ element: '戊', days: 5 }, { element: '庚', days: 9 }, { element: '丙', days: 16 }],
39
+ '午': [{ element: '丙', days: 10 }, { element: '己', days: 9 }, { element: '丁', days: 11 }],
40
+ '未': [{ element: '丁', days: 9 }, { element: '乙', days: 3 }, { element: '己', days: 18 }],
41
+ '申': [{ element: '己', days: 7 }, { element: '壬', days: 7 }, { element: '庚', days: 16 }],
42
+ '酉': [{ element: '庚', days: 10 }, { element: '辛', days: 20 }],
43
+ '戌': [{ element: '辛', days: 9 }, { element: '丁', days: 3 }, { element: '戊', days: 18 }],
44
+ '亥': [{ element: '戊', days: 7 }, { element: '甲', days: 7 }, { element: '壬', days: 16 }],
45
+ '子': [{ element: '壬', days: 10 }, { element: '癸', days: 20 }],
46
+ '丑': [{ element: '癸', days: 9 }, { element: '辛', days: 3 }, { element: '己', days: 18 }]
47
+ };
48
+ // Distance decay coefficients
49
+ const DISTANCE_DECAY = {
50
+ adjacent: 1.0, // 相邻 (year-month, month-day, day-hour)
51
+ oneApart: 0.7, // 隔位 (year-day, month-hour)
52
+ twoApart: 0.5 // 遥隔 (year-hour)
53
+ };
54
+ class StrengthAnalyzer {
55
+ /**
56
+ * Analyze day master strength for components (synchronous)
57
+ * 使用360分量化算法计算日主旺衰
58
+ */
59
+ static analyzeDayMasterStrength(chart, birthInfo) {
60
+ const dayMaster = chart.day.stem;
61
+ const dayMasterElement = this.getStemElement(dayMaster);
62
+ // 详细的计算步骤记录
63
+ const detailedBreakdown = [];
64
+ // Step 1: 静态评分系统 (Static Scoring)
65
+ const staticScores = this.calculateStaticScores(chart, birthInfo, detailedBreakdown);
66
+ // Step 2: 动态计算引擎 (Dynamic Calculations)
67
+ const dynamicResult = this.applyDynamicCalculations(chart, staticScores, detailedBreakdown);
68
+ // Step 3: 计算最终旺衰分数 (Final Strength Score)
69
+ const finalScores = dynamicResult.scores;
70
+ const { supportingForce, opposingForce } = this.calculateForces(dayMasterElement, finalScores);
71
+ const finalStrengthScore = supportingForce - opposingForce;
72
+ // 确定强弱类别
73
+ const category = this.categorizeStrength(finalStrengthScore);
74
+ // 计算百分比(基于支持力量占总力量的比例)
75
+ const totalForce = supportingForce + opposingForce;
76
+ const percentage = totalForce > 0 ? Math.round((supportingForce / totalForce) * 100) : 50;
77
+ // 获取月令司令元素
78
+ const monthCommandingElement = this.getMonthCommandingElement(chart.month.branch, birthInfo);
79
+ // Calculate dynamic adjustments (difference between final and static)
80
+ const dynamicAdjustments = {
81
+ wood: finalScores.wood - staticScores.wood,
82
+ fire: finalScores.fire - staticScores.fire,
83
+ earth: finalScores.earth - staticScores.earth,
84
+ metal: finalScores.metal - staticScores.metal,
85
+ water: finalScores.water - staticScores.water
86
+ };
87
+ return {
88
+ strength: category.strength,
89
+ score: finalStrengthScore,
90
+ percentage,
91
+ factors: {
92
+ staticScores,
93
+ dynamicScores: dynamicAdjustments,
94
+ supportingForce,
95
+ opposingForce,
96
+ monthCommandingElement,
97
+ detailedBreakdown
98
+ },
99
+ analysis: this.generateAnalysis(category, finalStrengthScore, dayMasterElement),
100
+ suggestions: this.generateSuggestions(category.strength, dayMasterElement)
101
+ };
102
+ }
103
+ /**
104
+ * Analyze day master strength quantitatively
105
+ * Async wrapper for service layer compatibility
106
+ */
107
+ static async analyze(chart, birthInfo) {
108
+ const analysis = this.analyzeDayMasterStrength(chart, birthInfo);
109
+ // Generate characteristics based on strength category
110
+ const characteristics = this.generateCharacteristics(analysis.strength, analysis.score, analysis.factors);
111
+ // Convert detailed breakdown to the format expected by the type
112
+ const details = analysis.factors.detailedBreakdown.map(item => ({
113
+ category: this.categorizeBreakdownItem(item.item),
114
+ item: item.item,
115
+ score: item.baseScore,
116
+ adjustment: item.finalScore - item.baseScore,
117
+ adjustmentString: item.adjustment,
118
+ finalScore: item.finalScore
119
+ }));
120
+ return {
121
+ dayMasterStrength: analysis.strength,
122
+ totalScore: analysis.score,
123
+ breakdown: {
124
+ base: 0, // Not applicable in new algorithm
125
+ monthSupport: analysis.factors.monthCommandingElement ?
126
+ (analysis.factors.staticScores[this.getElementKey(analysis.factors.monthCommandingElement)] || 0) : 0,
127
+ stemSupport: analysis.factors.supportingForce,
128
+ branchSupport: 0, // Included in total scores
129
+ seasonalAdjustment: 0,
130
+ branchInteractionAdjustment: 0
131
+ },
132
+ percentage: analysis.percentage,
133
+ analysis: analysis.analysis,
134
+ details,
135
+ characteristics,
136
+ recommendations: analysis.suggestions
137
+ };
138
+ }
139
+ /**
140
+ * Calculate static scores for all elements
141
+ * 静态评分系统:建立基础能量值
142
+ */
143
+ static calculateStaticScores(chart, birthInfo, breakdown) {
144
+ const scores = {
145
+ wood: 0,
146
+ fire: 0,
147
+ earth: 0,
148
+ metal: 0,
149
+ water: 0
150
+ };
151
+ // 1. 月令司令元素得90分 (根据算法文档更新)
152
+ const monthBranch = chart.month.branch;
153
+ const commandingElement = this.getMonthCommandingElement(monthBranch, birthInfo);
154
+ const commandingElementKey = this.getElementKey(commandingElement);
155
+ const COMMANDING_ELEMENT_SCORE = 90; // 司令元素得90分
156
+ // 确保commandingElement有效
157
+ if (commandingElement && commandingElementKey) {
158
+ scores[commandingElementKey] += COMMANDING_ELEMENT_SCORE;
159
+ }
160
+ breakdown.push({
161
+ item: `月支${monthBranch}司令${commandingElement}`,
162
+ element: commandingElement,
163
+ baseScore: COMMANDING_ELEMENT_SCORE,
164
+ adjustment: '',
165
+ finalScore: COMMANDING_ELEMENT_SCORE
166
+ });
167
+ // 2. 剩余90分按月支藏干分配
168
+ const REMAINING_MONTH_SCORE = 90;
169
+ this.distributeHiddenStemScores(monthBranch, REMAINING_MONTH_SCORE, '月支', scores, breakdown);
170
+ // 3. 其他6个位置共享180分(月支已单独处理)
171
+ // Year stem
172
+ const yearStemElement = this.getStemElement(chart.year.stem);
173
+ const yearStemKey = this.getElementKey(yearStemElement);
174
+ scores[yearStemKey] += POSITION_WEIGHTS.yearStem;
175
+ breakdown.push({
176
+ item: `年干${chart.year.stem}`,
177
+ element: yearStemElement,
178
+ baseScore: POSITION_WEIGHTS.yearStem,
179
+ adjustment: '',
180
+ finalScore: POSITION_WEIGHTS.yearStem
181
+ });
182
+ // Year branch hidden stems
183
+ this.distributeHiddenStemScores(chart.year.branch, POSITION_WEIGHTS.yearBranch, '年支', scores, breakdown);
184
+ // Month stem - Skip if it's the same as month commanding element
185
+ const monthStemElement = this.getStemElement(chart.month.stem);
186
+ // Include month stem in calculations
187
+ // While the document examples might have omitted it, the month stem should
188
+ // receive its allocated 35 points as part of the comprehensive calculation
189
+ const includeMonthStem = true; // Include month stem for complete analysis
190
+ if (includeMonthStem) {
191
+ const monthStemKey = this.getElementKey(monthStemElement);
192
+ scores[monthStemKey] += POSITION_WEIGHTS.monthStem;
193
+ breakdown.push({
194
+ item: `月干${chart.month.stem}`,
195
+ element: monthStemElement,
196
+ baseScore: POSITION_WEIGHTS.monthStem,
197
+ adjustment: '',
198
+ finalScore: POSITION_WEIGHTS.monthStem
199
+ });
200
+ }
201
+ // Day branch hidden stems
202
+ this.distributeHiddenStemScores(chart.day.branch, POSITION_WEIGHTS.dayBranch, '日支', scores, breakdown);
203
+ // Hour stem
204
+ const hourStemElement = this.getStemElement(chart.hour.stem);
205
+ const hourStemKey = this.getElementKey(hourStemElement);
206
+ scores[hourStemKey] += POSITION_WEIGHTS.hourStem;
207
+ breakdown.push({
208
+ item: `时干${chart.hour.stem}`,
209
+ element: hourStemElement,
210
+ baseScore: POSITION_WEIGHTS.hourStem,
211
+ adjustment: '',
212
+ finalScore: POSITION_WEIGHTS.hourStem
213
+ });
214
+ // Hour branch hidden stems
215
+ this.distributeHiddenStemScores(chart.hour.branch, POSITION_WEIGHTS.hourBranch, '时支', scores, breakdown);
216
+ return scores;
217
+ }
218
+ /**
219
+ * Distribute scores among hidden stems in a branch
220
+ */
221
+ static distributeHiddenStemScores(branch, totalScore, position, scores, breakdown) {
222
+ const hiddenStems = bazi_1.HIDDEN_STEMS[branch];
223
+ if (!hiddenStems)
224
+ return;
225
+ hiddenStems.forEach(({ stem, power }) => {
226
+ const element = this.getStemElement(stem);
227
+ const elementKey = this.getElementKey(element);
228
+ const score = Math.round((totalScore || 0) * (power || 0));
229
+ if (!isNaN(score) && isFinite(score)) {
230
+ scores[elementKey] += score;
231
+ }
232
+ breakdown.push({
233
+ item: `${position}${branch}藏${stem}`,
234
+ element,
235
+ baseScore: !isNaN(score) && isFinite(score) ? score : 0,
236
+ adjustment: '',
237
+ finalScore: !isNaN(score) && isFinite(score) ? score : 0
238
+ });
239
+ });
240
+ }
241
+ /**
242
+ * Apply dynamic calculations including combinations, clashes, etc.
243
+ * 动态计算引擎:基于规则的分值修正系统
244
+ */
245
+ static applyDynamicCalculations(chart, staticScores, breakdown) {
246
+ // Clone scores for modification
247
+ const scores = { ...staticScores };
248
+ const adjustments = [];
249
+ // Priority order: 三会 > 三合 > 六合 > 冲 > 刑 > 害 > 生克
250
+ // 1. Check Three Meetings (三会局)
251
+ this.checkThreeMeetings(chart, scores, adjustments, breakdown);
252
+ // 2. Check Three Harmonies (三合局)
253
+ this.checkThreeHarmonies(chart, scores, adjustments, breakdown);
254
+ // 3. Check Six Combinations (六合)
255
+ this.checkSixCombinations(chart, scores, adjustments, breakdown);
256
+ // 4. Check Clashes (冲)
257
+ this.checkClashes(chart, scores, adjustments, breakdown);
258
+ // 5. Check Punishments (刑)
259
+ this.checkPunishments(chart, scores, adjustments, breakdown);
260
+ // 6. Check Harms (害)
261
+ this.checkHarms(chart, scores, adjustments, breakdown);
262
+ // 7. Apply Generating and Overcoming (生克)
263
+ // NOTE: Temporarily disabled - this method is incorrectly reducing all scores
264
+ // this.applyGeneratingOvercoming(chart, scores, adjustments, breakdown);
265
+ return { scores, adjustments };
266
+ }
267
+ /**
268
+ * Calculate supporting and opposing forces
269
+ * 阵营划分:我党vs异党
270
+ */
271
+ static calculateForces(dayMasterElement, scores) {
272
+ const dayMasterKey = this.getElementKey(dayMasterElement);
273
+ const generatingKey = this.getElementKey(this.getGeneratingElement(dayMasterElement));
274
+ // 我党: 比劫(same) + 印枭(generating)
275
+ const supportingForce = (scores[dayMasterKey] || 0) + (scores[generatingKey] || 0);
276
+ // 异党: 官杀(controlling) + 食伤(generated) + 财才(controlled)
277
+ const controllingKey = this.getElementKey(this.getControllingElement(dayMasterElement));
278
+ const generatedKey = this.getElementKey(this.getGeneratedElement(dayMasterElement));
279
+ const controlledKey = this.getElementKey(this.getControlledElement(dayMasterElement));
280
+ const opposingForce = (scores[controllingKey] || 0) + (scores[generatedKey] || 0) + (scores[controlledKey] || 0);
281
+ // 确保返回值不是NaN
282
+ return {
283
+ supportingForce: isNaN(supportingForce) ? 0 : supportingForce,
284
+ opposingForce: isNaN(opposingForce) ? 0 : opposingForce
285
+ };
286
+ }
287
+ /**
288
+ * Get the commanding element for a month branch
289
+ */
290
+ static getMonthCommandingElement(monthBranch, birthInfo) {
291
+ // If we have birth info, calculate the exact commanding element based on days
292
+ if (birthInfo) {
293
+ try {
294
+ let solar;
295
+ // 兼容两种格式的birthInfo
296
+ if (birthInfo.solar instanceof Date) {
297
+ // Core格式:包含solar Date对象
298
+ const date = birthInfo.solar;
299
+ solar = lunar_javascript_1.Solar.fromYmdHms(date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds());
300
+ }
301
+ else if (birthInfo.year && birthInfo.month && birthInfo.day) {
302
+ // Service格式:包含具体日期字段
303
+ solar = lunar_javascript_1.Solar.fromYmdHms(birthInfo.year, birthInfo.month, birthInfo.day, birthInfo.hour || 0, birthInfo.minute || 0, 0);
304
+ }
305
+ else {
306
+ // 格式不可识别,使用当前月支
307
+ return monthBranch + '司令';
308
+ }
309
+ const lunar = solar.getLunar();
310
+ // Get the previous Jie (major solar term) that marks the start of this month
311
+ const prevJie = lunar.getPrevJie();
312
+ if (prevJie) {
313
+ // Calculate days from the start of the solar term
314
+ const termDate = prevJie.getSolar();
315
+ const currentDate = new Date(solar.getYear(), solar.getMonth() - 1, solar.getDay());
316
+ const termDateObj = new Date(termDate.getYear(), termDate.getMonth() - 1, termDate.getDay());
317
+ const daysSinceTermStart = Math.floor((currentDate.getTime() - termDateObj.getTime()) / (1000 * 60 * 60 * 24));
318
+ // Get commanding elements for this month
319
+ const commanders = MONTH_COMMANDING_ELEMENTS[monthBranch];
320
+ if (commanders && commanders.length > 0) {
321
+ // Find which element is commanding based on accumulated days
322
+ let accumulatedDays = 0;
323
+ for (const commander of commanders) {
324
+ accumulatedDays += commander.days;
325
+ if (daysSinceTermStart < accumulatedDays) {
326
+ return commander.element;
327
+ }
328
+ }
329
+ // If we've gone past all defined days, return the last element
330
+ return commanders[commanders.length - 1].element;
331
+ }
332
+ }
333
+ }
334
+ catch (error) {
335
+ // Fallback to default method if calculation fails
336
+ }
337
+ }
338
+ // Default: use the last element which usually has the most days
339
+ const commanders = MONTH_COMMANDING_ELEMENTS[monthBranch];
340
+ if (commanders && commanders.length > 0) {
341
+ return commanders[commanders.length - 1].element;
342
+ }
343
+ // Final fallback: use main qi of branch
344
+ const hiddenStems = bazi_1.HIDDEN_STEMS[monthBranch];
345
+ if (hiddenStems && hiddenStems.length > 0) {
346
+ return hiddenStems[0].stem;
347
+ }
348
+ return '戊'; // Default to earth if not found
349
+ }
350
+ /**
351
+ * Check for Three Meetings (三会局)
352
+ */
353
+ static checkThreeMeetings(chart, scores, adjustments, breakdown) {
354
+ const branches = [
355
+ chart.year.branch,
356
+ chart.month.branch,
357
+ chart.day.branch,
358
+ chart.hour.branch
359
+ ];
360
+ const meetings = [
361
+ { branches: ['亥', '子', '丑'], element: '水', name: '北方水局' },
362
+ { branches: ['寅', '卯', '辰'], element: '木', name: '东方木局' },
363
+ { branches: ['巳', '午', '未'], element: '火', name: '南方火局' },
364
+ { branches: ['申', '酉', '戌'], element: '金', name: '西方金局' }
365
+ ];
366
+ for (const meeting of meetings) {
367
+ const foundBranches = meeting.branches.filter(b => branches.includes(b));
368
+ if (foundBranches.length === 3) {
369
+ // Three meeting found - transform all participating branches' energy
370
+ const totalEnergy = this.calculateBranchEnergy(foundBranches, chart, scores);
371
+ // Clear original element scores for participating branches
372
+ this.clearBranchScores(foundBranches, chart, scores);
373
+ // Add total energy to the meeting element
374
+ scores[meeting.element] += totalEnergy;
375
+ adjustments.push({
376
+ type: '三会局',
377
+ description: `${meeting.name}成功,${foundBranches.join('、')}化为${this.getElementName(meeting.element)}`,
378
+ scoreChange: totalEnergy
379
+ });
380
+ }
381
+ }
382
+ }
383
+ /**
384
+ * Check for Three Harmonies (三合局)
385
+ */
386
+ static checkThreeHarmonies(chart, scores, adjustments, breakdown) {
387
+ const branches = [
388
+ chart.year.branch,
389
+ chart.month.branch,
390
+ chart.day.branch,
391
+ chart.hour.branch
392
+ ];
393
+ const harmonies = [
394
+ { branches: ['申', '子', '辰'], element: '水', name: '水局' },
395
+ { branches: ['亥', '卯', '未'], element: '木', name: '木局' },
396
+ { branches: ['寅', '午', '戌'], element: '火', name: '火局' },
397
+ { branches: ['巳', '酉', '丑'], element: '金', name: '金局' }
398
+ ];
399
+ for (const harmony of harmonies) {
400
+ const foundBranches = harmony.branches.filter(b => branches.includes(b));
401
+ if (foundBranches.length === 3) {
402
+ // Check if harmony can transform (needs seasonal support and stem revelation)
403
+ const canTransform = this.checkHarmonyTransformation(harmony.element, chart);
404
+ if (canTransform) {
405
+ // Transform all energy to the harmony element
406
+ const totalEnergy = this.calculateBranchEnergy(foundBranches, chart, scores);
407
+ this.clearBranchScores(foundBranches, chart, scores);
408
+ scores[harmony.element] += totalEnergy;
409
+ adjustments.push({
410
+ type: '三合化局',
411
+ description: `${harmony.name}化成功,${foundBranches.join('、')}化为${this.getElementName(harmony.element)}`,
412
+ scoreChange: totalEnergy
413
+ });
414
+ }
415
+ else {
416
+ // Just increase affinity, no transformation
417
+ foundBranches.forEach(branch => {
418
+ const branchScores = this.getBranchElementScores(branch, chart, scores);
419
+ Object.entries(branchScores).forEach(([element, score]) => {
420
+ const increase = Math.round(score * 0.3);
421
+ scores[element] += increase;
422
+ });
423
+ });
424
+ adjustments.push({
425
+ type: '三合不化',
426
+ description: `${harmony.name}合而不化,各增力30%`,
427
+ scoreChange: 0
428
+ });
429
+ }
430
+ }
431
+ }
432
+ }
433
+ /**
434
+ * Check for Six Combinations (六合)
435
+ */
436
+ static checkSixCombinations(chart, scores, adjustments, breakdown) {
437
+ const combinations = [
438
+ { pair: ['子', '丑'], result: '土' },
439
+ { pair: ['寅', '亥'], result: '木' },
440
+ { pair: ['卯', '戌'], result: '火' },
441
+ { pair: ['辰', '酉'], result: '金' },
442
+ { pair: ['巳', '申'], result: '水' },
443
+ { pair: ['午', '未'], result: '土' }
444
+ ];
445
+ const positions = [
446
+ { branch: chart.year.branch, name: '年支' },
447
+ { branch: chart.month.branch, name: '月支' },
448
+ { branch: chart.day.branch, name: '日支' },
449
+ { branch: chart.hour.branch, name: '时支' }
450
+ ];
451
+ // Check adjacent positions only
452
+ for (let i = 0; i < positions.length - 1; i++) {
453
+ const branch1 = positions[i].branch;
454
+ const branch2 = positions[i + 1].branch;
455
+ for (const combo of combinations) {
456
+ if ((combo.pair[0] === branch1 && combo.pair[1] === branch2) ||
457
+ (combo.pair[1] === branch1 && combo.pair[0] === branch2)) {
458
+ // Found a six combination
459
+ const canTransform = this.checkCombinationTransformation(combo.result, chart);
460
+ if (canTransform) {
461
+ // Transform both branches to the result element
462
+ const energy1 = this.getBranchTotalScore(branch1, chart, scores);
463
+ const energy2 = this.getBranchTotalScore(branch2, chart, scores);
464
+ this.clearBranchScores([branch1, branch2], chart, scores);
465
+ scores[combo.result] += energy1 + energy2;
466
+ adjustments.push({
467
+ type: '六合化',
468
+ description: `${branch1}${branch2}合化${this.getElementName(combo.result)}`,
469
+ scoreChange: energy1 + energy2
470
+ });
471
+ }
472
+ else {
473
+ // Just increase affinity
474
+ adjustments.push({
475
+ type: '六合不化',
476
+ description: `${branch1}${branch2}合而不化,各增力20%`,
477
+ scoreChange: 0
478
+ });
479
+ }
480
+ }
481
+ }
482
+ }
483
+ }
484
+ /**
485
+ * Check for Clashes (冲)
486
+ */
487
+ static checkClashes(chart, scores, adjustments, breakdown) {
488
+ const clashes = [
489
+ ['子', '午'], ['丑', '未'], ['寅', '申'],
490
+ ['卯', '酉'], ['辰', '戌'], ['巳', '亥']
491
+ ];
492
+ const positions = [
493
+ { branch: chart.year.branch, name: '年支', index: 0 },
494
+ { branch: chart.month.branch, name: '月支', index: 1 },
495
+ { branch: chart.day.branch, name: '日支', index: 2 },
496
+ { branch: chart.hour.branch, name: '时支', index: 3 }
497
+ ];
498
+ for (let i = 0; i < positions.length; i++) {
499
+ for (let j = i + 1; j < positions.length; j++) {
500
+ const pos1 = positions[i];
501
+ const pos2 = positions[j];
502
+ for (const clash of clashes) {
503
+ if ((clash[0] === pos1.branch && clash[1] === pos2.branch) ||
504
+ (clash[1] === pos1.branch && clash[0] === pos2.branch)) {
505
+ // Apply clash with distance decay
506
+ const distance = Math.abs(pos1.index - pos2.index);
507
+ const decay = distance === 1 ? DISTANCE_DECAY.adjacent :
508
+ distance === 2 ? DISTANCE_DECAY.oneApart :
509
+ DISTANCE_DECAY.twoApart;
510
+ // Reduce scores by 50% * decay
511
+ const reduction = 0.5 * decay;
512
+ this.reduceBranchScores(pos1.branch, scores, reduction, chart);
513
+ this.reduceBranchScores(pos2.branch, scores, reduction, chart);
514
+ adjustments.push({
515
+ type: '地支相冲',
516
+ description: `${pos1.name}${pos1.branch}与${pos2.name}${pos2.branch}相冲`,
517
+ scoreChange: 0
518
+ });
519
+ }
520
+ }
521
+ }
522
+ }
523
+ }
524
+ /**
525
+ * Check for Punishments (刑)
526
+ */
527
+ static checkPunishments(chart, scores, adjustments, breakdown) {
528
+ const positions = [
529
+ { branch: chart.year.branch, name: '年支', index: 0 },
530
+ { branch: chart.month.branch, name: '月支', index: 1 },
531
+ { branch: chart.day.branch, name: '日支', index: 2 },
532
+ { branch: chart.hour.branch, name: '时支', index: 3 }
533
+ ];
534
+ // Self punishments (自刑)
535
+ const selfPunishments = ['辰', '午', '酉', '亥'];
536
+ const branchCounts = {};
537
+ positions.forEach(pos => {
538
+ branchCounts[pos.branch] = (branchCounts[pos.branch] || 0) + 1;
539
+ });
540
+ // Check for self punishments (two of same branch)
541
+ Object.entries(branchCounts).forEach(([branch, count]) => {
542
+ if (count >= 2 && selfPunishments.includes(branch)) {
543
+ // Self punishment - reduce by 30%
544
+ this.reduceBranchScores(branch, scores, 0.3, chart);
545
+ adjustments.push({
546
+ type: '自刑',
547
+ description: `${branch}${branch}自刑`,
548
+ scoreChange: 0
549
+ });
550
+ }
551
+ });
552
+ // Other punishments
553
+ const punishments = [
554
+ // 无恩之刑
555
+ ['子', '卯'],
556
+ // 恃势之刑 (任意两个都算)
557
+ ['寅', '巳'], ['巳', '申'], ['寅', '申'],
558
+ // 无礼之刑 (任意两个都算)
559
+ ['丑', '戌'], ['戌', '未'], ['丑', '未']
560
+ ];
561
+ // Check all positions for punishments
562
+ for (let i = 0; i < positions.length; i++) {
563
+ for (let j = i + 1; j < positions.length; j++) {
564
+ const pos1 = positions[i];
565
+ const pos2 = positions[j];
566
+ for (const punishment of punishments) {
567
+ if ((punishment[0] === pos1.branch && punishment[1] === pos2.branch) ||
568
+ (punishment[1] === pos1.branch && punishment[0] === pos2.branch)) {
569
+ // Apply punishment with distance decay
570
+ const distance = Math.abs(pos1.index - pos2.index);
571
+ const decay = distance === 1 ? DISTANCE_DECAY.adjacent :
572
+ distance === 2 ? DISTANCE_DECAY.oneApart :
573
+ DISTANCE_DECAY.twoApart;
574
+ // Determine punishment type and reduction
575
+ let reduction = 0.3; // Default 30%
576
+ let punishmentType = '相刑';
577
+ if ((pos1.branch === '子' && pos2.branch === '卯') ||
578
+ (pos1.branch === '卯' && pos2.branch === '子')) {
579
+ punishmentType = '无恩之刑';
580
+ reduction = 0.35;
581
+ }
582
+ else if (['寅', '巳', '申'].includes(pos1.branch) &&
583
+ ['寅', '巳', '申'].includes(pos2.branch)) {
584
+ punishmentType = '恃势之刑';
585
+ reduction = 0.4;
586
+ }
587
+ else if (['丑', '戌', '未'].includes(pos1.branch) &&
588
+ ['丑', '戌', '未'].includes(pos2.branch)) {
589
+ punishmentType = '无礼之刑';
590
+ reduction = 0.4;
591
+ }
592
+ // Apply reduction with decay
593
+ const finalReduction = reduction * decay;
594
+ this.reduceBranchScores(pos1.branch, scores, finalReduction, chart);
595
+ this.reduceBranchScores(pos2.branch, scores, finalReduction, chart);
596
+ adjustments.push({
597
+ type: punishmentType,
598
+ description: `${pos1.name}${pos1.branch}与${pos2.name}${pos2.branch}${punishmentType}`,
599
+ scoreChange: 0
600
+ });
601
+ }
602
+ }
603
+ }
604
+ }
605
+ }
606
+ /**
607
+ * Check for Harms (害)
608
+ */
609
+ static checkHarms(chart, scores, adjustments, breakdown) {
610
+ const harms = [
611
+ ['子', '未'], ['丑', '午'], ['寅', '巳'],
612
+ ['卯', '辰'], ['申', '亥'], ['酉', '戌']
613
+ ];
614
+ const positions = [
615
+ { branch: chart.year.branch, name: '年支', index: 0 },
616
+ { branch: chart.month.branch, name: '月支', index: 1 },
617
+ { branch: chart.day.branch, name: '日支', index: 2 },
618
+ { branch: chart.hour.branch, name: '时支', index: 3 }
619
+ ];
620
+ // Check all positions for harms
621
+ for (let i = 0; i < positions.length; i++) {
622
+ for (let j = i + 1; j < positions.length; j++) {
623
+ const pos1 = positions[i];
624
+ const pos2 = positions[j];
625
+ for (const harm of harms) {
626
+ if ((harm[0] === pos1.branch && harm[1] === pos2.branch) ||
627
+ (harm[1] === pos1.branch && harm[0] === pos2.branch)) {
628
+ // Apply harm with distance decay
629
+ const distance = Math.abs(pos1.index - pos2.index);
630
+ const decay = distance === 1 ? DISTANCE_DECAY.adjacent :
631
+ distance === 2 ? DISTANCE_DECAY.oneApart :
632
+ DISTANCE_DECAY.twoApart;
633
+ // Harm reduces by 20% with decay
634
+ const reduction = 0.2 * decay;
635
+ this.reduceBranchScores(pos1.branch, scores, reduction, chart);
636
+ this.reduceBranchScores(pos2.branch, scores, reduction, chart);
637
+ adjustments.push({
638
+ type: '地支相害',
639
+ description: `${pos1.name}${pos1.branch}与${pos2.name}${pos2.branch}相害`,
640
+ scoreChange: 0
641
+ });
642
+ }
643
+ }
644
+ }
645
+ }
646
+ }
647
+ /**
648
+ * Apply generating and overcoming cycles
649
+ */
650
+ static applyGeneratingOvercoming(chart, scores, adjustments, breakdown) {
651
+ // Basic five element interactions (生克)
652
+ // This is the lowest priority and applies to remaining energy
653
+ const elements = ['wood', 'fire', 'earth', 'metal', 'water'];
654
+ const elementNames = { wood: '木', fire: '火', earth: '土', metal: '金', water: '水' };
655
+ // Apply generating cycle (生) - 10% transfer
656
+ elements.forEach(element => {
657
+ const generated = this.getElementKey(this.getGeneratedElement(elementNames[element]));
658
+ if (scores[element] > 0 && generated !== element) {
659
+ const transfer = Math.round(scores[element] * 0.1);
660
+ scores[element] -= transfer;
661
+ scores[generated] += transfer;
662
+ if (transfer > 0) {
663
+ adjustments.push({
664
+ type: '五行相生',
665
+ description: `${elementNames[element]}生${elementNames[generated]}`,
666
+ scoreChange: transfer
667
+ });
668
+ }
669
+ }
670
+ });
671
+ // Apply overcoming cycle (克) - 20% reduction
672
+ elements.forEach(element => {
673
+ const controlled = this.getElementKey(this.getControlledElement(elementNames[element]));
674
+ if (scores[element] > 0 && controlled !== element) {
675
+ const reduction = Math.round(scores[controlled] * 0.2);
676
+ scores[controlled] = Math.max(0, scores[controlled] - reduction);
677
+ if (reduction > 0) {
678
+ adjustments.push({
679
+ type: '五行相克',
680
+ description: `${elementNames[element]}克${elementNames[controlled]}`,
681
+ scoreChange: -reduction
682
+ });
683
+ }
684
+ }
685
+ });
686
+ }
687
+ /**
688
+ * Helper method to get element key from Chinese name
689
+ */
690
+ static getElementKey(element) {
691
+ const map = {
692
+ '木': 'wood',
693
+ '火': 'fire',
694
+ '土': 'earth',
695
+ '金': 'metal',
696
+ '水': 'water',
697
+ '甲': 'wood', '乙': 'wood',
698
+ '丙': 'fire', '丁': 'fire',
699
+ '戊': 'earth', '己': 'earth',
700
+ '庚': 'metal', '辛': 'metal',
701
+ '壬': 'water', '癸': 'water'
702
+ };
703
+ return map[element] || 'earth';
704
+ }
705
+ /**
706
+ * Helper method to get Chinese element name
707
+ */
708
+ static getElementName(element) {
709
+ const map = {
710
+ '木': '木',
711
+ '火': '火',
712
+ '土': '土',
713
+ '金': '金',
714
+ '水': '水'
715
+ };
716
+ return map[element] || element;
717
+ }
718
+ /**
719
+ * Get generating element (the one that generates this element)
720
+ */
721
+ static getGeneratingElement(element) {
722
+ const map = {
723
+ '木': '水',
724
+ '火': '木',
725
+ '土': '火',
726
+ '金': '土',
727
+ '水': '金'
728
+ };
729
+ return map[element] || '土';
730
+ }
731
+ /**
732
+ * Get controlling element (the one that controls this element)
733
+ */
734
+ static getControllingElement(element) {
735
+ const map = {
736
+ '木': '金',
737
+ '火': '水',
738
+ '土': '木',
739
+ '金': '火',
740
+ '水': '土'
741
+ };
742
+ return map[element] || '木';
743
+ }
744
+ /**
745
+ * Get generated element (the one this element generates)
746
+ */
747
+ static getGeneratedElement(element) {
748
+ const map = {
749
+ '木': '火',
750
+ '火': '土',
751
+ '土': '金',
752
+ '金': '水',
753
+ '水': '木'
754
+ };
755
+ return map[element] || '金';
756
+ }
757
+ /**
758
+ * Get controlled element (the one this element controls)
759
+ */
760
+ static getControlledElement(element) {
761
+ const map = {
762
+ '木': '土',
763
+ '火': '金',
764
+ '土': '水',
765
+ '金': '木',
766
+ '水': '火'
767
+ };
768
+ return map[element] || '水';
769
+ }
770
+ /**
771
+ * Calculate total energy in branches
772
+ */
773
+ static calculateBranchEnergy(branches, chart, scores) {
774
+ let totalEnergy = 0;
775
+ // Get positions of branches in chart
776
+ const positions = [
777
+ { branch: chart.year.branch, weight: POSITION_WEIGHTS.yearBranch },
778
+ { branch: chart.month.branch, weight: 0 }, // Month branch handled separately
779
+ { branch: chart.day.branch, weight: POSITION_WEIGHTS.dayBranch },
780
+ { branch: chart.hour.branch, weight: POSITION_WEIGHTS.hourBranch }
781
+ ];
782
+ branches.forEach(targetBranch => {
783
+ // Find this branch in the chart
784
+ const pos = positions.find(p => p.branch === targetBranch);
785
+ if (pos && pos.weight > 0) {
786
+ // Get energy from hidden stems
787
+ const hiddenStems = bazi_1.HIDDEN_STEMS[targetBranch];
788
+ if (hiddenStems) {
789
+ hiddenStems.forEach(({ stem, power }) => {
790
+ const element = this.getStemElement(stem);
791
+ const elementKey = this.getElementKey(element);
792
+ const energy = Math.round(pos.weight * power);
793
+ totalEnergy += energy;
794
+ });
795
+ }
796
+ }
797
+ });
798
+ return totalEnergy;
799
+ }
800
+ /**
801
+ * Clear scores for specified branches
802
+ */
803
+ static clearBranchScores(branches, chart, scores) {
804
+ branches.forEach(targetBranch => {
805
+ // Clear scores from hidden stems of this branch
806
+ const hiddenStems = bazi_1.HIDDEN_STEMS[targetBranch];
807
+ if (hiddenStems) {
808
+ hiddenStems.forEach(({ stem }) => {
809
+ const element = this.getStemElement(stem);
810
+ const elementKey = this.getElementKey(element);
811
+ // Find how much this branch contributed and subtract it
812
+ // This is a simplified approach - in reality we'd need to track contributions
813
+ const positions = [
814
+ { branch: chart.year.branch, weight: POSITION_WEIGHTS.yearBranch },
815
+ { branch: chart.day.branch, weight: POSITION_WEIGHTS.dayBranch },
816
+ { branch: chart.hour.branch, weight: POSITION_WEIGHTS.hourBranch }
817
+ ];
818
+ const pos = positions.find(p => p.branch === targetBranch);
819
+ if (pos) {
820
+ hiddenStems.forEach(({ stem: hs, power }) => {
821
+ if (hs === stem) {
822
+ const deduction = Math.round(pos.weight * power);
823
+ scores[elementKey] = Math.max(0, scores[elementKey] - deduction);
824
+ }
825
+ });
826
+ }
827
+ });
828
+ }
829
+ });
830
+ }
831
+ /**
832
+ * Get branch element scores
833
+ */
834
+ static getBranchElementScores(branch, chart, scores) {
835
+ const result = {};
836
+ // Get position weight
837
+ let weight = 0;
838
+ if (chart.year.branch === branch)
839
+ weight = POSITION_WEIGHTS.yearBranch;
840
+ else if (chart.day.branch === branch)
841
+ weight = POSITION_WEIGHTS.dayBranch;
842
+ else if (chart.hour.branch === branch)
843
+ weight = POSITION_WEIGHTS.hourBranch;
844
+ else if (chart.month.branch === branch)
845
+ weight = MONTH_BRANCH_ENERGY;
846
+ // Get scores from hidden stems
847
+ const hiddenStems = bazi_1.HIDDEN_STEMS[branch];
848
+ if (hiddenStems && weight > 0) {
849
+ hiddenStems.forEach(({ stem, power }) => {
850
+ const element = this.getStemElement(stem);
851
+ const elementKey = this.getElementKey(element);
852
+ result[elementKey] = Math.round(weight * power);
853
+ });
854
+ }
855
+ return result;
856
+ }
857
+ /**
858
+ * Get total score for a branch
859
+ */
860
+ static getBranchTotalScore(branch, chart, scores) {
861
+ const branchScores = this.getBranchElementScores(branch, chart, scores);
862
+ return Object.values(branchScores).reduce((sum, score) => sum + score, 0);
863
+ }
864
+ /**
865
+ * Reduce branch scores by percentage
866
+ */
867
+ static reduceBranchScores(branch, scores, percentage, chart) {
868
+ // Find the position weight for this branch
869
+ let positionWeight = 0;
870
+ if (chart.year.branch === branch) {
871
+ positionWeight = POSITION_WEIGHTS.yearBranch;
872
+ }
873
+ else if (chart.day.branch === branch) {
874
+ positionWeight = POSITION_WEIGHTS.dayBranch;
875
+ }
876
+ else if (chart.hour.branch === branch) {
877
+ positionWeight = POSITION_WEIGHTS.hourBranch;
878
+ }
879
+ else if (chart.month.branch === branch) {
880
+ // Month branch has special handling - it gets the commanding element's energy
881
+ positionWeight = MONTH_BRANCH_ENERGY;
882
+ }
883
+ // Apply reduction only to the contribution from this specific branch
884
+ const hiddenStems = bazi_1.HIDDEN_STEMS[branch];
885
+ if (hiddenStems && positionWeight > 0) {
886
+ hiddenStems.forEach(({ stem, power }) => {
887
+ const element = this.getStemElement(stem);
888
+ const elementKey = this.getElementKey(element);
889
+ // Calculate the original contribution from this branch
890
+ const originalContribution = Math.round(positionWeight * power);
891
+ const reduction = Math.round(originalContribution * percentage);
892
+ // Reduce the element score by the calculated reduction
893
+ scores[elementKey] = Math.max(0, scores[elementKey] - reduction);
894
+ });
895
+ }
896
+ }
897
+ /**
898
+ * Check if harmony can transform
899
+ */
900
+ static checkHarmonyTransformation(element, chart) {
901
+ // Check 1: Seasonal support (得时)
902
+ const monthBranch = chart.month.branch;
903
+ const seasonSupport = this.hasSeasonalSupport(element, monthBranch);
904
+ // Check 2: Stem revelation (透干)
905
+ const stemRevelation = this.hasStemRevelation(element, chart);
906
+ // Both conditions must be met
907
+ return seasonSupport && stemRevelation;
908
+ }
909
+ /**
910
+ * Check if combination can transform
911
+ */
912
+ static checkCombinationTransformation(element, chart) {
913
+ // Similar to harmony transformation
914
+ return this.checkHarmonyTransformation(element, chart);
915
+ }
916
+ /**
917
+ * Check if element has seasonal support
918
+ */
919
+ static hasSeasonalSupport(element, monthBranch) {
920
+ // Seasonal support mapping
921
+ const seasonalSupport = {
922
+ '木': ['寅', '卯', '辰'], // Spring supports wood
923
+ '火': ['巳', '午', '未'], // Summer supports fire
924
+ '金': ['申', '酉', '戌'], // Autumn supports metal
925
+ '水': ['亥', '子', '丑'], // Winter supports water
926
+ '土': ['辰', '未', '戌', '丑'] // Four seasons support earth
927
+ };
928
+ return seasonalSupport[element]?.includes(monthBranch) || false;
929
+ }
930
+ /**
931
+ * Check if element is revealed in stems
932
+ */
933
+ static hasStemRevelation(element, chart) {
934
+ const stems = [
935
+ chart.year.stem,
936
+ chart.month.stem,
937
+ chart.day.stem,
938
+ chart.hour.stem
939
+ ];
940
+ // Check if any stem has the same element
941
+ return stems.some(stem => {
942
+ const stemElement = this.getStemElement(stem);
943
+ return this.getElementKey(stemElement) === element;
944
+ });
945
+ }
946
+ /**
947
+ * Categorize strength based on score
948
+ */
949
+ static categorizeStrength(score) {
950
+ for (const category of STRENGTH_CATEGORIES) {
951
+ if (score >= category.min && score <= category.max) {
952
+ return category;
953
+ }
954
+ }
955
+ return STRENGTH_CATEGORIES.find(c => c.strength === '中和');
956
+ }
957
+ /**
958
+ * Generate analysis text
959
+ */
960
+ static generateAnalysis(category, score, element) {
961
+ const scoreDesc = `${score}分`;
962
+ const categoryDesc = category.description;
963
+ const analysisMap = {
964
+ '衰极': {
965
+ '木': `日主木${categoryDesc}(${scoreDesc}),可考虑从弱,需要金火土的制化`,
966
+ '火': `日主火${categoryDesc}(${scoreDesc}),可考虑从弱,需要水土金的制化`,
967
+ '土': `日主土${categoryDesc}(${scoreDesc}),可考虑从弱,需要木水的制化`,
968
+ '金': `日主金${categoryDesc}(${scoreDesc}),可考虑从弱,需要火水木的制化`,
969
+ '水': `日主水${categoryDesc}(${scoreDesc}),可考虑从弱,需要土火木的制化`
970
+ },
971
+ '身弱': {
972
+ '木': `日主木${categoryDesc}(${scoreDesc}),需要大量水木生扶`,
973
+ '火': `日主火${categoryDesc}(${scoreDesc}),需要大量木火生扶`,
974
+ '土': `日主土${categoryDesc}(${scoreDesc}),需要大量火土生扶`,
975
+ '金': `日主金${categoryDesc}(${scoreDesc}),需要大量土金生扶`,
976
+ '水': `日主水${categoryDesc}(${scoreDesc}),需要大量金水生扶`
977
+ },
978
+ '偏弱': {
979
+ '木': `日主木${categoryDesc}(${scoreDesc}),喜水生木助,忌金克火泄`,
980
+ '火': `日主火${categoryDesc}(${scoreDesc}),喜木生火助,忌水克土泄`,
981
+ '土': `日主土${categoryDesc}(${scoreDesc}),喜火生土助,忌木克金泄`,
982
+ '金': `日主金${categoryDesc}(${scoreDesc}),喜土生金助,忌火克水泄`,
983
+ '水': `日主水${categoryDesc}(${scoreDesc}),喜金生水助,忌土克木泄`
984
+ },
985
+ '中和': {
986
+ '木': `日主木${categoryDesc}(${scoreDesc}),五行平衡,宜维持现状`,
987
+ '火': `日主火${categoryDesc}(${scoreDesc}),五行平衡,宜维持现状`,
988
+ '土': `日主土${categoryDesc}(${scoreDesc}),五行平衡,宜维持现状`,
989
+ '金': `日主金${categoryDesc}(${scoreDesc}),五行平衡,宜维持现状`,
990
+ '水': `日主水${categoryDesc}(${scoreDesc}),五行平衡,宜维持现状`
991
+ },
992
+ '偏强': {
993
+ '木': `日主木${categoryDesc}(${scoreDesc}),需要金克火泄土耗`,
994
+ '火': `日主火${categoryDesc}(${scoreDesc}),需要水克土泄金耗`,
995
+ '土': `日主土${categoryDesc}(${scoreDesc}),需要木克金泄水耗`,
996
+ '金': `日主金${categoryDesc}(${scoreDesc}),需要火克水泄木耗`,
997
+ '水': `日主水${categoryDesc}(${scoreDesc}),需要土克木泄火耗`
998
+ },
999
+ '身旺': {
1000
+ '木': `日主木${categoryDesc}(${scoreDesc}),宜泄不宜克,火土为用`,
1001
+ '火': `日主火${categoryDesc}(${scoreDesc}),宜泄不宜克,土金为用`,
1002
+ '土': `日主土${categoryDesc}(${scoreDesc}),宜泄不宜克,金水为用`,
1003
+ '金': `日主金${categoryDesc}(${scoreDesc}),宜泄不宜克,水木为用`,
1004
+ '水': `日主水${categoryDesc}(${scoreDesc}),宜泄不宜克,木火为用`
1005
+ },
1006
+ '旺极': {
1007
+ '木': `日主木${categoryDesc}(${scoreDesc}),一气专旺,顺势而为`,
1008
+ '火': `日主火${categoryDesc}(${scoreDesc}),一气专旺,顺势而为`,
1009
+ '土': `日主土${categoryDesc}(${scoreDesc}),一气专旺,顺势而为`,
1010
+ '金': `日主金${categoryDesc}(${scoreDesc}),一气专旺,顺势而为`,
1011
+ '水': `日主水${categoryDesc}(${scoreDesc}),一气专旺,顺势而为`
1012
+ }
1013
+ };
1014
+ return analysisMap[categoryDesc]?.[element] || `日主${element}${categoryDesc}(${scoreDesc})`;
1015
+ }
1016
+ /**
1017
+ * Generate suggestions based on strength
1018
+ */
1019
+ static generateSuggestions(strength, element) {
1020
+ const suggestionMap = {
1021
+ '衰极': [
1022
+ '命局极弱,宜顺从大势,不宜逆流而上',
1023
+ '适合依附他人,借力使力',
1024
+ '避免独自创业,宜合作共赢',
1025
+ '注重人际关系,贵人运佳'
1026
+ ],
1027
+ '身弱': [
1028
+ '命局偏弱,需要生扶帮助',
1029
+ '寻找贵人相助,善用外力',
1030
+ '避免过度消耗,注意休息',
1031
+ '选择稳定发展,循序渐进'
1032
+ ],
1033
+ '偏弱': [
1034
+ '加强自身能力,提升专业技能',
1035
+ '寻找贵人相助,善用外力',
1036
+ '避免过度消耗,注意休息',
1037
+ '选择稳定发展,循序渐进'
1038
+ ],
1039
+ '中和': [
1040
+ '保持现有平衡,稳中求进',
1041
+ '发挥自身优势,把握机会',
1042
+ '注意五行流通,避免偏颇',
1043
+ '中庸之道,进退有度'
1044
+ ],
1045
+ '偏强': [
1046
+ '发挥自身优势,积极进取',
1047
+ '适合主动发展,把握机会',
1048
+ '注意适度收敛,避免过度',
1049
+ '善用自身能量,帮助他人'
1050
+ ],
1051
+ '身旺': [
1052
+ '发挥领导才能,勇于担当',
1053
+ '适合开创事业,独当一面',
1054
+ '注意谦虚谨慎,避免刚愎',
1055
+ '善用自身能量,造福他人'
1056
+ ],
1057
+ '旺极': [
1058
+ '顺势而为,不宜逆转',
1059
+ '专注一个方向深耕',
1060
+ '避免分散精力,集中突破',
1061
+ '以柔克刚,以德服人'
1062
+ ]
1063
+ };
1064
+ return suggestionMap[strength] || suggestionMap['中和'];
1065
+ }
1066
+ /**
1067
+ * Generate characteristics
1068
+ */
1069
+ static generateCharacteristics(strength, score, factors) {
1070
+ const characteristics = [];
1071
+ if (strength === '衰极') {
1072
+ characteristics.push('日主极弱,可能从弱格');
1073
+ }
1074
+ else if (strength === '身弱') {
1075
+ characteristics.push('日主偏弱,需要生扶');
1076
+ }
1077
+ else if (strength === '中和') {
1078
+ characteristics.push('日主中和,五行平衡');
1079
+ }
1080
+ else if (strength === '身旺') {
1081
+ characteristics.push('日主偏旺,需要制化');
1082
+ }
1083
+ else if (strength === '旺极') {
1084
+ characteristics.push('日主极旺,可能从旺格');
1085
+ }
1086
+ if (factors.supportingForce > factors.opposingForce * 2) {
1087
+ characteristics.push('生扶力量强大');
1088
+ }
1089
+ else if (factors.opposingForce > factors.supportingForce * 2) {
1090
+ characteristics.push('克泄耗力量强大');
1091
+ }
1092
+ return characteristics;
1093
+ }
1094
+ /**
1095
+ * Categorize breakdown item
1096
+ */
1097
+ static categorizeBreakdownItem(item) {
1098
+ if (item.includes('干')) {
1099
+ return 'stem';
1100
+ }
1101
+ else if (item.includes('支')) {
1102
+ return 'root';
1103
+ }
1104
+ else {
1105
+ return 'interaction';
1106
+ }
1107
+ }
1108
+ // Basic helper methods
1109
+ static getStemElement(stem) {
1110
+ const stemData = bazi_1.HEAVENLY_STEMS.find(s => s.name === stem);
1111
+ return stemData?.element || '';
1112
+ }
1113
+ static getBranchElement(branch) {
1114
+ const branchData = bazi_1.EARTHLY_BRANCHES.find(b => b.name === branch);
1115
+ return branchData?.element || '';
1116
+ }
1117
+ }
1118
+ exports.StrengthAnalyzer = StrengthAnalyzer;
1119
+ //# sourceMappingURL=StrengthAnalyzer.js.map