dsh-metaphysics 0.65.0 → 0.66.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.
package/engine/bazi.js CHANGED
@@ -542,7 +542,15 @@ export function buildBazi(input) {
542
542
  const monthBranch = pillars.month.branch;
543
543
 
544
544
  // 神煞:表驱动、双查法(年干/日干、年支/日支、月支、日柱、时柱),详见 engine/shensha.js
545
- const shensha = computeBaziShensha(pillars, kongwang);
545
+ // gender 供勾绞煞/元辰辨阴阳男女;年柱纳音五行供学堂/词馆
546
+ const shensha = computeBaziShensha(pillars, kongwang, {
547
+ gender,
548
+ // nayin() 返回的是纳音名(字符串),不是对象——取五行须走 nayinOf()。
549
+ // 这里长期写成 nayin(yearIdx).element 恒为 undefined,导致学堂/词馆(纳音长生/临官)
550
+ // 与童子煞纳音法在真实排盘里从未命中过;单测因为直接喂了 yearNayinElement 而没暴露。
551
+ yearNayinElement: nayinOf(yearIdx).element,
552
+ yearKongwang: xunKongOf(pillars.year.ganzhi),
553
+ });
546
554
 
547
555
  for (const key of ['year', 'month', 'day', 'hour']) {
548
556
  const pl = pillars[key];
package/engine/shensha.js CHANGED
@@ -62,7 +62,10 @@ const YUEDE_HE = { 丙: '辛', 壬: '丁', 甲: '己', 庚: '乙' };
62
62
  const FUXING = {
63
63
  甲: ['寅', '子'], 丙: ['寅', '子'],
64
64
  乙: ['卯', '丑'], 癸: ['卯', '丑'],
65
- 丁: ['酉'], 戊: ['申'], 己: ['未'], 庚: ['午'], 辛: ['巳'], 壬: ['辰'],
65
+ // 丁取亥:本表所引《甲丙相邀入虎乡歌》原文作「戊猴己未丁宜亥」。
66
+ // 旧表写丁酉,与自家所引歌诀对不上——酉是「丁酉」这个干支的干,不是亥的形讹,
67
+ // 属后世把干支名误当查法所致。改从歌诀,并保留旧说于 rule 文本备查。
68
+ 丁: ['亥'], 戊: ['申'], 己: ['未'], 庚: ['午'], 辛: ['巳'], 壬: ['辰'],
66
69
  };
67
70
  // 天官贵人(通行表,流派有异)
68
71
  const TIANGUAN = { 甲: ['未'], 乙: ['辰'], 丙: ['巳'], 丁: ['酉'], 戊: ['戌'], 己: ['卯'], 庚: ['亥'], 辛: ['申'], 壬: ['午'], 癸: ['寅'] };
@@ -97,10 +100,16 @@ const WANGSHEN = { 寅午戌: ['巳'], 申子辰: ['亥'], 巳酉丑: ['申'],
97
100
  const JINYU = { 甲: ['辰'], 乙: ['巳'], 丙: ['未'], 丁: ['申'], 戊: ['未'], 己: ['申'], 庚: ['戌'], 辛: ['亥'], 壬: ['丑'], 癸: ['寅'] };
98
101
  // 禄神
99
102
  const LUSHEN = { 甲: ['寅'], 乙: ['卯'], 丙: ['巳'], 丁: ['午'], 戊: ['巳'], 己: ['午'], 庚: ['申'], 辛: ['酉'], 壬: ['亥'], 癸: ['子'] };
100
- // 羊刃:禄前一位(阳干论刃为主)
101
- const YANGREN = { 甲: ['卯'], 乙: ['辰'], 丙: ['午'], 丁: ['未'], 戊: ['午'], 己: ['未'], 庚: ['酉'], 辛: ['戌'], 壬: ['子'], 癸: ['丑'] };
102
- // 飞刃:羊刃六冲
103
- const FEIREN = { 甲: ['酉'], 乙: ['戌'], 丙: ['子'], 丁: ['丑'], 戊: ['子'], 己: ['丑'], 庚: ['卯'], 辛: ['辰'], 壬: ['午'], 癸: ['未'] };
103
+ // 羊刃(DeepSeek 派):帝旺本位,阴阳干都取「禄后一位」——即阳干帝旺、阴干亦帝旺。
104
+ // 阳干:甲卯 丙午 戊午 庚酉 壬子(十二长生帝旺位,历代无争议)。
105
+ // 阴干:乙寅 丁巳 己巳 辛申 癸亥(阴干逆行长生,帝旺在其长生进位,非禄前)。
106
+ // 旧表把阴干取成禄前一位(乙辰 丁未 己未 辛戌 癸丑),那一位其实是阴干的「衰」;
107
+ // 说「乙刃在辰」是宋以后禄命家把阳干式硬套到阴干的结果,与十二长生自相矛盾。
108
+ const YANGREN = { 甲: ['卯'], 乙: ['寅'], 丙: ['午'], 丁: ['巳'], 戊: ['午'], 己: ['巳'], 庚: ['酉'], 辛: ['申'], 壬: ['子'], 癸: ['亥'] };
109
+ // 羊刃六冲(由 YANGREN 逐干取对冲,勿手写,免两表再度走散)
110
+ const FEIREN = Object.fromEntries(
111
+ Object.entries(YANGREN).map(([stem, arr]) => [stem, arr.map((b) => BRANCHES[(BRANCHES.indexOf(b) + 6) % 12])]),
112
+ );
104
113
  // 流霞(通行表)
105
114
  const LIUXIA = { 甲: ['酉'], 乙: ['戌'], 丙: ['未'], 丁: ['申'], 戊: ['巳'], 己: ['午'], 庚: ['辰'], 辛: ['卯'], 壬: ['亥'], 癸: ['寅'] };
106
115
 
@@ -117,11 +126,40 @@ const TIAN_SHE = { 春: '戊寅', 夏: '甲午', 秋: '戊申', 冬: '甲子' };
117
126
  const SIFEI = { 春: ['庚申', '辛酉'], 夏: ['壬子', '癸亥'], 秋: ['甲寅', '乙卯'], 冬: ['丙午', '丁巳'] };
118
127
  // 童子煞:春秋生见寅子,冬夏生见卯未辰
119
128
  const TONGZI = { 春: ['寅', '子'], 秋: ['寅', '子'], 夏: ['卯', '未', '辰'], 冬: ['卯', '未', '辰'] };
129
+ // 童子煞(纳音法,与季节法并行):金木命见午卯、水火命见酉戌、土命见辰巳
130
+ // 口诀作「金木马卯合,水火鸡犬多,土命逢辰巳」——马=午、鸡=酉、犬=戌。
131
+ // 旧实现只做季节法,把纳音法写作「流派多歧,仅作参考」,等于漏了一半查法。
132
+ const TONGZI_NAYIN = { 金: ['午', '卯'], 木: ['午', '卯'], 水: ['酉', '戌'], 火: ['酉', '戌'], 土: ['辰', '巳'] };
120
133
  // 三奇贵人(须依序相连)
121
134
  const SANQI = ['甲戊庚', '乙丙丁', '壬癸辛'];
122
135
  // 金神:时柱见之
123
136
  const JINSHEN = ['乙丑', '己巳', '癸酉'];
124
137
 
138
+ // ── 问真神煞大全补缺批(0.66):口径全部按《问真神煞大全》55 章实读钉死 ──
139
+
140
+ // 天德合:天德之干五合/支六合(寅月天德丁→壬合…)
141
+ const TIANDE_HE = { 寅: '壬', 卯: '巳', 辰: '丁', 巳: '丙', 午: '寅', 未: '己', 申: '戊', 酉: '亥', 戌: '辛', 亥: '庚', 子: '申', 丑: '乙' };
142
+ // 血刃:按月支查支
143
+ const XUEYREN = { 寅: '丑', 卯: '未', 辰: '寅', 巳: '申', 午: '卯', 未: '酉', 申: '辰', 酉: '戌', 戌: '巳', 亥: '亥', 子: '午', 丑: '子' };
144
+ // 红艳煞:日干查支
145
+ const HONGYAN = { 甲: ['午'], 乙: ['午'], 丙: ['寅'], 丁: ['未'], 戊: ['辰'], 己: ['辰'], 庚: ['戌'], 辛: ['酉'], 壬: ['子'], 癸: ['申'] };
146
+ // 孤鸾煞:日柱
147
+ const GUALUAN = ['甲寅', '乙巳', '丙午', '丁巳', '戊午', '戊申', '辛亥', '壬子'];
148
+ // 十灵日:日柱
149
+ const SHILING = ['甲辰', '乙亥', '丙辰', '丁酉', '戊午', '庚戌', '庚寅', '辛亥', '壬寅', '癸未'];
150
+ // 八专:日柱
151
+ const BAZHUAN = ['甲寅', '乙卯', '丁未', '戊戌', '己未', '庚申', '辛酉', '癸丑'];
152
+ // 九丑:日柱
153
+ const JIUCHOU = ['丁酉', '戊子', '戊午', '己卯', '己酉', '辛卯', '辛酉', '壬子', '壬午'];
154
+ // 天转:寅卯辰月乙卯、巳午未月丙午、申酉戌月辛酉、亥子丑月壬子(日柱)
155
+ const TIANZH = { 春: '乙卯', 夏: '丙午', 秋: '辛酉', 冬: '壬子' };
156
+ // 地转:春辛卯、夏戊午、秋癸酉、冬丙子(日柱)
157
+ const DIZH = { 春: '辛卯', 夏: '戊午', 秋: '癸酉', 冬: '丙子' };
158
+ // 学堂(纳音长生查法,问真采用第一种):年纳音五行 → 支
159
+ const XUETANG = { 金: '巳', 木: '亥', 水: '申', 土: '申', 火: '寅' };
160
+ // 词馆(纳音临官查法):年纳音五行 → 支
161
+ const CIGUAN = { 金: '申', 木: '寅', 水: '亥', 土: '亥', 火: '巳' };
162
+
125
163
  // ── 规则工厂 ────────────────────────────────────────────────────
126
164
 
127
165
  /** 天干查支:天乙、太极、文昌、福星、天官、国印、天厨、禄神、羊刃、飞刃、金舆、流霞 */
@@ -194,8 +232,16 @@ const RULES = [
194
232
  const he = k && YUEDE[k] ? YUEDE_HE[YUEDE[k]] : null;
195
233
  return he ? [S(he)] : [];
196
234
  }),
235
+ byMonth('天德合', '贵人', 'good',
236
+ '天德之合:寅月丁德→壬合、卯月申→巳、辰月壬→丁、巳月辛→丙、午月亥→寅、未月甲→己、申月癸→戊、酉月寅→亥、戌月丙→辛、亥月乙→庚、子月巳→申、丑月庚→乙(月支查干或支;无天德时天德合同样起作用)',
237
+ (ctx) => {
238
+ const ch = TIANDE_HE[ctx.pillars.month.branch];
239
+ if (!ch) return [];
240
+ return STEMS_LIST.includes(ch) ? [S(ch)] : [T(ch)];
241
+ }),
197
242
  byStem('福星贵人', '贵人', 'good',
198
- '甲丙见寅子,乙癸见卯丑,丁见酉、戊见申、己见未、庚见午、辛见巳、壬见辰(年干、日干查四支)',
243
+ '甲丙见寅子,乙癸见卯丑,戊见申、己见未、丁见亥、庚见午、辛见巳、壬见辰(年干、日干查四支)。'
244
+ + '出自《甲丙相邀入虎乡歌》「戊猴己未丁宜亥」;旧本有作丁见酉者,与歌诀不合,本插件从歌诀',
199
245
  FUXING, ['year', 'day']),
200
246
  byStem('天官贵人', '贵人', 'good',
201
247
  '甲未、乙辰、丙巳、丁酉、戊戌、己卯、庚亥、辛申、壬午、癸寅(年干、日干查四支;此表流派有异)',
@@ -256,26 +302,61 @@ const RULES = [
256
302
  byStem('金舆', '婚姻', 'good',
257
303
  '禄前二位:甲龙乙蛇丙戊羊、丁己申宫庚犬乡、辛猪壬牛癸虎位(日干查四支)',
258
304
  JINYU, ['day']),
305
+ byStem('红艳煞', '婚姻', 'neutral',
306
+ '甲乙午、丙寅、丁未、戊己辰、庚戌、辛酉、壬子、癸申(日干查四支)',
307
+ HONGYAN, ['day']),
308
+ byPillarGz('孤鸾煞', '婚姻', 'bad',
309
+ '日柱甲寅、乙巳、丙午、丁巳、戊午、戊申、辛亥、壬子(日柱见之;又名呻吟煞)',
310
+ GUALUAN),
311
+ // 勾绞煞:桃花六冲之位,阳男阴女命前三辰为勾、后三辰为绞;阴男阳女反之
312
+ byRefBranch('勾绞煞', '婚姻', 'bad',
313
+ '桃花六冲之位:子见卯酉、丑见辰戌、寅见巳亥……(年支查余三支;阳男阴女命前三辰为勾后三为绞,阴男阳女反之)',
314
+ (ctx) => {
315
+ const idx = BRANCHES.indexOf(ctx.pillars.year.branch);
316
+ const qian = BRANCHES[(idx + 3) % 12];
317
+ const hou = BRANCHES[(idx + 9) % 12];
318
+ const gouxian = ctx.gender === 'male'
319
+ ? ((idx % 2) === 0) // 阳男:命前为勾
320
+ : ((idx % 2) === 1); // 阴女:命前为勾
321
+ return gouxian
322
+ ? [{ kind: 'branch', ch: qian, label: '勾' }, { kind: 'branch', ch: hou, label: '绞' }]
323
+ : [{ kind: 'branch', ch: hou, label: '勾' }, { kind: 'branch', ch: qian, label: '绞' }];
324
+ }),
325
+ // 元辰:阳男阴女年支后一辰冲位、阴男阳女前一辰冲位(问真:阳男阴女子年未…阴男阳女子年巳…)
326
+ byRefBranch('元辰', '凶煞', 'bad',
327
+ '阳男阴女:子见未、丑见申、寅见酉……(年支次一位的对冲);阴男阳女:子见巳、丑见午……(年支退一位的对冲)(年支查余三支;又名毛头星、大耗)',
328
+ (ctx) => {
329
+ const idx = BRANCHES.indexOf(ctx.pillars.year.branch);
330
+ const yangNan = ctx.gender === 'male'
331
+ ? ((idx % 2) === 0)
332
+ : ((idx % 2) === 1);
333
+ const base = yangNan ? (idx + 1) % 12 : (idx + 11) % 12;
334
+ return [T(BRANCHES[(base + 6) % 12])];
335
+ }),
259
336
 
260
337
  // 性情
261
338
  byTrio('华盖', '性情', 'neutral',
262
339
  '寅午戌见戌、申子辰见辰、巳酉丑见丑、亥卯未见未(年支、日支双查)',
263
340
  HUAGAI),
264
341
  byStem('羊刃', '性情', 'bad',
265
- '禄前一位:甲卯、乙辰、丙戊午、丁己未、庚酉、辛戌、壬子、癸丑(日干查四支;阳干论刃为主)',
342
+ '帝旺之位:甲卯、乙寅、丙午、丁巳、戊午、己巳、庚酉、辛申、壬子、癸亥(日干查四支)。'
343
+ + '旧本把阴干记作禄前一位(乙辰、丁未、己未、辛戌、癸丑),那一位实为阴干之「衰」,与十二长生自相矛盾,本插件不取',
266
344
  YANGREN, ['day']),
267
345
  byStem('飞刃', '性情', 'bad',
268
- '羊刃六冲:甲酉、乙戌、丙子、丁丑、戊子、己丑、庚卯、辛辰、壬午、癸未(日干查四支)',
346
+ '羊刃六冲(由羊刃位逐干对冲推得,不另立表):甲酉、乙申、丙子、丁亥、戊子、己亥、庚卯、辛寅、壬午、癸巳(日干查四支)',
269
347
  FEIREN, ['day']),
270
348
  byPillarGz('魁罡', '性情', 'neutral',
271
349
  '日柱庚辰、庚戌、壬辰、戊戌(日柱见之)',
272
350
  KUIGANG),
273
351
  byMonth('童子煞', '性情', 'neutral',
274
- '春秋生见寅子,冬夏生见卯未辰(取季节口诀,查日支、时支;另有纳音五行一法,流派多歧,仅作参考)',
352
+ '两法并行:①季节法——春秋生见寅子,冬夏生见卯未辰(查日支、时支);'
353
+ + '②纳音法——金木命见午卯,水火命见酉戌,土命见辰巳(年柱纳音查日支、时支)。任一中即算',
275
354
  (ctx) => {
355
+ const set = new Set();
276
356
  const season = SEASON[ctx.pillars.month.branch];
277
- const list = season && TONGZI[season];
278
- return list ? list.map(T) : [];
357
+ for (const b of (season && TONGZI[season]) || []) set.add(b);
358
+ for (const b of TONGZI_NAYIN[ctx.nayinElement] || []) set.add(b);
359
+ return [...set].map(T);
279
360
  }, ['day', 'hour']),
280
361
  byPillarGz('六秀日', '性情', 'good',
281
362
  '日柱丙午、丁未、戊子、戊午、己丑、己未(日柱见之)',
@@ -286,6 +367,15 @@ const RULES = [
286
367
  byPillarGz('日贵', '性情', 'good',
287
368
  '日柱丁酉、丁亥、癸巳、癸卯(日柱见之)',
288
369
  RIGUI),
370
+ byPillarGz('十灵日', '性情', 'good',
371
+ '日柱甲辰、乙亥、丙辰、丁酉、戊午、庚戌、庚寅、辛亥、壬寅、癸未(日柱见之;「男带十灵好文章,女带十灵好衣裳」)',
372
+ SHILING),
373
+ byPillarGz('八专', '性情', 'neutral',
374
+ '日柱甲寅、乙卯、丁未、戊戌、己未、庚申、辛酉、癸丑(日柱见之;干支同气坐禄或冠带)',
375
+ BAZHUAN),
376
+ byPillarGz('九丑', '性情', 'neutral',
377
+ '日柱丁酉、戊子、戊午、己卯、己酉、辛卯、辛酉、壬子、壬午(日柱见之;「丑」指名声风评非容貌)',
378
+ JIUCHOU),
289
379
 
290
380
  // 事业
291
381
  byStem('禄神', '事业', 'good',
@@ -310,10 +400,19 @@ const RULES = [
310
400
  WANGSHEN),
311
401
  {
312
402
  name: '空亡', cat: '凶煞', luck: 'bad',
313
- rule: '以日柱所在旬的空亡反查各柱地支(日柱旬空)',
403
+ // 日柱旬空为古法正传;年柱旬空另有一派(以年柱所在旬的空亡查余三支),
404
+ // 两派所据都是「本旬无支可用」,逻辑同构,故并列而不取此舍彼。
405
+ rule: '以日柱所在旬的空亡反查各柱地支(日柱旬空,主法);另有年柱旬空一派,一并标出',
314
406
  refs: ['day'], refKind: 'branch', viaLabel: '日柱旬空',
315
407
  targets: (ctx) => (ctx.kongwang || []).map(T),
316
408
  },
409
+ {
410
+ name: '年空', cat: '凶煞', luck: 'bad',
411
+ rule: '以年柱所在旬的空亡反查其余三支(年柱旬空;古法多取日柱旬空,此派并存,命中单独标注)',
412
+ refs: ['year'], refKind: 'branch', viaLabel: '年柱旬空',
413
+ slots: ['month', 'day', 'hour'],
414
+ targets: (ctx) => (ctx.yearKongwang || []).map(T),
415
+ },
317
416
  byPillarGz('十恶大败', '凶煞', 'bad',
318
417
  '日柱甲辰、乙巳、丙申、丁亥、戊戌、己丑、庚辰、辛巳、壬申、癸亥(日柱见之)',
319
418
  SHIE_DABAI),
@@ -330,6 +429,30 @@ const RULES = [
330
429
  byStem('流霞', '凶煞', 'bad',
331
430
  '甲酉、乙戌、丙未、丁申、戊巳、己午、庚辰、辛卯、壬亥、癸寅(日干查四支;通行表)',
332
431
  LIUXIA, ['day']),
432
+ byMonth('血刃', '凶煞', 'bad',
433
+ '寅月丑、卯月未、辰月寅、巳月申、午月卯、未月酉、申月辰、酉月戌、戌月巳、亥月亥、子月午、丑月子(月支查四支)',
434
+ (ctx) => {
435
+ const ch = XUEYREN[ctx.pillars.month.branch];
436
+ return ch ? [T(ch)] : [];
437
+ }),
438
+ byRefBranch('丧门', '凶煞', 'bad',
439
+ '年支前两位:子见寅、丑见卯……(年支查余三支)',
440
+ (ctx) => {
441
+ const idx = BRANCHES.indexOf(ctx.pillars.year.branch);
442
+ return [T(BRANCHES[(idx + 2) % 12])];
443
+ }),
444
+ byRefBranch('吊客', '凶煞', 'bad',
445
+ '年支后两位:子见戌、丑见亥……(年支查余三支)',
446
+ (ctx) => {
447
+ const idx = BRANCHES.indexOf(ctx.pillars.year.branch);
448
+ return [T(BRANCHES[(idx + 10) % 12])];
449
+ }),
450
+ byRefBranch('披麻', '凶煞', 'bad',
451
+ '年支后三位:子见酉、丑见戌……(年支查余三支)',
452
+ (ctx) => {
453
+ const idx = BRANCHES.indexOf(ctx.pillars.year.branch);
454
+ return [T(BRANCHES[(idx + 9) % 12])];
455
+ }),
333
456
 
334
457
  // 格局
335
458
  byMonth('天赦日', '格局', 'good',
@@ -342,15 +465,55 @@ const RULES = [
342
465
  byPillarGz('金神', '格局', 'neutral',
343
466
  '时柱乙丑、己巳、癸酉(时柱见之;严格成格另需日主身旺,此仅记星)',
344
467
  JINSHEN, 'hour'),
468
+ // 拱禄:五日五时,日时同干、虚拱禄支(癸亥日癸丑时拱子、癸丑日癸亥时拱子、丁巳日丁未时拱午、己未日己巳时拱午、戊辰日戊午时拱巳)
469
+ {
470
+ name: '拱禄', cat: '格局', luck: 'good',
471
+ rule: '日时同干而禄位虚拱:癸亥日癸丑时、癸丑日癸亥时(拱子),丁巳日丁未时、己未日己巳时(拱午),戊辰日戊午时(拱巳);只宜虚拱,忌填实空亡',
472
+ custom: (ctx) => {
473
+ const GONG = { '癸亥|癸丑': '子', '癸丑|癸亥': '子', '丁巳|丁未': '午', '己未|己巳': '午', '戊辰|戊午': '巳' };
474
+ const k = `${ctx.pillars.day.ganzhi}|${ctx.pillars.hour.ganzhi}`;
475
+ return GONG[k] ? [{ pillars: ['day', 'hour'], via: '日时', target: GONG[k] }] : [];
476
+ },
477
+ },
478
+ // 天转 / 地转:以月支定季节查日柱(五行旺极之日,盛极而衰)
479
+ byMonth('天转', '凶煞', 'bad',
480
+ '春乙卯、夏丙午、秋辛酉、冬壬子(月支定季节查日柱;「春兔夏马天地转,秋鸡冬鼠便为殃」)',
481
+ (ctx) => {
482
+ const season = SEASON[ctx.pillars.month.branch];
483
+ const gz = ctx.pillars.day.ganzhi;
484
+ return season && TIANZH[season] === gz ? [GZ(gz)] : [];
485
+ }, ['day']),
486
+ byMonth('地转', '凶煞', 'bad',
487
+ '春辛卯、夏戊午、秋癸酉、冬丙子(月支定季节查日柱)',
488
+ (ctx) => {
489
+ const season = SEASON[ctx.pillars.month.branch];
490
+ const gz = ctx.pillars.day.ganzhi;
491
+ return season && DIZH[season] === gz ? [GZ(gz)] : [];
492
+ }, ['day']),
493
+ // 学堂 / 词馆:年柱纳音五行的长生 / 临官之支(问真采用禄命法第一种;正学堂另需干支同见,此仅记支)
494
+ {
495
+ name: '学堂', cat: '文星', luck: 'good',
496
+ rule: '年纳音五行之长生:金见巳、木见亥、水见申、土见申、火见寅(年纳音查月日时支;学堂为纳音长生,主人聪明文才出众)',
497
+ refs: ['year'], refKind: 'branch', viaLabel: '年纳音', slots: ['month', 'day', 'hour'],
498
+ targets: (ctx) => [T(XUETANG[ctx.nayinElement] || '')].filter((t) => t.ch),
499
+ },
500
+ {
501
+ name: '词馆', cat: '文星', luck: 'good',
502
+ rule: '年纳音五行之临官:金见申、木见寅、水见亥、土见亥、火见巳(年纳音查月日时支;词馆为纳音临官,主文章学识)',
503
+ refs: ['year'], refKind: 'branch', viaLabel: '年纳音', slots: ['month', 'day', 'hour'],
504
+ targets: (ctx) => [T(CIGUAN[ctx.nayinElement] || '')].filter((t) => t.ch),
505
+ },
345
506
  {
346
507
  name: '天罗', cat: '凶煞', luck: 'bad',
347
- rule: '四支见戌、亥俱全(戌亥为天罗)',
348
- custom: (ctx) => chartPattern(ctx, ['戌', '亥']),
508
+ rule: '戌亥为天罗。法一以年支/日支查余三支:戌见亥、亥见戌即算,不要求四支俱全;'
509
+ + '法二以年柱纳音查日支:火命见戌或亥。两法任一成立即命中(男忌天罗)',
510
+ custom: (ctx) => luowangPair(ctx, '戌', '亥', '天罗', ['火']),
349
511
  },
350
512
  {
351
513
  name: '地网', cat: '凶煞', luck: 'bad',
352
- rule: '四支见辰、巳俱全(辰巳为地网)',
353
- custom: (ctx) => chartPattern(ctx, ['辰', '巳']),
514
+ rule: '辰巳为地网。法一以年支/日支查余三支:辰见巳、巳见辰即算,不要求四支俱全;'
515
+ + '法二以年柱纳音查日支:水命、土命见辰或巳。两法任一成立即命中(女忌地网)',
516
+ custom: (ctx) => luowangPair(ctx, '辰', '巳', '地网', ['水', '土']),
354
517
  },
355
518
  {
356
519
  name: '三奇贵人', cat: '格局', luck: 'good',
@@ -366,11 +529,29 @@ const RULES = [
366
529
  },
367
530
  ];
368
531
 
369
- /** 四支俱全即命中(天罗地网) */
370
- function chartPattern(ctx, branches) {
371
- const keys = PILLAR_KEYS.filter((k) => branches.includes(ctx.pillars[k].branch));
372
- if (keys.length < branches.length) return [];
373
- return [{ pillars: keys, via: '四支', target: branches.join('') }];
532
+ /**
533
+ * 天罗地网:两法并行,任一成立即命中。
534
+ * 法一(主):以年支/日支查余三支——戌见亥、亥见戌为天罗;辰见巳、巳见辰为地网。只需两支成对,
535
+ * 不要求四支俱全(旧实现要求「戌亥俱全」,四支里必须同时出现戌与亥,漏掉大半)。
536
+ * 法二(辅):以年柱纳音查日支——火命见戌或亥为天罗,水命、土命见辰或巳为地网,金木二命无。
537
+ * 命中时把「起查柱」与「应柱」都标出来,否则只看天罗在亥那一柱,读盘时找不到起因。
538
+ */
539
+ function luowangPair(ctx, a, b, label, nayinEls) {
540
+ const out = [];
541
+ // 法一:以年/日支查余三支(只需两支成对)
542
+ for (const ref of ['year', 'day']) {
543
+ const rb = ctx.pillars[ref].branch;
544
+ const want = rb === a ? b : rb === b ? a : null;
545
+ if (!want) continue;
546
+ const hits = PILLAR_KEYS.filter((k) => k !== ref && ctx.pillars[k].branch === want);
547
+ for (const k of hits) out.push({ pillars: [ref, k], via: BRANCH_VIA[ref], target: want });
548
+ }
549
+ // 法二:以年纳音查日支
550
+ if (nayinEls.includes(ctx.nayinElement) && (ctx.pillars.day.branch === a || ctx.pillars.day.branch === b)) {
551
+ out.push({ pillars: ['day'], via: `年纳音${ctx.nayinElement}`, target: ctx.pillars.day.branch });
552
+ }
553
+ void label;
554
+ return out;
374
555
  }
375
556
 
376
557
  // ── 执行 ────────────────────────────────────────────────────────
@@ -402,12 +583,19 @@ function runRule(r, ctx) {
402
583
  * 计算八字神煞
403
584
  * @param {object} pillars { year, month, day, hour },每柱需有 stem/branch/ganzhi
404
585
  * @param {string[]} kongwang 日柱旬空的两个地支
586
+ * @param {object} [opts] { gender?: 'male'|'female', yearNayinElement?: string }
587
+ * gender 供勾绞煞 / 元辰辨阴阳男女;yearNayinElement 供学堂 / 词馆(年柱纳音五行)
405
588
  * @returns {{ detail: object, groups: array, names: string[], count: number }}
406
589
  * detail[key] = [{ name, cat, luck, via, target, rule }]
407
590
  * groups = [{ cat, items: [{ name, luck, pillars, via, target, rule }] }]
408
591
  */
409
- export function computeBaziShensha(pillars, kongwang = []) {
410
- const ctx = { pillars, kongwang };
592
+ export function computeBaziShensha(pillars, kongwang = [], opts = {}) {
593
+ const ctx = {
594
+ pillars, kongwang,
595
+ gender: opts.gender,
596
+ nayinElement: opts.yearNayinElement,
597
+ yearKongwang: opts.yearKongwang || [],
598
+ };
411
599
  const items = [];
412
600
  for (const r of RULES) {
413
601
  const hits = runRule(r, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-metaphysics",
3
- "version": "0.65.0",
3
+ "version": "0.66.0",
4
4
  "type": "module",
5
5
  "description": "命理排盘(玄学 xuanxue)for DeepSeek Harness: nineteen systems and eleven quick references (almanac, solar–lunar conversion, …), with climate adjustment (tiaohou), the 120 Qiongtong Baojian entries, 1,062-char name library with per-character pinyin, Kangxi stroke counts, five-grid (五格/三才) analysis and mechanical phonetics checks, a 78-card tarot library on the Rider-Waite-Smith base (per-card English name, image description, core meaning and astrological correspondence, cross-checked against three published sources; court cards carry no zodiac by design and the chart says so), classical quotes (Ziping Zhenquan / Di Tian Sui / San Ming Tong Hui), hexagram body relations (六冲/六合/伏吟/反吟/化进神/化退神) plus yongshen placement (用神落位) with month/day strength (月建日辰旺衰) and whole-hexagram per-line strength (全卦逐爻旺衰) and response-time clues (应期线索:逢值/逢冲/逢合 + 月建五态快慢) for liuyao, plus an MBTI-style 16-type questionnaire and exact dayun handover dates (交运日) with annual suiyun hints (岁运并临/伏吟反吟), palace conditions (击刑/入墓), month states (旺相休囚死) and stem–palace relations (天盘干与宫生克) for qimen, flying four-transformations with chained flights (四化飞星串连), three-way-four-corner composite flights (三方四正复合飞化), switchable leap-month policy (闰月按日切分/整月归次月/整月归本月) eleven school switches (亮度/庚干四化/天马/天空/截空旬空/魁钺/天伤天使/长生/晚子时/身主/杂曜全表) for ziwei, the last one opting in to a 61-star minor-star (杂曜) table where each of the twelve extra stars carries its placement verse and the palace it landed in (verses accepted only when at least two independent published editions give the same table, homonymous stars never merged), caliber diffs for bazi/ziwei/qimen (same inputs, one switch, root-field changes), shadow-chart IDs (same normalized params → same ID; version bumps auto-diff chart fields), step-by-step deduction traces (liuyao najia→liuqin→shiying→fushen→strength; bazi strength score and pattern/yongshen; ziwei pattern conditions), and a bundled offline single-file page (命理排盘.html), a panel-to-chat handoff that asks the Harness about a finished chart, per-tab lazy engine chunks so the first-screen client bundle stays under 100KB gzip, and a public verification report (128 classical hexagram cases, 312 Zi Wei charts × 34 patterns, 12 dayun handover cases) generated from the same source that gates CI.",
6
6
  "keywords": [
@@ -85,7 +85,8 @@
85
85
  "stress": "node test/stress.test.mjs",
86
86
  "e2e:page": "node tools/e2e-dom.mjs",
87
87
  "e2e:panel": "node tools/e2e-panel.mjs",
88
- "check:verification": "node tools/check-verification.mjs"
88
+ "check:verification": "node tools/check-verification.mjs",
89
+ "gh:release": "node tools/gh-release.mjs"
89
90
  },
90
91
  "exports": {
91
92
  ".": "./index.js",