dsh-metaphysics 0.60.0 → 0.61.0

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 (45) hide show
  1. package/CHANGELOG.md +1550 -1435
  2. package/README.md +17 -14
  3. package/client.almanac.js +24 -24
  4. package/client.bazi.js +120 -112
  5. package/client.chenggu.js +40 -40
  6. package/client.convert.js +22 -22
  7. package/client.daliuren.js +79 -79
  8. package/client.fengshui.js +29 -29
  9. package/client.hepan.js +111 -103
  10. package/client.js +282 -263
  11. package/client.liuyao.js +101 -100
  12. package/client.mbti.js +21 -21
  13. package/client.name.js +71 -71
  14. package/client.numerology.js +21 -21
  15. package/client.qimen.js +82 -82
  16. package/client.star.js +14 -14
  17. package/client.tarot.js +107 -107
  18. package/client.trend.js +81 -81
  19. package/client.xiaoliuren.js +30 -30
  20. package/client.zeri.js +59 -59
  21. package/client.ziwei.js +116 -108
  22. package/engine/citations.js +4 -0
  23. package/engine/diff.js +3 -0
  24. package/engine/event-prompt.js +7 -1
  25. package/engine/followup.js +16 -1
  26. package/engine/index.js +16 -3
  27. package/engine/interpret-knowledge.js +21 -0
  28. package/engine/interpret.js +7 -3
  29. package/engine/liuyao.js +4 -0
  30. package/engine/meihua.js +3 -0
  31. package/engine/name-data.js +24 -78
  32. package/engine/name-extra.js +415 -0
  33. package/engine/name.js +36 -9
  34. package/engine/plain-summary.js +5 -1
  35. package/engine/surname-data.js +249 -0
  36. package/engine/tarot-cards.js +143 -0
  37. package/engine/tarot.js +74 -27
  38. package/engine/yijing-text.js +792 -0
  39. package/engine/zeri-shensha.js +958 -0
  40. package/engine/zeri.js +126 -28
  41. package/engine/ziwei-zuoyao.js +313 -0
  42. package/engine/ziwei.js +37 -1
  43. package/index.js +6 -1
  44. package/package.json +2 -2
  45. package//345/221/275/347/220/206/346/216/222/347/233/230.html +3324 -163
@@ -0,0 +1,958 @@
1
+ // 择日神煞宜忌层:底本《钦定协纪辨方书》(四库全书本)。
2
+ //
3
+ // 数据分三层:
4
+ // 1. 卷十一「用事」逐条宜忌清单——每个事项宜某神、忌某神,原文直接编码;
5
+ // 2. 卷四「建除十二神」、卷五/卷六「义例」、卷九「立成」的起法——按节气月与日干支
6
+ // 逐日推算当日命中哪些神煞;
7
+ // 3. 卷十「宜忌」的吉凶轻重六等与德神制化——六等判词逐字编码,德神能否解忌按卷十
8
+ // 各条的「遇德/犹忌」按语编码。
9
+ //
10
+ // 口径边界:六等判词是原文,吉/凶权重与比值阈值是本仓校准,不是古籍常数;卷十等第表按
11
+ // 「月支 × 日建除」逐格列等,本层只编码可逐条引证的判词与制化条件,未把整张表逐格数字化。
12
+ // 没有可靠起法的条目(土王用事后、血忌、伏社日、八风等)不参与计分,逐日列出 unsupported,
13
+ // 不猜、不补。卷四「建除同位异名」把福德/天巫/天喜/天医/土府等并入建除,本层照此归并,
14
+ // 同一建除日只计一次,避免一月破同时按「月破」「大耗」重复扣分。
15
+
16
+ const BRANCHES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
17
+ const STEMS = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
18
+ const ZHIXING = ['建', '除', '满', '平', '定', '执', '破', '危', '成', '收', '开', '闭'];
19
+ const SEASON_OF = {
20
+ 寅: '春', 卯: '春', 辰: '春',
21
+ 巳: '夏', 午: '夏', 未: '夏',
22
+ 申: '秋', 酉: '秋', 戌: '秋',
23
+ 亥: '冬', 子: '冬', 丑: '冬',
24
+ };
25
+ const MONTH_NO = { 寅: 1, 卯: 2, 辰: 3, 巳: 4, 午: 5, 未: 6, 申: 7, 酉: 8, 戌: 9, 亥: 10, 子: 11, 丑: 12 };
26
+ const SANHE = {
27
+ 寅: '寅午戌', 午: '寅午戌', 戌: '寅午戌',
28
+ 亥: '亥卯未', 卯: '亥卯未', 未: '亥卯未',
29
+ 申: '申子辰', 子: '申子辰', 辰: '申子辰',
30
+ 巳: '巳酉丑', 酉: '巳酉丑', 丑: '巳酉丑',
31
+ };
32
+
33
+ // ── 卷四 建除十二神「同位异名」 ─────────────────────────────
34
+ // 建【兵福 小时 土府】、除【吉期 兵宝】、满【福德 天巫 天狗】、平【死神】、
35
+ // 定【时阴 官符 死气】、执【枝德 小耗】、破【大耗】、成【天医 天喜】、
36
+ // 收【…】、开【时阳 生气】、闭【血支】。天狗只作「申月戌日」的条件神煞,不并入满日。
37
+ export const XING_ALIAS = {
38
+ 建: ['建日', '兵福', '小时', '土府', '月建'],
39
+ 除: ['除日', '吉期', '兵宝'],
40
+ 满: ['满日', '福德', '天巫'],
41
+ 平: ['平日', '死神'],
42
+ 定: ['定日', '时阴', '官符', '死气'],
43
+ 执: ['执日', '枝德', '小耗'],
44
+ 破: ['破日', '大耗', '月破'],
45
+ 危: ['危日'],
46
+ 成: ['成日', '天医', '天喜'],
47
+ 收: ['收日'],
48
+ 开: ['开日', '时阳', '生气'],
49
+ 闭: ['闭日', '血支'],
50
+ };
51
+ const XING_NAME_MAP = {};
52
+ for (const [xing, names] of Object.entries(XING_ALIAS)) for (const n of names) XING_NAME_MAP[n] = xing;
53
+
54
+ // ── 卷五 义例三:月神起法 ───────────────────────────────────
55
+ // 天德:堪舆经「正月丁二月坤三月壬四月辛五月乾六月甲七月癸八月艮九月丙十月乙十一月巽十二月庚」。
56
+ // 四仲月(卯午酉子)天德居四维,无日可值,表中记 null。
57
+ const TIAN_DE = { 寅: '丁', 卯: null, 辰: '壬', 巳: '辛', 午: null, 未: '甲', 申: '癸', 酉: null, 戌: '丙', 亥: '乙', 子: null, 丑: '庚' };
58
+ // 天德合:历例「正月壬三月丁四月丙六月己七月戊九月辛十月庚十二月乙」。
59
+ const TIAN_DE_HE = { 寅: '壬', 卯: null, 辰: '丁', 巳: '丙', 午: null, 未: '己', 申: '戊', 酉: null, 戌: '辛', 亥: '庚', 子: null, 丑: '乙' };
60
+ // 月德:历例「正五九月在丙,二六十月在甲,三七十一月在壬,四八十二月在庚」。
61
+ const YUE_DE = { 寅: '丙', 卯: '甲', 辰: '壬', 巳: '庚', 午: '丙', 未: '甲', 申: '壬', 酉: '庚', 戌: '丙', 亥: '甲', 子: '壬', 丑: '庚' };
62
+ // 月德合:历例「正五九月在辛,二六十月在己,三七十一月在丁,四八十二月在乙」。
63
+ const YUE_DE_HE = { 寅: '辛', 卯: '己', 辰: '丁', 巳: '乙', 午: '辛', 未: '己', 申: '丁', 酉: '乙', 戌: '辛', 亥: '己', 子: '丁', 丑: '乙' };
64
+ // 月空:历例「寅午戌月壬,亥卯未月庚,申子辰月丙,巳酉丑月甲」。
65
+ const YUE_KONG = { 寅: '壬', 卯: '庚', 辰: '丙', 巳: '甲', 午: '壬', 未: '庚', 申: '丙', 酉: '甲', 戌: '壬', 亥: '庚', 子: '丙', 丑: '甲' };
66
+ // 天恩:历例「甲子至戊辰、己卯至癸未、己酉至癸丑,凡十五日」。
67
+ const TIAN_EN = ['甲子', '乙丑', '丙寅', '丁卯', '戊辰', '己卯', '庚辰', '辛巳', '壬午', '癸未', '己酉', '庚戌', '辛亥', '壬子', '癸丑'];
68
+ // 天赦:历例「春戊寅,夏甲午,秋戊申,冬甲子」。
69
+ const SEASON_SHE = { 春: '戊寅', 夏: '甲午', 秋: '戊申', 冬: '甲子' };
70
+ // 天愿:卷五按语据神煞起例改正后的十二辰(原历例传写多误)。
71
+ const TIAN_YUAN = {
72
+ 寅: '乙亥', 卯: '甲戌', 辰: '乙酉', 巳: '丙申', 午: '丁未', 未: '戊午',
73
+ 申: '己巳', 酉: '庚辰', 戌: '辛卯', 亥: '壬寅', 子: '癸丑', 丑: '甲子',
74
+ };
75
+ // 母仓:历例「春亥子,夏寅卯,秋辰戌丑未,冬申酉」;土王后巳午未实现。
76
+ const SEASON_MU_CANG = { 春: ['亥', '子'], 夏: ['寅', '卯'], 秋: ['辰', '戌', '丑', '未'], 冬: ['申', '酉'] };
77
+ // 月恩:历例「正月丙、二月丁、三月庚、四月己、五月戊、六月辛、七月壬、八月癸、九月庚、十月乙、十一月甲、十二月辛」。
78
+ const YUE_EN = { 寅: '丙', 卯: '丁', 辰: '庚', 巳: '己', 午: '戊', 未: '辛', 申: '壬', 酉: '癸', 戌: '庚', 亥: '乙', 子: '甲', 丑: '辛' };
79
+ // 四相:历例「春丙丁,夏戊己,秋壬癸,冬甲乙」。
80
+ const SEASON_SI_XIANG = { 春: ['丙', '丁'], 夏: ['戊', '己'], 秋: ['壬', '癸'], 冬: ['甲', '乙'] };
81
+ // 时德:历例「春午,夏辰,秋子,冬寅」。
82
+ const SEASON_SHI_DE = { 春: '午', 夏: '辰', 秋: '子', 冬: '寅' };
83
+ // 王官守相民日:卷五按语从旧说,王日寅巳申亥、官日卯午酉子;守日从邵泰衢改正为辰未戌丑。
84
+ const SEASON_WANG = { 春: '寅', 夏: '巳', 秋: '申', 冬: '亥' };
85
+ const SEASON_GUAN = { 春: '卯', 夏: '午', 秋: '酉', 冬: '子' };
86
+ const SEASON_SHOU = { 春: '辰', 夏: '未', 秋: '戌', 冬: '丑' };
87
+ const SEASON_XIANG = { 春: '巳', 夏: '申', 秋: '亥', 冬: '寅' };
88
+ const SEASON_MIN = { 春: '午', 夏: '酉', 秋: '子', 冬: '卯' };
89
+ // 四击:通书「春戌,夏丑,秋辰,冬未」。
90
+ const SEASON_SI_JI = { 春: '戌', 夏: '丑', 秋: '辰', 冬: '未' };
91
+ // 九空:历例「正月在辰逆行四季」,曹震圭以三合墓冲释之。
92
+ const JIU_KONG = { 寅: '辰', 卯: '丑', 辰: '戌', 巳: '未', 午: '辰', 未: '丑', 申: '戌', 酉: '未', 戌: '辰', 亥: '丑', 子: '戌', 丑: '未' };
93
+ // 五墓:历例「正二月乙未,四五月丙戌,七八月辛丑,十月十一月壬辰,四季月戊辰」。
94
+ const WU_MU = { 寅: '乙未', 卯: '乙未', 辰: '戊辰', 巳: '丙戌', 午: '丙戌', 未: '戊辰', 申: '辛丑', 酉: '辛丑', 戌: '戊辰', 亥: '壬辰', 子: '壬辰', 丑: '戊辰' };
95
+ // 四耗:历例「春壬子,夏乙卯,秋戊午,冬辛酉」。
96
+ const SEASON_SI_HAO = { 春: ['壬子'], 夏: ['乙卯'], 秋: ['戊午'], 冬: ['辛酉'] };
97
+ // 四废:历例「春庚申辛酉,夏壬子癸亥,秋甲寅乙卯,冬丙午丁巳」。
98
+ const SEASON_SI_FEI = { 春: ['庚申', '辛酉'], 夏: ['壬子', '癸亥'], 秋: ['甲寅', '乙卯'], 冬: ['丙午', '丁巳'] };
99
+ // 四忌:神煞起例「春甲子,夏丙子,秋庚子,冬壬子」。
100
+ const SEASON_SI_JI4 = { 春: ['甲子'], 夏: ['丙子'], 秋: ['庚子'], 冬: ['壬子'] };
101
+ // 四穷:历例「春乙亥,夏丁亥,秋辛亥,冬癸亥」。
102
+ const SEASON_SI_QIONG = { 春: ['乙亥'], 夏: ['丁亥'], 秋: ['辛亥'], 冬: ['癸亥'] };
103
+ // 九坎/九焦:历例「正月在辰逆行四季,五月在卯逆行四仲,九月在寅逆行四孟」。
104
+ const JIU_KAN = { 寅: '辰', 卯: '丑', 辰: '戌', 巳: '未', 午: '卯', 未: '子', 申: '酉', 酉: '午', 戌: '寅', 亥: '亥', 子: '申', 丑: '巳' };
105
+ // 五虚:历例「春巳酉丑,夏申子辰,秋亥卯未,冬寅午戌」。
106
+ const SEASON_WU_XU = { 春: ['巳', '酉', '丑'], 夏: ['申', '子', '辰'], 秋: ['亥', '卯', '未'], 冬: ['寅', '午', '戌'] };
107
+ // 触水龙:丙子、癸丑、癸未。
108
+ const CHU_SHUI_LONG = ['丙子', '癸丑', '癸未'];
109
+ // 八专:曾门经「丁未、己未、庚申、甲寅、癸丑」。
110
+ const BA_ZHUAN = ['丁未', '己未', '庚申', '甲寅', '癸丑'];
111
+ // 无禄:通书十日「甲辰乙巳庚辰辛巳丙申戊戌丁亥己丑壬申癸亥」。
112
+ const WU_LU = ['甲辰', '乙巳', '庚辰', '辛巳', '丙申', '戊戌', '丁亥', '己丑', '壬申', '癸亥'];
113
+ // 重日:己亥。
114
+ const CHONG_RI = ['己亥'];
115
+ // 五合:寅卯日;五离/除神:申酉日(卷五)。
116
+ // 解神:历例「正二月申,三四月戌,五六月子,七八月寅,九十月辰,十一月十二月午」。
117
+ const JIE_SHEN = { 寅: '申', 卯: '申', 辰: '戌', 巳: '戌', 午: '子', 未: '子', 申: '寅', 酉: '寅', 戌: '辰', 亥: '辰', 子: '午', 丑: '午' };
118
+ // 复日:历例「正七月甲庚,二八月乙辛,四十月丙壬,五十一月丁癸,三九六十二月戊己」。
119
+ const FU_RI = {
120
+ 寅: ['甲', '庚'], 卯: ['乙', '辛'], 辰: ['戊', '己'], 巳: ['丙', '壬'], 午: ['丁', '癸'], 未: ['戊', '己'],
121
+ 申: ['甲', '庚'], 酉: ['乙', '辛'], 戌: ['戊', '己'], 亥: ['丙', '壬'], 子: ['丁', '癸'], 丑: ['戊', '己'],
122
+ };
123
+ // 鸣吠十三日、鸣吠对十一日:卷五按语据神煞起例改正(通书旧本有误)。
124
+ const MING_FEI = ['甲午', '丙午', '庚午', '壬午', '甲申', '丙申', '庚申', '壬申', '乙酉', '丁酉', '己酉', '辛酉', '癸酉'];
125
+ const MING_FEI_DUI = ['丙子', '庚子', '壬子', '甲寅', '丙寅', '庚寅', '壬寅', '乙卯', '丁卯', '辛卯', '癸卯'];
126
+
127
+ // ── 卷六 义例四:月神起法 ───────────────────────────────────
128
+ // 三合:日支落在月建三合局的另外两支。
129
+ // 临日:历例「正月午、二月亥、三月申、四月丑、五月戌、六月卯、七月子、八月巳、九月寅、十月未、十一月辰、十二月酉」。
130
+ const LIN_RI = { 寅: '午', 卯: '亥', 辰: '申', 巳: '丑', 午: '戌', 未: '卯', 申: '子', 酉: '巳', 戌: '寅', 亥: '未', 子: '辰', 丑: '酉' };
131
+ // 驿马:正月起申逆行四孟;天后同位。
132
+ const YI_MA = { 寅: '申', 卯: '巳', 辰: '寅', 巳: '亥', 午: '申', 未: '巳', 申: '寅', 酉: '亥', 戌: '申', 亥: '巳', 子: '寅', 丑: '亥' };
133
+ // 劫煞:正月起亥逆行四孟。
134
+ const JIE_SHA = { 寅: '亥', 卯: '申', 辰: '巳', 巳: '寅', 午: '亥', 未: '申', 申: '巳', 酉: '寅', 戌: '亥', 亥: '申', 子: '巳', 丑: '寅' };
135
+ // 灾煞:正月起子逆行四仲(卷六按语从曹震圭说)。
136
+ const ZAI_SHA = { 寅: '子', 卯: '酉', 辰: '午', 巳: '卯', 午: '子', 未: '酉', 申: '午', 酉: '卯', 戌: '子', 亥: '酉', 子: '午', 丑: '卯' };
137
+ // 月煞/月虚:正月起丑逆行四季。
138
+ const YUE_SHA = { 寅: '丑', 卯: '戌', 辰: '未', 巳: '辰', 午: '丑', 未: '戌', 申: '未', 酉: '辰', 戌: '丑', 亥: '戌', 子: '未', 丑: '辰' };
139
+ // 月刑:与岁刑同(三刑)。
140
+ const YUE_XING = { 寅: '巳', 卯: '子', 辰: '辰', 巳: '申', 午: '午', 未: '丑', 申: '寅', 酉: '酉', 戌: '未', 亥: '亥', 子: '卯', 丑: '戌' };
141
+ // 月害:正月起巳逆行十二辰。
142
+ const YUE_HAI = { 寅: '巳', 卯: '辰', 辰: '卯', 巳: '寅', 午: '丑', 未: '子', 申: '亥', 酉: '戌', 戌: '酉', 亥: '申', 子: '未', 丑: '午' };
143
+ // 大时/大败/咸池:正月起卯逆行四仲。
144
+ const DA_SHI = { 寅: '卯', 卯: '子', 辰: '酉', 巳: '午', 午: '卯', 未: '子', 申: '酉', 酉: '午', 戌: '卯', 亥: '子', 子: '酉', 丑: '午' };
145
+ // 游祸:正月起巳逆行四孟。
146
+ const YOU_HUO = { 寅: '巳', 卯: '寅', 辰: '亥', 巳: '申', 午: '巳', 未: '寅', 申: '亥', 酉: '申', 戌: '巳', 亥: '寅', 子: '亥', 丑: '申' };
147
+ // 天吏:正月起酉逆行四仲。
148
+ const TIAN_LI = { 寅: '酉', 卯: '午', 辰: '卯', 巳: '子', 午: '酉', 未: '午', 申: '卯', 酉: '子', 戌: '酉', 亥: '午', 子: '卯', 丑: '子' };
149
+ // 六合:正月在亥逆行十二辰。
150
+ const LIU_HE = { 寅: '亥', 卯: '戌', 辰: '酉', 巳: '申', 午: '未', 未: '午', 申: '巳', 酉: '辰', 戌: '卯', 亥: '寅', 子: '丑', 丑: '子' };
151
+ // 五富:正月起亥顺行四孟。
152
+ const WU_FU = { 寅: '亥', 卯: '寅', 辰: '巳', 巳: '申', 午: '亥', 未: '寅', 申: '巳', 酉: '申', 戌: '亥', 亥: '寅', 子: '巳', 丑: '申' };
153
+ // 天仓:正月起寅逆行十二辰。
154
+ const TIAN_CANG = { 寅: '寅', 卯: '丑', 辰: '子', 巳: '亥', 午: '戌', 未: '酉', 申: '申', 酉: '未', 戌: '午', 亥: '巳', 子: '辰', 丑: '卯' };
155
+ // 天贼:正月在丑逆行十二辰。
156
+ const TIAN_ZEI = { 寅: '丑', 卯: '子', 辰: '亥', 巳: '戌', 午: '酉', 未: '申', 申: '未', 酉: '午', 戌: '巳', 亥: '辰', 子: '卯', 丑: '寅' };
157
+ // 要安、玉宇、金堂、敬安、普护、福生、圣心、益后、续世:九神逐月表。
158
+ const YAO_AN = { 寅: '寅', 卯: '申', 辰: '卯', 巳: '酉', 午: '辰', 未: '戌', 申: '巳', 酉: '亥', 戌: '午', 亥: '子', 子: '未', 丑: '丑' };
159
+ const YU_YU = { 寅: '卯', 卯: '酉', 辰: '辰', 巳: '戌', 午: '巳', 未: '亥', 申: '午', 酉: '子', 戌: '未', 亥: '丑', 子: '申', 丑: '寅' };
160
+ const JIN_TANG = { 寅: '辰', 卯: '戌', 辰: '巳', 巳: '亥', 午: '午', 未: '子', 申: '未', 酉: '丑', 戌: '申', 亥: '寅', 子: '酉', 丑: '卯' };
161
+ const JING_AN = { 寅: '未', 卯: '丑', 辰: '申', 巳: '寅', 午: '酉', 未: '卯', 申: '戌', 酉: '辰', 戌: '亥', 亥: '巳', 子: '子', 丑: '午' };
162
+ const PU_HU = { 寅: '申', 卯: '寅', 辰: '酉', 巳: '卯', 午: '戌', 未: '辰', 申: '亥', 酉: '巳', 戌: '子', 亥: '午', 子: '丑', 丑: '未' };
163
+ const FU_SHENG = { 寅: '酉', 卯: '卯', 辰: '戌', 巳: '辰', 午: '亥', 未: '巳', 申: '子', 酉: '午', 戌: '丑', 亥: '未', 子: '寅', 丑: '申' };
164
+ const SHENG_XIN = { 寅: '亥', 卯: '巳', 辰: '子', 巳: '午', 午: '丑', 未: '未', 申: '寅', 酉: '申', 戌: '卯', 亥: '酉', 子: '辰', 丑: '戌' };
165
+ const YI_HOU = { 寅: '子', 卯: '午', 辰: '丑', 巳: '未', 午: '寅', 未: '申', 申: '卯', 酉: '酉', 戌: '辰', 亥: '戌', 子: '巳', 丑: '亥' };
166
+ const XU_SHI = { 寅: '丑', 卯: '未', 辰: '寅', 巳: '申', 午: '卯', 未: '酉', 申: '辰', 酉: '戌', 戌: '巳', 亥: '亥', 子: '午', 丑: '子' };
167
+ // 阳德:正月起戌顺行六阳辰;阴德:正月起酉逆行六阴辰。
168
+ const YANG_DE = { 寅: '戌', 卯: '子', 辰: '寅', 巳: '辰', 午: '午', 未: '申', 申: '戌', 酉: '子', 戌: '寅', 亥: '辰', 子: '午', 丑: '申' };
169
+ const YIN_DE = { 寅: '酉', 卯: '未', 辰: '巳', 巳: '卯', 午: '丑', 未: '亥', 申: '酉', 酉: '未', 戌: '巳', 亥: '卯', 子: '丑', 丑: '亥' };
170
+ // 天马:正月起午顺行六阳辰;兵禁:正月起寅逆行六阳辰。
171
+ const TIAN_MA = { 寅: '午', 卯: '申', 辰: '戌', 巳: '子', 午: '寅', 未: '辰', 申: '午', 酉: '申', 戌: '戌', 亥: '子', 子: '寅', 丑: '辰' };
172
+ const BING_JIN = { 寅: '寅', 卯: '子', 辰: '戌', 巳: '申', 午: '午', 未: '辰', 申: '寅', 酉: '子', 戌: '戌', 亥: '申', 子: '午', 丑: '辰' };
173
+ // 地囊:卷六按语据纳甲改正后的逐月两日。
174
+ const DI_NANG = {
175
+ 寅: ['庚子', '庚午'], 卯: ['乙未', '癸丑'], 辰: ['甲子', '壬午'], 巳: ['己卯', '己酉'],
176
+ 午: ['壬戌', '甲辰'], 未: ['丙辰', '丙戌'], 申: ['丁巳', '丁亥'], 酉: ['丙寅', '丙申'],
177
+ 戌: ['辛丑', '辛未'], 亥: ['戊寅', '戊申'], 子: ['辛卯', '辛酉'], 丑: ['癸酉', '乙卯'],
178
+ };
179
+ // 土符:正月丑、二月巳、三月酉、四月寅、五月午、六月戌、七月卯、八月未、九月亥、十月辰、十一月申、十二月子。
180
+ const TU_FU = { 寅: '丑', 卯: '巳', 辰: '酉', 巳: '寅', 午: '午', 未: '戌', 申: '卯', 酉: '未', 戌: '亥', 亥: '辰', 子: '申', 丑: '子' };
181
+ // 大煞:正月戌、二月巳、三月午、四月未、五月寅、六月卯、七月辰、八月亥、九月子、十月丑、十一月申、十二月酉。
182
+ const DA_SHA = { 寅: '戌', 卯: '巳', 辰: '午', 巳: '未', 午: '寅', 未: '卯', 申: '辰', 酉: '亥', 戌: '子', 亥: '丑', 子: '申', 丑: '酉' };
183
+ // 归忌:孟月丑、仲月寅、季月子。
184
+ // 往亡:正月寅、二月巳、三月申、四月亥、五月卯、六月午、七月酉、八月子、九月辰、十月未、十一月戌、十二月丑。
185
+ const WANG_WANG = { 寅: '寅', 卯: '巳', 辰: '申', 巳: '亥', 午: '卯', 未: '午', 申: '酉', 酉: '子', 戌: '辰', 亥: '未', 子: '戌', 丑: '丑' };
186
+ // 月厌/地火:正月在戌逆行十二辰;厌对/六仪/招摇:正月起辰逆行十二辰。
187
+ const YUE_YAN = { 寅: '戌', 卯: '酉', 辰: '申', 巳: '未', 午: '午', 未: '巳', 申: '辰', 酉: '卯', 戌: '寅', 亥: '丑', 子: '子', 丑: '亥' };
188
+ const YAN_DUI = { 寅: '辰', 卯: '卯', 辰: '寅', 巳: '丑', 午: '子', 未: '亥', 申: '戌', 酉: '酉', 戌: '申', 亥: '未', 子: '午', 丑: '巳' };
189
+ // 天火:正月在子顺行四仲。
190
+ const TIAN_HUO = { 寅: '子', 卯: '卯', 辰: '午', 巳: '酉', 午: '子', 未: '卯', 申: '午', 酉: '酉', 戌: '子', 亥: '卯', 子: '午', 丑: '酉' };
191
+
192
+ // ── 卷四 阴阳不将:历例逐月日表 ─────────────────────────────
193
+ const BU_JIANG = {
194
+ 寅: ['辛亥', '辛丑', '辛卯', '庚子', '庚寅', '己亥', '己丑', '己卯', '丁亥', '丁丑', '丁卯', '丙子', '丙寅'],
195
+ 卯: ['庚戌', '庚子', '庚寅', '己亥', '己丑', '丁亥', '丁丑', '丙戌', '丙子', '丙寅', '乙亥', '乙丑'],
196
+ 辰: ['己酉', '己亥', '己丑', '丁酉', '丁亥', '丁丑', '丙戌', '丙子', '乙酉', '乙亥', '乙丑', '甲戌', '甲子'],
197
+ 巳: ['丁酉', '丁亥', '丙申', '丙戌', '丙子', '乙酉', '乙亥', '甲申', '甲戌', '甲子', '戊申', '戊戌', '戊子'],
198
+ 午: ['丙申', '丙戌', '乙未', '乙酉', '乙亥', '甲申', '甲戌', '戊申', '戊戌', '癸未', '癸酉', '癸亥'],
199
+ 未: ['乙未', '乙酉', '甲午', '甲申', '甲戌', '戊午', '戊申', '戊戌', '癸未', '癸酉', '壬午', '壬申', '壬戌'],
200
+ 申: ['乙巳', '乙未', '乙酉', '甲午', '甲申', '戊午', '戊申', '癸巳', '癸未', '癸酉', '壬午', '壬申'],
201
+ 酉: ['甲辰', '甲午', '甲申', '戊辰', '戊午', '戊申', '癸巳', '癸未', '壬辰', '壬午', '壬申', '辛巳', '辛未'],
202
+ 戌: ['戊辰', '戊午', '癸卯', '癸巳', '癸未', '壬辰', '壬午', '辛卯', '辛巳', '辛未', '庚辰', '庚午'],
203
+ 亥: ['癸卯', '癸巳', '壬寅', '壬辰', '壬午', '辛卯', '辛巳', '庚寅', '庚辰', '庚午', '己卯', '己巳'],
204
+ 子: ['壬寅', '壬辰', '辛丑', '辛卯', '辛巳', '庚寅', '庚辰', '己丑', '己卯', '己巳', '丁丑', '丁卯', '丁巳'],
205
+ 丑: ['辛丑', '辛卯', '庚子', '庚寅', '庚辰', '己丑', '己卯', '丁丑', '丁卯', '丙子', '丙寅', '丙辰'],
206
+ };
207
+
208
+ // ── 卷十一 用事:逐条宜忌(原文按顺序编码,已把「防」还原为「护」等异体) ──
209
+ export const SHENSHA_LISTS = {
210
+ 祭祀: {
211
+ use: ['天德', '月德', '天德合', '月德合', '天赦', '天愿', '月恩', '四相', '时德', '天巫', '开日', '普护', '福生', '圣心', '益后', '续世'],
212
+ avoid: ['天狗', '寅日'],
213
+ },
214
+ 宴会: {
215
+ use: ['天德', '月德', '天德合', '月德合', '天恩', '天赦', '天愿', '月恩', '四相', '时德', '王日', '民日', '三合', '福德', '天喜', '开日', '六合', '五合'],
216
+ avoid: ['月破', '平日', '收日', '闭日', '劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '四废', '五离', '酉日'],
217
+ },
218
+ 开市: {
219
+ use: ['天愿', '民日', '满日', '成日', '开日', '五富'],
220
+ avoid: ['月破', '大耗', '平日', '收日', '闭日', '劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '大时', '天吏', '小耗', '四耗', '四废', '四穷', '五墓', '九空'],
221
+ },
222
+ 立券交易: {
223
+ use: ['天愿', '民日', '三合', '满日', '六合', '五富', '五合'],
224
+ avoid: ['月破', '大耗', '平日', '收日', '劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '大时', '天吏', '小耗', '四耗', '四废', '四穷', '五墓', '九空', '五离'],
225
+ },
226
+ 纳财: {
227
+ use: ['母仓', '天愿', '月恩', '四相', '时德', '民日', '三合', '满日', '收日', '六合', '五富', '天仓'],
228
+ avoid: ['月破', '大耗', '平日', '劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '大时', '天吏', '小耗', '四耗', '四废', '四穷', '九空'],
229
+ },
230
+ 般移: {
231
+ use: ['天德', '月德', '天德合', '月德合', '天赦', '天愿', '月恩', '四相', '时德', '民日', '驿马', '天马', '成日', '开日'],
232
+ avoid: ['月破', '平日', '收日', '闭日', '劫煞', '灾煞', '月煞', '月刑', '月厌', '大时', '天吏', '四废', '五墓', '归忌', '往亡'],
233
+ },
234
+ 兴造动土: {
235
+ use: ['天德', '月德', '天德合', '月德合', '天赦', '天愿', '月恩', '四相', '时德', '三合', '开日'],
236
+ avoid: ['月建', '土府', '月破', '平日', '收日', '闭日', '劫煞', '灾煞', '月煞', '月刑', '月厌', '大时', '天吏', '四废', '五墓', '土符', '地囊', '土王用事后'],
237
+ },
238
+ 安葬: {
239
+ use: ['天德', '月德', '天德合', '月德合', '天赦', '天愿', '六合', '鸣吠'],
240
+ avoid: ['月建', '月破', '平日', '收日', '劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '四废', '四忌', '四穷', '五墓', '复日', '重日'],
241
+ },
242
+ 破土: {
243
+ use: ['鸣吠', '鸣吠对'],
244
+ avoid: ['月建', '土府', '月破', '平日', '收日', '劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '四废', '五墓', '土符', '地囊', '复日', '重日', '土王用事后'],
245
+ },
246
+ 行幸遣使: {
247
+ use: ['天德', '月德', '天德合', '月德合', '天赦', '天愿', '月恩', '四相', '时德', '王日', '驿马', '天马', '建日', '吉期', '天喜', '开日'],
248
+ avoid: ['月破', '平日', '收日', '闭日', '劫煞', '灾煞', '月煞', '月刑', '月厌', '大时', '天吏', '天贼', '四废', '五墓', '往亡', '巳日'],
249
+ },
250
+ 上官赴任: {
251
+ use: ['天德', '月德', '天德合', '月德合', '天赦', '天愿', '月恩', '四相', '时德', '王日', '官日', '守日', '相日', '临日', '建日', '吉期', '天喜', '开日'],
252
+ avoid: ['月破', '平日', '收日', '满日', '闭日', '劫煞', '灾煞', '月煞', '月刑', '月厌', '大时', '天吏', '四废', '五墓', '往亡'],
253
+ },
254
+ 结婚姻: {
255
+ use: ['天德', '月德', '天德合', '月德合', '天赦', '天愿', '月恩', '四相', '时德', '民日', '三合', '天喜', '六合', '五合'],
256
+ avoid: ['月建', '月破', '平日', '收日', '满日', '闭日', '劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '大时', '天吏', '四废', '四忌', '四穷', '五墓', '五离', '八专'],
257
+ },
258
+ 纳采问名: {
259
+ use: ['天德', '月德', '天德合', '月德合', '天赦', '天愿', '月恩', '四相', '时德', '民日', '三合', '天喜'],
260
+ avoid: ['月建', '月破', '平日', '收日', '满日', '闭日', '劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '大时', '天吏', '四废', '四忌', '四穷', '五墓', '五离', '八专'],
261
+ },
262
+ 嫁娶: {
263
+ use: ['天德', '月德', '天德合', '月德合', '天赦', '天愿', '三合', '天喜', '六合', '不将'],
264
+ avoid: ['月破', '平日', '收日', '闭日', '劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '厌对', '大时', '天吏', '四废', '四忌', '四穷', '五墓', '往亡', '八专', '亥日'],
265
+ },
266
+ 求医疗病: {
267
+ use: ['天德', '月德', '天德合', '月德合', '天赦', '月恩', '四相', '时德', '天后', '除日', '破日', '天医', '开日', '解神', '除神'],
268
+ avoid: ['月建', '平日', '死神', '收日', '满日', '闭日', '劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '大时', '游祸', '天吏', '死气', '四废', '五墓', '往亡', '未日', '每月十五日', '朔望日'],
269
+ },
270
+ 入学: {
271
+ use: ['成日', '开日'],
272
+ avoid: [],
273
+ },
274
+ };
275
+
276
+ /** 事项 → 卷十一条目(一个事项可对应多条,如安葬兼看破土)。 */
277
+ export const TASK_ENTRIES = {
278
+ 嫁娶: ['嫁娶'],
279
+ 订婚: ['纳采问名'],
280
+ 领证: ['结婚姻'],
281
+ 提亲: ['纳采问名'],
282
+ 收送聘金: ['纳采问名'],
283
+ 满月酒: ['祭祀', '宴会'],
284
+ 开业: ['开市'],
285
+ 交易: ['立券交易'],
286
+ 投资: ['纳财'],
287
+ 签订合同: ['立券交易'],
288
+ 签约: ['立券交易'],
289
+ 纳财: ['纳财'],
290
+ 入宅: ['般移'],
291
+ 搬家: ['般移'],
292
+ 搬家入宅: ['般移'],
293
+ 动土: ['兴造动土'],
294
+ 装修: ['兴造动土'],
295
+ 装修建造: ['兴造动土'],
296
+ 试车买车: ['立券交易'],
297
+ 安葬: ['安葬', '破土'],
298
+ 出行: ['行幸遣使'],
299
+ 旅行: ['行幸遣使'],
300
+ 赴任: ['上官赴任'],
301
+ 访亲友: ['宴会'],
302
+ 入学: ['入学'],
303
+ 祭祀: ['祭祀'],
304
+ 求医: ['求医疗病'],
305
+ };
306
+
307
+ /** 已实现起法的神煞名(含别名)。名单外又出现在卷十一清单里的,记入 unsupported。 */
308
+ const KNOWN_SHENSHA = new Set([
309
+ '天德', '月德', '天德合', '月德合', '月空', '天恩', '天赦', '天愿', '母仓', '月恩', '四相', '时德',
310
+ '王日', '官日', '守日', '相日', '民日', '四击', '九空', '五墓', '四耗', '四废', '四忌', '四穷',
311
+ '九坎', '九焦', '五虚', '触水龙', '八专', '无禄', '重日', '五合', '五离', '除神', '解神', '复日',
312
+ '鸣吠', '鸣吠对', '三合', '临日', '驿马', '天后', '劫煞', '灾煞', '月煞', '月虚', '月刑', '月害',
313
+ '大时', '大败', '咸池', '游祸', '天吏', '六合', '五富', '天仓', '天贼', '要安', '玉宇', '金堂',
314
+ '敬安', '普护', '福生', '圣心', '益后', '续世', '阳德', '阴德', '天马', '兵禁', '地囊', '土符',
315
+ '大煞', '归忌', '往亡', '月厌', '地火', '厌对', '六仪', '招摇', '天火', '不将', '天狗',
316
+ '天医', '天喜', '天巫', '福德', '吉期', '兵宝', '兵福', '小时', '土府', '月建', '月破', '大耗',
317
+ '小耗', '死神', '死气', '时阴', '官符', '枝德', '血支', '时阳', '生气',
318
+ '建日', '除日', '满日', '平日', '定日', '执日', '破日', '危日', '成日', '收日', '开日', '闭日',
319
+ ]);
320
+
321
+ /** 卷十一出现、但本层没有可靠起法/历法条件的条目:不猜,直接列出来。 */
322
+ const UNSUPPORTED_NAMES = new Set(['土王用事后', '血忌', '伏社日', '八风', '气往亡', '专日', '伐日', '四离', '四绝', '上朔']);
323
+
324
+ export const ZERI_SHENSHA_BASIS = {
325
+ text: '《钦定协纪辨方书》卷十一「用事」逐条宜忌 + 卷十「宜忌」吉凶轻重六等',
326
+ edition: '四库全书本(清乾隆内府刊本;殆知阁整理本对校)',
327
+ volumes: ['卷四 义例二 建除十二神', '卷五 义例三 月神', '卷六 义例四 月神', '卷九 立成', '卷十 宜忌', '卷十一 用事'],
328
+ unsupported: [...UNSUPPORTED_NAMES],
329
+ note: '神煞按节气月与日干支确定性起算;六等判词逐字取卷十,德神制化按卷十各条按语;未实现的条目不计分,逐日 shensha.unsupported 会列出。',
330
+ };
331
+
332
+ const uniquePush = (arr, value) => { if (!arr.includes(value)) arr.push(value); };
333
+
334
+ /** 中文数字月日解析:用于「每月十五日」「每月一日六日…」这类规则。 */
335
+ function lunarDaysOf(name) {
336
+ if (name === '朔望日') return [1, 15];
337
+ if (!name.startsWith('每月') || !name.endsWith('日')) return null;
338
+ const body = name.slice(2, -1).replace(/日/g, '');
339
+ const re = /二十[一二三四五六七八九]?|十[一二三四五六七八九]?|[一二三四五六七八九]/g;
340
+ const table = { 一: 1, 二: 2, 三: 3, 四: 4, 五: 5, 六: 6, 七: 7, 八: 8, 九: 9 };
341
+ const out = [];
342
+ for (const m of body.matchAll(re)) {
343
+ const s = m[0];
344
+ let n = 0;
345
+ if (s.startsWith('二十')) n = 20 + (s.length === 3 ? table[s[2]] : 0);
346
+ else if (s.startsWith('十')) n = 10 + (s.length === 2 ? table[s[1]] : 0);
347
+ else n = table[s] || 0;
348
+ if (n >= 1 && n <= 30) out.push(n);
349
+ }
350
+ return out.length ? out : null;
351
+ }
352
+
353
+ /** 日干/日支类禁忌:「寅日」「丁日」「亥子日」。 */
354
+ function dayRuleOf(name) {
355
+ if (!name.endsWith('日') || name === '朔望日') return null;
356
+ const body = name.slice(0, -1);
357
+ if (body.length === 1) {
358
+ if (BRANCHES.includes(body)) return { kind: 'branch', values: [body] };
359
+ if (STEMS.includes(body)) return { kind: 'stem', values: [body] };
360
+ }
361
+ if (body.length === 2 && [...body].every((c) => BRANCHES.includes(c))) return { kind: 'branch', values: [...body] };
362
+ return null;
363
+ }
364
+
365
+ /**
366
+ * 逐日神煞。
367
+ * @param {{monthBranch:string, dayGz:string, xing?:string, lunarDay?:number}} input
368
+ * monthBranch 为节气月支(寅=正月);xing 不传时按日支与月支现算。
369
+ */
370
+ export function computeDayShensha(input) {
371
+ const monthBranch = input && input.monthBranch;
372
+ const dayGz = input && input.dayGz;
373
+ const result = {
374
+ monthBranch, monthNumber: MONTH_NO[monthBranch] || 0, season: SEASON_OF[monthBranch] || '',
375
+ dayGz, dayStem: '', dayBranch: '', xing: '', lunarDay: input && input.lunarDay,
376
+ names: [],
377
+ };
378
+ if (!BRANCHES.includes(monthBranch) || typeof dayGz !== 'string' || dayGz.length !== 2) return result;
379
+ const dayStem = dayGz[0];
380
+ const dayBranch = dayGz[1];
381
+ if (!STEMS.includes(dayStem) || !BRANCHES.includes(dayBranch)) return result;
382
+ const season = SEASON_OF[monthBranch];
383
+ const xing = input.xing && ZHIXING.includes(input.xing)
384
+ ? input.xing
385
+ : ZHIXING[((BRANCHES.indexOf(dayBranch) - BRANCHES.indexOf(monthBranch)) % 12 + 12) % 12];
386
+ const names = new Set();
387
+ const add = (n) => { if (n) names.add(n); };
388
+
389
+ // 建除同位异名:同一建除日只以一组名字出现,计分时按建除归并。
390
+ for (const n of XING_ALIAS[xing] || []) add(n);
391
+
392
+ if (TIAN_DE[monthBranch] && TIAN_DE[monthBranch] === dayStem) add('天德');
393
+ if (TIAN_DE_HE[monthBranch] && TIAN_DE_HE[monthBranch] === dayStem) add('天德合');
394
+ if (YUE_DE[monthBranch] === dayStem) add('月德');
395
+ if (YUE_DE_HE[monthBranch] === dayStem) add('月德合');
396
+ if (YUE_KONG[monthBranch] === dayStem) add('月空');
397
+ if (TIAN_EN.includes(dayGz)) add('天恩');
398
+ if (SEASON_SHE[season] === dayGz) add('天赦');
399
+ if (TIAN_YUAN[monthBranch] === dayGz) add('天愿');
400
+ if (SEASON_MU_CANG[season].includes(dayBranch)) add('母仓');
401
+ if (YUE_EN[monthBranch] === dayStem) add('月恩');
402
+ if (SEASON_SI_XIANG[season].includes(dayStem)) add('四相');
403
+ if (SEASON_SHI_DE[season] === dayBranch) add('时德');
404
+ if (SEASON_WANG[season] === dayBranch) add('王日');
405
+ if (SEASON_GUAN[season] === dayBranch) add('官日');
406
+ if (SEASON_SHOU[season] === dayBranch) add('守日');
407
+ if (SEASON_XIANG[season] === dayBranch) add('相日');
408
+ if (SEASON_MIN[season] === dayBranch) add('民日');
409
+ if (SEASON_SI_JI[season] === dayBranch) add('四击');
410
+ if (JIU_KONG[monthBranch] === dayBranch) add('九空');
411
+ if (WU_MU[monthBranch] === dayGz) add('五墓');
412
+ if (SEASON_SI_HAO[season].includes(dayGz)) add('四耗');
413
+ if (SEASON_SI_FEI[season].includes(dayGz)) add('四废');
414
+ if (SEASON_SI_JI4[season].includes(dayGz)) add('四忌');
415
+ if (SEASON_SI_QIONG[season].includes(dayGz)) add('四穷');
416
+ if (JIU_KAN[monthBranch] === dayBranch) { add('九坎'); add('九焦'); }
417
+ if (SEASON_WU_XU[season].includes(dayBranch)) add('五虚');
418
+ if (CHU_SHUI_LONG.includes(dayGz)) add('触水龙');
419
+ if (BA_ZHUAN.includes(dayGz)) add('八专');
420
+ if (WU_LU.includes(dayGz)) add('无禄');
421
+ if (CHONG_RI.includes(dayGz)) add('重日');
422
+ if (dayBranch === '寅' || dayBranch === '卯') add('五合');
423
+ if (dayBranch === '申' || dayBranch === '酉') { add('五离'); add('除神'); }
424
+ if (JIE_SHEN[monthBranch] === dayBranch) add('解神');
425
+ if (FU_RI[monthBranch].includes(dayStem)) add('复日');
426
+ if (MING_FEI.includes(dayGz)) add('鸣吠');
427
+ if (MING_FEI_DUI.includes(dayGz)) add('鸣吠对');
428
+
429
+ if (LIN_RI[monthBranch] === dayBranch) add('临日');
430
+ if (YI_MA[monthBranch] === dayBranch) { add('驿马'); add('天后'); }
431
+ if (JIE_SHA[monthBranch] === dayBranch) add('劫煞');
432
+ if (ZAI_SHA[monthBranch] === dayBranch) add('灾煞');
433
+ if (YUE_SHA[monthBranch] === dayBranch) { add('月煞'); add('月虚'); }
434
+ if (YUE_XING[monthBranch] === dayBranch) add('月刑');
435
+ if (YUE_HAI[monthBranch] === dayBranch) add('月害');
436
+ if (DA_SHI[monthBranch] === dayBranch) { add('大时'); add('大败'); add('咸池'); }
437
+ if (YOU_HUO[monthBranch] === dayBranch) add('游祸');
438
+ if (TIAN_LI[monthBranch] === dayBranch) add('天吏');
439
+ if (LIU_HE[monthBranch] === dayBranch) add('六合');
440
+ if (WU_FU[monthBranch] === dayBranch) add('五富');
441
+ if (TIAN_CANG[monthBranch] === dayBranch) add('天仓');
442
+ if (TIAN_ZEI[monthBranch] === dayBranch) add('天贼');
443
+ if (YAO_AN[monthBranch] === dayBranch) add('要安');
444
+ if (YU_YU[monthBranch] === dayBranch) add('玉宇');
445
+ if (JIN_TANG[monthBranch] === dayBranch) add('金堂');
446
+ if (JING_AN[monthBranch] === dayBranch) add('敬安');
447
+ if (PU_HU[monthBranch] === dayBranch) add('普护');
448
+ if (FU_SHENG[monthBranch] === dayBranch) add('福生');
449
+ if (SHENG_XIN[monthBranch] === dayBranch) add('圣心');
450
+ if (YI_HOU[monthBranch] === dayBranch) add('益后');
451
+ if (XU_SHI[monthBranch] === dayBranch) add('续世');
452
+ if (YANG_DE[monthBranch] === dayBranch) add('阳德');
453
+ if (YIN_DE[monthBranch] === dayBranch) add('阴德');
454
+ if (TIAN_MA[monthBranch] === dayBranch) add('天马');
455
+ if (BING_JIN[monthBranch] === dayBranch) add('兵禁');
456
+ if (DI_NANG[monthBranch].includes(dayGz)) add('地囊');
457
+ if (TU_FU[monthBranch] === dayBranch) add('土符');
458
+ if (DA_SHA[monthBranch] === dayBranch) add('大煞');
459
+ if (WANG_WANG[monthBranch] === dayBranch) add('往亡');
460
+ if (YUE_YAN[monthBranch] === dayBranch) { add('月厌'); add('地火'); }
461
+ if (YAN_DUI[monthBranch] === dayBranch) { add('厌对'); add('六仪'); add('招摇'); }
462
+ if (TIAN_HUO[monthBranch] === dayBranch) add('天火');
463
+
464
+ // 归忌:孟月丑、仲月寅、季月子。
465
+ const meng = ['寅', '巳', '申', '亥'].includes(monthBranch);
466
+ const zhong = ['子', '卯', '午', '酉'].includes(monthBranch);
467
+ if ((meng && dayBranch === '丑') || (zhong && dayBranch === '寅') || (!meng && !zhong && dayBranch === '子')) add('归忌');
468
+ // 三合:月建三合局中除去月支的另外两支。
469
+ if (SANHE[monthBranch] && [...SANHE[monthBranch]].filter((b) => b !== monthBranch).includes(dayBranch)) add('三合');
470
+ // 不将:卷四历例逐月日表。
471
+ if (BU_JIANG[monthBranch].includes(dayGz)) add('不将');
472
+ // 天狗:申月戌日(满日);卷五按语明确「非谓凡为满日皆天狗」。
473
+ if (monthBranch === '申' && dayBranch === '戌') add('天狗');
474
+
475
+ result.dayStem = dayStem;
476
+ result.dayBranch = dayBranch;
477
+ result.xing = xing;
478
+ result.names = [...names];
479
+ return result;
480
+ }
481
+
482
+ function addXingHit(list, xing, rawName) {
483
+ let item = list.find((x) => x.xing === xing);
484
+ if (!item) { item = { xing, names: [] }; list.push(item); }
485
+ uniquePush(item.names, rawName);
486
+ }
487
+
488
+ /**
489
+ * 某事项在当日的神煞命中。
490
+ * 返回 use/avoid 各分四类:xing(建除)、shensha(神煞)、day(日干/日支禁忌)、lunar(农历日禁忌)。
491
+ */
492
+ export function taskShenshaHits(task, day) {
493
+ const entries = TASK_ENTRIES[task] || [];
494
+ const out = {
495
+ entries,
496
+ use: { xing: [], shensha: [], day: [], lunar: [] },
497
+ avoid: { xing: [], shensha: [], day: [], lunar: [] },
498
+ unsupported: [],
499
+ };
500
+ if (!day || !day.xing || !Array.isArray(day.names)) return out;
501
+ const addList = (bucket, names) => {
502
+ for (const name of names) {
503
+ if (UNSUPPORTED_NAMES.has(name)) { uniquePush(out.unsupported, name); continue; }
504
+ const lunar = lunarDaysOf(name);
505
+ if (lunar) {
506
+ if (Number.isInteger(day.lunarDay) && lunar.includes(day.lunarDay)) uniquePush(bucket.lunar, name);
507
+ continue;
508
+ }
509
+ const rule = dayRuleOf(name);
510
+ if (rule) {
511
+ const hit = rule.kind === 'branch'
512
+ ? rule.values.includes(day.dayBranch)
513
+ : rule.values.includes(day.dayStem);
514
+ if (hit) uniquePush(bucket.day, name);
515
+ continue;
516
+ }
517
+ const xing = XING_NAME_MAP[name];
518
+ if (xing) {
519
+ if (xing === day.xing) addXingHit(bucket.xing, xing, name);
520
+ continue;
521
+ }
522
+ if (KNOWN_SHENSHA.has(name)) {
523
+ if (day.names.includes(name)) uniquePush(bucket.shensha, name);
524
+ continue;
525
+ }
526
+ uniquePush(out.unsupported, name);
527
+ }
528
+ };
529
+ for (const entry of entries) {
530
+ const list = SHENSHA_LISTS[entry];
531
+ if (!list) continue;
532
+ addList(out.use, list.use);
533
+ addList(out.avoid, list.avoid);
534
+ }
535
+ return out;
536
+ }
537
+
538
+ /** 把建除命中格式化成「满日」「土府(建日)」这样的可读串。 */
539
+ export function xingHitText(hit) {
540
+ const raw = hit.names.join('、');
541
+ return hit.names.includes(`${hit.xing}日`) ? raw : `${raw}(${hit.xing}日)`;
542
+ }
543
+
544
+ // ── 卷十 宜忌:吉凶轻重六等与德神制化 ────────────────────────
545
+ // 原文(殆知阁整理本第 4853 行):
546
+ // 「今参互较量,除建破等日条例简易及专忌之日无庸分别外,余分为六等:
547
+ // 其上吉足胜凶,从宜不从忌;上次吉足抵凶,遇德从宜不从忌,不遇从宜亦从忌;
548
+ // 中等吉不抵凶,遇德则吉胜,从宜不从忌,不遇从忌不从宜;
549
+ // 中次凶胜于吉,遇德始相抵,从宜亦从忌,不遇从忌不从宜;
550
+ // 其下凶又逢凶,遇德从忌不从宜,不遇诸事皆忌;
551
+ // 最下凶叠大凶,遇德仍诸事皆忌。」
552
+ // 判词逐字照录;权重与比值阈值是本仓校准,见 GRADE_THRESHOLDS。
553
+ // 卷十等第表按「月支 × 日建除」逐格列等,本层只编码可逐条引证的判词与德神制化条件。
554
+
555
+ export const DECISION = {
556
+ YI: '从宜不从忌',
557
+ YI_JI: '从宜亦从忌',
558
+ PING: '不宜不忌',
559
+ JI: '从忌不从宜',
560
+ ALL_JI: '诸事皆忌',
561
+ };
562
+
563
+ export const GRADE_TEXT =
564
+ '今参互较量除建破等日条例简易及专忌之日无庸分别外余分为六等'
565
+ + '其上吉足胜凶从宜不从忌'
566
+ + '上次吉足抵凶遇德从宜不从忌不遇从宜亦从忌'
567
+ + '中等吉不抵凶遇德则吉胜从宜不从忌不遇从忌不从宜'
568
+ + '中次凶胜于吉遇德始相抵从宜亦从忌不遇从忌不从宜'
569
+ + '其下凶又逢凶遇德从忌不从宜不遇诸事皆忌'
570
+ + '最下凶叠大凶遇德仍诸事皆忌'
571
+ + '然后吉凶轻重各有区分而用静用作趋避各当云表例并列于后';
572
+
573
+ /** 六等判词。withDe / withoutDe 是「遇德」「不遇德」两列的决断。 */
574
+ export const GRADE_RULES = [
575
+ {
576
+ key: '上', label: '上吉',
577
+ doctrine: '吉足胜凶,从宜不从忌',
578
+ withDe: DECISION.YI, withoutDe: DECISION.YI,
579
+ },
580
+ {
581
+ key: '上次', label: '上次',
582
+ doctrine: '吉足抵凶,遇德从宜不从忌,不遇从宜亦从忌',
583
+ withDe: DECISION.YI, withoutDe: DECISION.YI_JI,
584
+ },
585
+ {
586
+ key: '中', label: '中',
587
+ doctrine: '吉不抵凶,遇德则吉胜,从宜不从忌,不遇从忌不从宜',
588
+ withDe: DECISION.YI, withoutDe: DECISION.JI,
589
+ },
590
+ {
591
+ key: '中次', label: '中次',
592
+ doctrine: '凶胜于吉,遇德始相抵,从宜亦从忌,不遇从忌不从宜',
593
+ withDe: DECISION.YI_JI, withoutDe: DECISION.JI,
594
+ },
595
+ {
596
+ key: '下', label: '下',
597
+ doctrine: '凶又逢凶,遇德从忌不从宜,不遇诸事皆忌',
598
+ withDe: DECISION.JI, withoutDe: DECISION.ALL_JI,
599
+ },
600
+ {
601
+ key: '下下', label: '下下',
602
+ doctrine: '凶叠大凶,遇德仍诸事皆忌',
603
+ withDe: DECISION.ALL_JI, withoutDe: DECISION.ALL_JI,
604
+ },
605
+ ];
606
+
607
+ /** 卷十铺注条例:「吉凶相抵,不注宜亦不注忌」——六等之外的持平态。 */
608
+ export const GRADE_PING = {
609
+ key: '平', label: '平',
610
+ doctrine: '吉凶相抵,不注宜亦不注忌',
611
+ withDe: DECISION.PING, withoutDe: DECISION.PING,
612
+ };
613
+
614
+ const GRADE_RULE_BY_KEY = Object.fromEntries(GRADE_RULES.map((r) => [r.key, r]));
615
+
616
+ /** 六等比值阈值:本仓校准,不是古籍常数。 */
617
+ export const GRADE_THRESHOLDS = {
618
+ up: 3 / 2,
619
+ upCi: 1,
620
+ mid: 2 / 3,
621
+ midCi: 1 / 2,
622
+ xiaXiaWeight: 12,
623
+ };
624
+
625
+ /** 吉项权重(本仓校准,按卷十对神煞轻重的相对比较分档)。 */
626
+ export const XING_GOOD = { 建: 1, 除: 2, 满: 2, 平: 1, 定: 2, 执: 1, 破: 1, 危: 1, 成: 2, 收: 1, 开: 3, 闭: 1 };
627
+ export const SHENSHA_GOOD = {
628
+ 天德: 3, 月德: 3, 天德合: 3, 月德合: 3, 天赦: 3, 天愿: 3,
629
+ 月恩: 2, 四相: 2, 时德: 2, 王日: 2, 官日: 2, 守日: 2, 相日: 2, 民日: 2, 临日: 2,
630
+ 三合: 2, 六合: 2, 五合: 2, 五富: 2, 天仓: 2, 母仓: 2, 驿马: 2, 天马: 2, 不将: 2,
631
+ 鸣吠: 2, 鸣吠对: 2, 解神: 2, 除神: 2, 天后: 2, 普护: 2, 福生: 2, 圣心: 2, 益后: 2, 续世: 2,
632
+ 阳德: 2, 阴德: 2, 天恩: 1, 月空: 1,
633
+ };
634
+
635
+ /** 凶项权重(同上;月破/三煞/月刑/月厌按卷十「忌同月破」「所忌与三煞同」分档)。 */
636
+ export const XING_BAD = { 建: 2, 除: 1, 满: 2, 平: 2, 定: 1, 执: 1, 破: 4, 危: 1, 成: 1, 收: 2, 开: 1, 闭: 1 };
637
+ export const SHENSHA_BAD = {
638
+ 月破: 4, 大耗: 4, 四忌: 4, 四穷: 4,
639
+ 劫煞: 3, 灾煞: 3, 月煞: 3, 月刑: 3, 月厌: 3, 往亡: 3, 天狗: 3,
640
+ 月害: 2, 大时: 2, 大败: 2, 天吏: 2, 四废: 2, 五墓: 2, 天贼: 2, 厌对: 2, 九空: 2,
641
+ 重日: 2, 复日: 2, 土符: 2, 地囊: 2, 五离: 2, 八专: 2, 游祸: 2, 大煞: 2, 四击: 2, 天火: 2,
642
+ 小耗: 1, 四耗: 1, 归忌: 1, 九坎: 1, 九焦: 1, 五虚: 1, 月虚: 1, 地火: 1, 咸池: 1,
643
+ 兵禁: 1, 血忌: 1, 六仪: 1, 招摇: 1,
644
+ };
645
+ const DAY_TABOO_WEIGHT = 2;
646
+
647
+ // 卷十「遇德」列:德神含德合、天赦、天愿(原文或称「德愿」「德赦」)。
648
+ const DE_COLUMN = ['天德', '月德', '天德合', '月德合', '天赦', '天愿'];
649
+ // 月支三合局分组,用于卷十「某月值某日」的条件。
650
+ const MONTH_GROUP = {
651
+ 寅: '寅申巳亥', 申: '寅申巳亥', 巳: '寅申巳亥', 亥: '寅申巳亥',
652
+ 辰: '辰戌丑未', 戌: '辰戌丑未', 丑: '辰戌丑未', 未: '辰戌丑未',
653
+ 子: '子午卯酉', 午: '子午卯酉', 卯: '子午卯酉', 酉: '子午卯酉',
654
+ };
655
+
656
+ /**
657
+ * 德神制化表。cls:
658
+ * keJie 德神可解(卷十「与德合并则吉胜」「余皆不忌」)
659
+ * buJie 德神犹忌(「德神临此失力」「不能化解」「犹忌」)
660
+ * wuShe 与德义不相属(「与并犹忌」,不因德而解)
661
+ * anyOf 为可起作用的德神名单;whenXing / whenName / whenMonthXing 为卷十的月日条件
662
+ * (whenMonthXing 逐月指定建除,如「巳月值平日」);
663
+ * residualEntries 是「余皆不忌」之后仍然忌的卷十一次目。
664
+ */
665
+ export const DE_RULES = {
666
+ 月建: {
667
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合', '天赦', '月恩', '四相'],
668
+ residualEntries: ['兴造动土', '破土'],
669
+ quote: '与天德月德天德合月德合天赦月恩四相并,止忌营建宫室修宫室缮城郭筑堤防兴造动土修仓库修置产室开渠穿井安碓硙补垣修饰垣墙平治道涂破屋坏垣伐木栽种破土,余皆不忌',
670
+ },
671
+ 月破: {
672
+ cls: 'buJie', anyOf: [],
673
+ quote: '月破为月建之冲又为月建气绝之地,德神临此失力不能为福,故与并犹忌',
674
+ },
675
+ 满日: {
676
+ cls: 'keJie', anyOf: ['月德', '天德合', '月德合', '月恩', '四相'],
677
+ whenName: { 寅: '守日', 申: '守日', 子: '相日', 午: '相日', 卯: '相日', 酉: '相日' },
678
+ quote: '寅申月值守日,子午卯酉月值相日,与月德天德合月德合月恩四相并,则不忌',
679
+ },
680
+ 闭日: {
681
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合', '天赦', '天愿'],
682
+ whenName: { 子: '王日', 午: '王日', 卯: '王日', 酉: '王日', 辰: '官日', 戌: '官日', 丑: '官日', 未: '官日' },
683
+ quote: '子午卯酉月值王日,辰戌丑未月值官日天吏,与天德月德天德合月德合天赦天愿并,凡所忌者则不注宜,所宜者亦不注忌',
684
+ },
685
+ 劫煞: {
686
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合'],
687
+ whenXing: { 寅申巳亥: '收', 辰戌丑未: '除', 子午卯酉: '执' },
688
+ residualEntries: ['求医疗病'],
689
+ quote: '与天德月德天德合月德合并,止忌安抚边境选将训兵出师求医疗病,余皆不忌',
690
+ },
691
+ 灾煞: {
692
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合'],
693
+ whenXing: { 寅申巳亥: '开', 辰戌丑未: '满' },
694
+ residualEntries: ['求医疗病'],
695
+ quote: '寅申巳亥月值开日,辰戌丑未月值满日,与天德月德天德合月德合并,止忌安抚边境选将训兵出师求医疗病,余皆不忌',
696
+ },
697
+ 月煞: {
698
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合', '天愿'],
699
+ whenXing: { 子午卯酉: '危' },
700
+ residualEntries: ['求医疗病'],
701
+ quote: '卯酉月值六合,子午月值月害,与德愿并而后从德愿所宜;寅申巳亥月值闭日、辰戌丑未月值平日,凶又逢凶,不与德神并则诸事皆忌',
702
+ },
703
+ 月刑: {
704
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合', '天愿'],
705
+ whenMonthXing: { 巳: '平' },
706
+ residualEntries: ['求医疗病'],
707
+ quote: '巳月值平日相日六合,与德愿并则化凶为吉,止忌军事疗病,余皆不忌',
708
+ },
709
+ 月害: {
710
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合'],
711
+ whenMonthXing: { 卯: '除', 酉: '除', 丑: '执', 未: '执' },
712
+ residualEntries: ['求医疗病'],
713
+ quote: '卯酉月值除日守日,丑未月值执日大时,必与德神并而后止忌军事疗病,余不忌',
714
+ },
715
+ 月厌: {
716
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合'],
717
+ whenMonthXing: { 寅: '成', 申: '成', 丑: '开', 未: '开' },
718
+ residualEntries: ['行幸遣使', '上官赴任', '结婚姻', '纳采问名', '嫁娶', '求医疗病', '般移'],
719
+ quote: '寅申月值成日,丑未月值开日,再与德神并则吉胜矣,止忌行幸遣使安抚边境选将训兵出师上官赴任临政亲民结婚姻纳采问名嫁娶求医疗病般移远回栽种,余皆不忌',
720
+ },
721
+ 大时: {
722
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合'],
723
+ whenXing: { 寅申巳亥: '除', 辰戌丑未: '执' },
724
+ residualEntries: [],
725
+ quote: '寅申巳亥月值除日官日,辰戌月值执日六合,丑未月值执日六害,与德神并则吉胜,止忌军事,余皆不忌',
726
+ },
727
+ 天吏: {
728
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合'],
729
+ whenXing: { 寅申巳亥: '危' },
730
+ residualEntries: ['求医疗病'],
731
+ quote: '寅申巳亥月值危日,与德合并则吉胜,止忌军事疗病,余皆不忌',
732
+ },
733
+ 厌对: {
734
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合', '天赦'],
735
+ residualEntries: [],
736
+ quote: '与天德月德天德合月德合天赦并则不忌',
737
+ },
738
+ 复日: {
739
+ cls: 'keJie', anyOf: ['天德', '月德', '天德合', '月德合', '天赦', '六合'],
740
+ residualEntries: [],
741
+ quote: '今定为鸣吠遇复日则忌,德赦六合遇复日则不忌',
742
+ },
743
+ 天狗: {
744
+ cls: 'buJie', anyOf: [],
745
+ quote: '申月又为天狗忌祭祀,与德合并犹忌',
746
+ },
747
+ 游祸: {
748
+ cls: 'buJie', anyOf: [],
749
+ quote: '与德合天赦并犹忌,其旺如故故犹忌也',
750
+ },
751
+ 四忌: {
752
+ cls: 'buJie', anyOf: [],
753
+ quote: '此八日皆以旺极为凶,故德神不能化解',
754
+ },
755
+ 四穷: {
756
+ cls: 'buJie', anyOf: [],
757
+ quote: '此八日皆以旺极为凶,故德神不能化解',
758
+ },
759
+ 往亡: {
760
+ cls: 'buJie', anyOf: [],
761
+ quote: '虽值德合赦愿不能无疑,故与并犹忌',
762
+ },
763
+ 九坎: {
764
+ cls: 'wuShe', anyOf: [],
765
+ quote: '九坎九焦忌见义例,与德合义不相属,故与并犹忌',
766
+ },
767
+ 九焦: {
768
+ cls: 'wuShe', anyOf: [],
769
+ quote: '九坎九焦忌见义例,与德合义不相属,故与并犹忌',
770
+ },
771
+ 土符: {
772
+ cls: 'wuShe', anyOf: [],
773
+ quote: '土符地囊归忌血忌仿此',
774
+ },
775
+ 地囊: {
776
+ cls: 'wuShe', anyOf: [],
777
+ quote: '土符地囊归忌血忌仿此',
778
+ },
779
+ 归忌: {
780
+ cls: 'wuShe', anyOf: [],
781
+ quote: '土符地囊归忌血忌仿此',
782
+ },
783
+ 血忌: {
784
+ cls: 'wuShe', anyOf: [],
785
+ quote: '土符地囊归忌血忌仿此',
786
+ },
787
+ };
788
+
789
+ /** 卷十依据:六等判词、制化表与校准说明。 */
790
+ export const ZERI_JUAN10_BASIS = {
791
+ text: '《钦定协纪辨方书》卷十「宜忌」吉凶轻重六等与德神制化',
792
+ edition: '四库全书本(清乾隆内府刊本;殆知阁整理本对校)',
793
+ gradeText: GRADE_TEXT,
794
+ corpusLines: '卷十 宜忌(殆知阁整理本 4576–5095 行;六等判词见 4853 行)',
795
+ grades: GRADE_RULES,
796
+ ping: GRADE_PING,
797
+ thresholds: GRADE_THRESHOLDS,
798
+ thresholdsNote: '吉凶权重与比值阈值(3/2、1、2/3、1/2)是本仓校准,不是古籍常数。',
799
+ deRules: Object.entries(DE_RULES).map(([name, r]) => ({
800
+ name,
801
+ cls: r.cls,
802
+ anyOf: r.anyOf,
803
+ residualEntries: r.residualEntries || [],
804
+ quote: r.quote,
805
+ })),
806
+ note: '六等判词逐字取卷十;德神制化按卷十各条按语编码。等第表的逐月逐日单元格未全量数字化,未编码的组合按保守值(不因德而解)处理。',
807
+ };
808
+
809
+ const DE_COLUMN_SET = new Set(DE_COLUMN);
810
+ const deRuleOfXing = (hit) => {
811
+ for (const n of hit.names || []) if (DE_RULES[n]) return DE_RULES[n];
812
+ return null;
813
+ };
814
+ const deRuleOfShensha = (name) => {
815
+ if (DE_RULES[name]) return DE_RULES[name];
816
+ if (name === '大耗') return DE_RULES.月破;
817
+ if (name === '大败') return DE_RULES.大时;
818
+ if (name === '月虚') return DE_RULES.月煞;
819
+ if (name === '地火') return DE_RULES.月厌;
820
+ if (name === '六仪' || name === '招摇') return DE_RULES.厌对;
821
+ return null;
822
+ };
823
+ const conditionMet = (rule, day) => {
824
+ if (rule.whenXing) {
825
+ const want = rule.whenXing[MONTH_GROUP[day.monthBranch]];
826
+ if (!want || want !== day.xing) return false;
827
+ }
828
+ if (rule.whenName) {
829
+ const want = rule.whenName[day.monthBranch];
830
+ if (!want || !day.names.includes(want)) return false;
831
+ }
832
+ if (rule.whenMonthXing) {
833
+ const want = rule.whenMonthXing[day.monthBranch];
834
+ if (!want || want !== day.xing) return false;
835
+ }
836
+ return true;
837
+ };
838
+ const anyOfMet = (rule, day) => Array.isArray(rule.anyOf) && rule.anyOf.some((n) => day.names.includes(n));
839
+
840
+ /**
841
+ * 按卷十「六等」与德神制化给当日定等。
842
+ * @param {ReturnType<typeof taskShenshaHits>} hits
843
+ * @param {ReturnType<typeof computeDayShensha>} day
844
+ * @returns {{key:string,label:string,doctrine:string,decision:string,withDe:boolean,de:string[],
845
+ * ji:number,xiong:number,score:number,ratio:number|null,good:object[],resolved:object[],
846
+ * remains:object[],forced:object[],allJiCombo:boolean,thresholds:object,note:string}}
847
+ */
848
+ export function gradeDay(hits, day) {
849
+ const entries = hits && Array.isArray(hits.entries) ? hits.entries : [];
850
+ const info = day && Array.isArray(day.names) ? day : { monthBranch: '', xing: '', names: [] };
851
+ const de = DE_COLUMN.filter((n) => DE_COLUMN_SET.has(n) && info.names.includes(n));
852
+ const withDe = de.length > 0;
853
+ const good = [];
854
+ const remains = [];
855
+ const resolved = [];
856
+ const forced = [];
857
+ let ji = 0;
858
+ let xiong = 0;
859
+
860
+ for (const hit of (hits && hits.use && hits.use.xing) || []) {
861
+ const w = XING_GOOD[hit.xing] || 1;
862
+ ji += w;
863
+ good.push({ name: xingHitText(hit), w, kind: 'xing' });
864
+ }
865
+ for (const n of (hits && hits.use && hits.use.shensha) || []) {
866
+ const w = SHENSHA_GOOD[n] || 1;
867
+ ji += w;
868
+ good.push({ name: n, w, kind: 'shensha' });
869
+ }
870
+
871
+ const settle = (label, w, rule, kind) => {
872
+ if (!rule) {
873
+ xiong += w;
874
+ remains.push({ name: label, w, cls: 'none', kind });
875
+ return;
876
+ }
877
+ if (rule.cls === 'buJie' || rule.cls === 'wuShe') {
878
+ xiong += w;
879
+ remains.push({ name: label, w, cls: rule.cls, kind });
880
+ return;
881
+ }
882
+ if (!conditionMet(rule, info) || !anyOfMet(rule, info)) {
883
+ xiong += w;
884
+ remains.push({ name: label, w, cls: rule.cls, kind, pending: true });
885
+ return;
886
+ }
887
+ if ((rule.residualEntries || []).some((e) => entries.includes(e))) {
888
+ xiong += w;
889
+ const item = { name: label, w, cls: rule.cls, kind, residual: true };
890
+ remains.push(item);
891
+ forced.push(item);
892
+ return;
893
+ }
894
+ resolved.push({ name: label, w, cls: rule.cls, kind });
895
+ };
896
+
897
+ for (const hit of (hits && hits.avoid && hits.avoid.xing) || []) {
898
+ settle(xingHitText(hit), XING_BAD[hit.xing] || 2, deRuleOfXing(hit), 'xing');
899
+ }
900
+ for (const n of (hits && hits.avoid && hits.avoid.shensha) || []) {
901
+ settle(n, SHENSHA_BAD[n] || 1, deRuleOfShensha(n), 'shensha');
902
+ }
903
+ for (const n of (hits && hits.avoid && hits.avoid.day) || []) {
904
+ xiong += DAY_TABOO_WEIGHT;
905
+ remains.push({ name: n, w: DAY_TABOO_WEIGHT, cls: 'day', kind: 'day' });
906
+ }
907
+ for (const n of (hits && hits.avoid && hits.avoid.lunar) || []) {
908
+ xiong += DAY_TABOO_WEIGHT;
909
+ remains.push({ name: n, w: DAY_TABOO_WEIGHT, cls: 'lunar', kind: 'lunar' });
910
+ }
911
+
912
+ const avoidNames = [
913
+ ...((hits && hits.avoid && hits.avoid.xing) || []).flatMap((h) => h.names || []),
914
+ ...((hits && hits.avoid && hits.avoid.shensha) || []),
915
+ ];
916
+ // 卷十:月厌叠月破/灾煞,虽与德神并仍诸事皆忌。
917
+ const allJiCombo = avoidNames.includes('月厌')
918
+ && (avoidNames.includes('月破') || avoidNames.includes('灾煞'));
919
+
920
+ let key;
921
+ if (ji === 0 && xiong === 0) key = '平';
922
+ else if (xiong === 0) key = '上';
923
+ else if (ji === 0) key = xiong >= GRADE_THRESHOLDS.xiaXiaWeight ? '下下' : '下';
924
+ else {
925
+ const ratio = ji / xiong;
926
+ if (ratio >= GRADE_THRESHOLDS.up) key = '上';
927
+ else if (ratio >= GRADE_THRESHOLDS.upCi) key = '上次';
928
+ else if (ratio >= GRADE_THRESHOLDS.mid) key = '中';
929
+ else if (ratio >= GRADE_THRESHOLDS.midCi) key = '中次';
930
+ else key = '下';
931
+ }
932
+ if (allJiCombo) key = '下下';
933
+
934
+ const rule = key === '平' ? GRADE_PING : GRADE_RULE_BY_KEY[key];
935
+ let decision = key === '平' ? DECISION.PING : (withDe ? rule.withDe : rule.withoutDe);
936
+ if (forced.length) decision = DECISION.JI;
937
+
938
+ const ratio = xiong === 0 ? null : Number((ji / xiong).toFixed(2));
939
+ return {
940
+ key,
941
+ label: rule.label,
942
+ doctrine: rule.doctrine,
943
+ decision,
944
+ withDe,
945
+ de,
946
+ ji,
947
+ xiong,
948
+ score: ji - xiong,
949
+ ratio,
950
+ good,
951
+ resolved,
952
+ remains,
953
+ forced,
954
+ allJiCombo,
955
+ thresholds: GRADE_THRESHOLDS,
956
+ note: '六等判词为卷十原文;吉凶权重与比值阈值为本仓校准,非古籍常数。',
957
+ };
958
+ }