iching-shifa 1.1.0 → 1.3.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/README.md CHANGED
@@ -8,6 +8,8 @@
8
8
  - **时间起卦** - 按年月日时四柱起卦
9
9
  - **手动输入起卦** - 输入6位爻值字符串(6/7/8/9)
10
10
  - **纳甲排盘** - 完整的六爻排盘(本卦/之卦/互卦、六亲、六兽、世应、伏神等)
11
+ - **神煞排盘** - 按日支/月令/季节/日干计算神煞,并输出独立 JSON 字段
12
+ - **纳音信息** - 本卦和之卦六爻输出 60 甲子纳音
11
13
 
12
14
  ## 安装
13
15
 
@@ -82,6 +84,7 @@ interface PanResult {
82
84
  dayKong: string; // 日空
83
85
  hourKong: string; // 时空
84
86
  monthJian: string; // 月建
87
+ shenSha: ShenShaMap; // 神煞(独立 map)
85
88
  benGua: GuaPan; // 本卦
86
89
  zhiGua: GuaPan; // 之卦
87
90
  huGua: GuaPan; // 互卦
@@ -90,6 +93,16 @@ interface PanResult {
90
93
  explanation: string; // 断卦说明
91
94
  }
92
95
 
96
+ type ShenShaMap = Record<string, {
97
+ targetDiZhi: string[];
98
+ matches: {
99
+ guaKey: 'benGua' | 'zhiGua' | 'huGua';
100
+ position: number;
101
+ diZhi: string;
102
+ naJia: string;
103
+ }[];
104
+ }>;
105
+
93
106
  interface GuaPan {
94
107
  guaName: string; // 卦名
95
108
  palace: string; // 所属宫
@@ -101,6 +114,7 @@ interface GuaPan {
101
114
  tuanCi: string; // 彖辞
102
115
  shenYao?: number; // 身爻
103
116
  fuShen?: FuShenData[];// 伏神
117
+ pangFuShen?: FuShenData[];// 旁伏神
104
118
  }
105
119
 
106
120
  interface YaoData {
@@ -110,6 +124,7 @@ interface YaoData {
110
124
  tianGan: string; // 天干
111
125
  diZhi: string; // 地支
112
126
  naJia: string; // 纳甲
127
+ naYin?: string; // 纳音(本卦、之卦)
113
128
  wuXing: string; // 五行
114
129
  liuQin: string; // 六亲
115
130
  liuShou: string; // 六兽
@@ -138,10 +153,10 @@ bun test --watch
138
153
  ```bash
139
154
  $ bun test
140
155
 
141
- 12 pass
156
+ 19 pass
142
157
  0 fail
143
- 67 expect() calls
144
- Ran 12 tests across 1 file. [33.00ms]
158
+ 127 expect() calls
159
+ Ran 19 tests across 1 file. [28.00ms]
145
160
  ```
146
161
 
147
162
  ## 许可
package/dist/index.cjs CHANGED
@@ -232,6 +232,17 @@ function buildYaoString(upperNum, lowerNum, dongYao) {
232
232
  }
233
233
  return yaoArray.join("");
234
234
  }
235
+ function lueshifaOnce(groupSize) {
236
+ const left = 1 + Math.floor(Math.random() * 48);
237
+ const remainder = left % groupSize;
238
+ return remainder === 0 ? 1 : remainder + 1;
239
+ }
240
+ function lueshifa() {
241
+ const lowerNum = lueshifaOnce(8);
242
+ const upperNum = lueshifaOnce(8);
243
+ const dongYao = lueshifaOnce(6);
244
+ return buildYaoString(upperNum, lowerNum, dongYao);
245
+ }
235
246
  function threeNumberQiGua(num1, num2, num3) {
236
247
  const upperNum = modWithZero(num1, 8);
237
248
  const lowerNum = modWithZero(num2, 8);
@@ -5396,6 +5407,71 @@ const WU_XING_STARS = [
5396
5407
  "岁星",
5397
5408
  "熒惑"
5398
5409
  ];
5410
+ const NAYIN_60 = {
5411
+ "甲子": "海中金",
5412
+ "乙丑": "海中金",
5413
+ "丙寅": "炉中火",
5414
+ "丁卯": "炉中火",
5415
+ "戊辰": "大林木",
5416
+ "己巳": "大林木",
5417
+ "庚午": "路旁土",
5418
+ "辛未": "路旁土",
5419
+ "壬申": "剑锋金",
5420
+ "癸酉": "剑锋金",
5421
+ "甲戌": "山头火",
5422
+ "乙亥": "山头火",
5423
+ "丙子": "涧下水",
5424
+ "丁丑": "涧下水",
5425
+ "戊寅": "城头土",
5426
+ "己卯": "城头土",
5427
+ "庚辰": "白蜡金",
5428
+ "辛巳": "白蜡金",
5429
+ "壬午": "杨柳木",
5430
+ "癸未": "杨柳木",
5431
+ "甲申": "泉中水",
5432
+ "乙酉": "泉中水",
5433
+ "丙戌": "屋上土",
5434
+ "丁亥": "屋上土",
5435
+ "戊子": "霹雳火",
5436
+ "己丑": "霹雳火",
5437
+ "庚寅": "松柏木",
5438
+ "辛卯": "松柏木",
5439
+ "壬辰": "长流水",
5440
+ "癸巳": "长流水",
5441
+ "甲午": "沙中金",
5442
+ "乙未": "沙中金",
5443
+ "丙申": "山下火",
5444
+ "丁酉": "山下火",
5445
+ "戊戌": "平地木",
5446
+ "己亥": "平地木",
5447
+ "庚子": "壁上土",
5448
+ "辛丑": "壁上土",
5449
+ "壬寅": "金箔金",
5450
+ "癸卯": "金箔金",
5451
+ "甲辰": "覆灯火",
5452
+ "乙巳": "覆灯火",
5453
+ "丙午": "天河水",
5454
+ "丁未": "天河水",
5455
+ "戊申": "大驿土",
5456
+ "己酉": "大驿土",
5457
+ "庚戌": "钗钏金",
5458
+ "辛亥": "钗钏金",
5459
+ "壬子": "桑柘木",
5460
+ "癸丑": "桑柘木",
5461
+ "甲寅": "大溪水",
5462
+ "乙卯": "大溪水",
5463
+ "丙辰": "沙中土",
5464
+ "丁巳": "沙中土",
5465
+ "戊午": "天上火",
5466
+ "己未": "天上火",
5467
+ "庚申": "石榴木",
5468
+ "辛酉": "石榴木",
5469
+ "壬戌": "大海水",
5470
+ "癸亥": "大海水"
5471
+ };
5472
+ function getNaYin(ganZhi) {
5473
+ return NAYIN_60[ganZhi];
5474
+ }
5399
5475
  const GAN_ZHI_WUXING = {
5400
5476
  // 天干
5401
5477
  "甲": "木",
@@ -5583,6 +5659,146 @@ function getCurrentSolarTerm(year, month, day) {
5583
5659
  return "";
5584
5660
  }
5585
5661
  }
5662
+ const SHENSHA_ORDER = [
5663
+ "驿马",
5664
+ "桃花",
5665
+ "华盖",
5666
+ "天医",
5667
+ "天喜",
5668
+ "天马",
5669
+ "天乙贵人",
5670
+ "禄神",
5671
+ "文昌"
5672
+ ];
5673
+ function getDayBranchShenSha(dayZhi) {
5674
+ if (["申", "子", "辰"].includes(dayZhi)) {
5675
+ return { 驿马: ["寅"], 桃花: ["酉"], 华盖: ["辰"] };
5676
+ }
5677
+ if (["寅", "午", "戌"].includes(dayZhi)) {
5678
+ return { 驿马: ["申"], 桃花: ["卯"], 华盖: ["戌"] };
5679
+ }
5680
+ if (["巳", "酉", "丑"].includes(dayZhi)) {
5681
+ return { 驿马: ["亥"], 桃花: ["午"], 华盖: ["丑"] };
5682
+ }
5683
+ if (["亥", "卯", "未"].includes(dayZhi)) {
5684
+ return { 驿马: ["巳"], 桃花: ["子"], 华盖: ["未"] };
5685
+ }
5686
+ return { 驿马: [], 桃花: [], 华盖: [] };
5687
+ }
5688
+ function getTianYi(monthZhi) {
5689
+ const index = DI_ZHI.indexOf(monthZhi);
5690
+ if (index === -1) {
5691
+ return [];
5692
+ }
5693
+ return [DI_ZHI[(index + DI_ZHI.length - 1) % DI_ZHI.length]];
5694
+ }
5695
+ function getTianMa(monthZhi) {
5696
+ if (["寅", "申"].includes(monthZhi)) return ["午"];
5697
+ if (["卯", "酉"].includes(monthZhi)) return ["申"];
5698
+ if (["辰", "戌"].includes(monthZhi)) return ["戌"];
5699
+ if (["巳", "亥"].includes(monthZhi)) return ["子"];
5700
+ if (["子", "午"].includes(monthZhi)) return ["寅"];
5701
+ if (["丑", "未"].includes(monthZhi)) return ["辰"];
5702
+ return [];
5703
+ }
5704
+ function getSeasonByMonthZhi(monthZhi) {
5705
+ if (["寅", "卯", "辰"].includes(monthZhi)) return "春";
5706
+ if (["巳", "午", "未"].includes(monthZhi)) return "夏";
5707
+ if (["申", "酉", "戌"].includes(monthZhi)) return "秋";
5708
+ if (["亥", "子", "丑"].includes(monthZhi)) return "冬";
5709
+ return void 0;
5710
+ }
5711
+ function getTianXi(monthZhi) {
5712
+ const season = getSeasonByMonthZhi(monthZhi);
5713
+ if (season === "春") return ["戌"];
5714
+ if (season === "夏") return ["丑"];
5715
+ if (season === "秋") return ["辰"];
5716
+ if (season === "冬") return ["未"];
5717
+ return [];
5718
+ }
5719
+ function getTianYiGuiRen(dayGan) {
5720
+ if (["甲", "戊"].includes(dayGan)) return ["丑", "未"];
5721
+ if (["乙", "己"].includes(dayGan)) return ["子", "申"];
5722
+ if (["丙", "丁"].includes(dayGan)) return ["亥", "酉"];
5723
+ if (["庚", "辛"].includes(dayGan)) return ["午", "寅"];
5724
+ if (["壬", "癸"].includes(dayGan)) return ["卯", "巳"];
5725
+ return [];
5726
+ }
5727
+ function getLuShen(dayGan) {
5728
+ if (dayGan === "甲") return ["寅"];
5729
+ if (dayGan === "乙") return ["卯"];
5730
+ if (dayGan === "丙") return ["巳"];
5731
+ if (dayGan === "丁") return ["午"];
5732
+ if (dayGan === "戊") return ["巳"];
5733
+ if (dayGan === "己") return ["午"];
5734
+ if (dayGan === "庚") return ["申"];
5735
+ if (dayGan === "辛") return ["酉"];
5736
+ if (dayGan === "壬") return ["亥"];
5737
+ if (dayGan === "癸") return ["子"];
5738
+ return [];
5739
+ }
5740
+ function getWenChang(dayGan) {
5741
+ if (dayGan === "甲") return ["巳"];
5742
+ if (dayGan === "乙") return ["午"];
5743
+ if (dayGan === "丙") return ["申"];
5744
+ if (dayGan === "丁") return ["酉"];
5745
+ if (dayGan === "戊") return ["申"];
5746
+ if (dayGan === "己") return ["酉"];
5747
+ if (dayGan === "庚") return ["亥"];
5748
+ if (dayGan === "辛") return ["子"];
5749
+ if (dayGan === "壬") return ["寅"];
5750
+ if (dayGan === "癸") return ["卯"];
5751
+ return [];
5752
+ }
5753
+ function getShenShaTargets(dayGan, dayZhi, monthZhi) {
5754
+ const dayBranchShenSha = getDayBranchShenSha(dayZhi);
5755
+ return {
5756
+ 驿马: dayBranchShenSha.驿马,
5757
+ 桃花: dayBranchShenSha.桃花,
5758
+ 华盖: dayBranchShenSha.华盖,
5759
+ 天医: getTianYi(monthZhi),
5760
+ 天喜: getTianXi(monthZhi),
5761
+ 天马: getTianMa(monthZhi),
5762
+ 天乙贵人: getTianYiGuiRen(dayGan),
5763
+ 禄神: getLuShen(dayGan),
5764
+ 文昌: getWenChang(dayGan)
5765
+ };
5766
+ }
5767
+ function buildShenShaMap(dayGan, dayZhi, monthZhi, guaList) {
5768
+ const targets = getShenShaTargets(dayGan, dayZhi, monthZhi);
5769
+ const result = {};
5770
+ for (const name of SHENSHA_ORDER) {
5771
+ const targetDiZhi = targets[name];
5772
+ const matches = [];
5773
+ for (const gua of guaList) {
5774
+ for (const yao of gua.yaoList) {
5775
+ if (targetDiZhi.includes(yao.diZhi)) {
5776
+ matches.push({
5777
+ guaKey: gua.guaKey,
5778
+ position: yao.position,
5779
+ diZhi: yao.diZhi,
5780
+ naJia: yao.naJia
5781
+ });
5782
+ }
5783
+ }
5784
+ }
5785
+ result[name] = {
5786
+ targetDiZhi: [...targetDiZhi],
5787
+ matches
5788
+ };
5789
+ }
5790
+ return result;
5791
+ }
5792
+ const OPPOSITE_PALACE = {
5793
+ "乾": "坤",
5794
+ "坤": "乾",
5795
+ "坎": "离",
5796
+ "离": "坎",
5797
+ "震": "巽",
5798
+ "巽": "震",
5799
+ "艮": "兑",
5800
+ "兑": "艮"
5801
+ };
5586
5802
  function getGuaName(yaoString) {
5587
5803
  if (YAO_STRING_TO_GUA[yaoString]) {
5588
5804
  return YAO_STRING_TO_GUA[yaoString];
@@ -5627,7 +5843,7 @@ function getLiuShou(dayGan) {
5627
5843
  }
5628
5844
  return rotateList(shouList, startShou);
5629
5845
  }
5630
- function decodeGua(yaoString, dayGanZhi, isZhiGua = false) {
5846
+ function decodeGua(yaoString, dayGanZhi, isZhiGua = false, includeNaYin = true) {
5631
5847
  const guaName = getGuaName(yaoString);
5632
5848
  const palace = GUA_PALACE[guaName] || "乾";
5633
5849
  const palaceLevel = GUA_PALACE_LEVEL[guaName] || "本宮";
@@ -5672,7 +5888,7 @@ function decodeGua(yaoString, dayGanZhi, isZhiGua = false) {
5672
5888
  shenYaoIndex = shenYaoZhi;
5673
5889
  }
5674
5890
  }
5675
- yaoList.push({
5891
+ const yaoData = {
5676
5892
  position: i + 1,
5677
5893
  yaoValue,
5678
5894
  isMoving,
@@ -5683,7 +5899,11 @@ function decodeGua(yaoString, dayGanZhi, isZhiGua = false) {
5683
5899
  liuQin,
5684
5900
  liuShou,
5685
5901
  shiYing
5686
- });
5902
+ };
5903
+ if (includeNaYin) {
5904
+ yaoData.naYin = getNaYin(naJia);
5905
+ }
5906
+ yaoList.push(yaoData);
5687
5907
  }
5688
5908
  const desc = GUA_DESCRIPTIONS[guaName] || {};
5689
5909
  const guaCi = desc[0] || "";
@@ -5695,8 +5915,10 @@ function decodeGua(yaoString, dayGanZhi, isZhiGua = false) {
5695
5915
  const wuXingStarIndex = Math.floor(Math.random() * 5);
5696
5916
  const wuXingStar = WU_XING_STARS[wuXingStarIndex] || "鎮星";
5697
5917
  let fuShen;
5918
+ let pangFuShen;
5698
5919
  if (!isZhiGua) {
5699
5920
  fuShen = findFuShen(yaoList, palace, palaceWuXing);
5921
+ pangFuShen = findPangFuShen(yaoList, palace);
5700
5922
  }
5701
5923
  return {
5702
5924
  guaName,
@@ -5709,17 +5931,20 @@ function decodeGua(yaoString, dayGanZhi, isZhiGua = false) {
5709
5931
  yaoCi,
5710
5932
  tuanCi,
5711
5933
  shenYao: shenYaoIndex,
5712
- fuShen
5934
+ fuShen,
5935
+ pangFuShen
5713
5936
  };
5714
5937
  }
5715
5938
  function findFuShen(yaoList, palace, palaceWuXing) {
5716
- const existingLiuQin = new Set(yaoList.map((y) => y.liuQin));
5717
- const allLiuQin = ["父母", "兄弟", "官鬼", "妻财", "子孙"];
5718
- const missingLiuQin = allLiuQin.filter((lq) => !existingLiuQin.has(lq));
5719
- if (missingLiuQin.length === 0) {
5720
- return void 0;
5721
- }
5722
- const pureCode = PALACE_PURE_CODE[palace] || "777777";
5939
+ return buildFuShenFromPalace(yaoList, palace, palaceWuXing);
5940
+ }
5941
+ function findPangFuShen(yaoList, palace) {
5942
+ const oppositePalace = OPPOSITE_PALACE[palace] || "坤";
5943
+ const oppositeWuXing = PALACE_WUXING[oppositePalace] || "土";
5944
+ return buildFuShenFromPalace(yaoList, oppositePalace, oppositeWuXing);
5945
+ }
5946
+ function buildFuShenFromPalace(yaoList, sourcePalace, sourcePalaceWuXing) {
5947
+ const pureCode = PALACE_PURE_CODE[sourcePalace] || "777777";
5723
5948
  const pureLowerTrigram = CODE_TO_BAGUA[pureCode.slice(0, 3)] || "乾";
5724
5949
  const pureUpperTrigram = CODE_TO_BAGUA[pureCode.slice(3, 6)] || "乾";
5725
5950
  const pureLowerNajiaRaw = NAJIA_LOWER[pureLowerTrigram] || [];
@@ -5745,25 +5970,19 @@ function findFuShen(yaoList, palace, palaceWuXing) {
5745
5970
  const diZhi = DI_ZHI[najiaData[1]] || "子";
5746
5971
  const wuXing = WU_XING[najiaData[2]] || "金";
5747
5972
  const naJia = tianGan + diZhi;
5748
- const liuQin = wuXingToLiuQin(wuXing, palaceWuXing);
5749
- if (missingLiuQin.includes(liuQin)) {
5750
- const hostYao = yaoList[i];
5751
- fuShenList.push({
5752
- fuLiuQin: liuQin,
5753
- fuNaJia: naJia,
5754
- fuWuXing: wuXing,
5755
- hostYaoIndex: i,
5756
- hostNaJia: hostYao.naJia,
5757
- feiWuXing: hostYao.wuXing,
5758
- relation: getWuXingRelation(wuXing, hostYao.wuXing)
5759
- });
5760
- const idx = missingLiuQin.indexOf(liuQin);
5761
- if (idx > -1) {
5762
- missingLiuQin.splice(idx, 1);
5763
- }
5764
- }
5973
+ const liuQin = wuXingToLiuQin(wuXing, sourcePalaceWuXing);
5974
+ const hostYao = yaoList[i];
5975
+ fuShenList.push({
5976
+ fuLiuQin: liuQin,
5977
+ fuNaJia: naJia,
5978
+ fuWuXing: wuXing,
5979
+ hostYaoIndex: i,
5980
+ hostNaJia: hostYao.naJia,
5981
+ feiWuXing: hostYao.wuXing,
5982
+ relation: getWuXingRelation(wuXing, hostYao.wuXing)
5983
+ });
5765
5984
  }
5766
- return fuShenList.length > 0 ? fuShenList : void 0;
5985
+ return fuShenList;
5767
5986
  }
5768
5987
  function buildExplanation(yaoString, benGuaName, zhiGuaName, benGuaCi, zhiGuaCi, yaoCi) {
5769
5988
  const dongCount = countMovingYao(yaoString);
@@ -5810,7 +6029,17 @@ function decodePan(yaoString, options) {
5810
6029
  const zhiYaoString = getZhiGua(yaoString);
5811
6030
  const zhiGua = decodeGua(zhiYaoString, dayGZ, true);
5812
6031
  const huYaoString = getHuGua(yaoString);
5813
- const huGua = decodeGua(huYaoString, dayGZ, true);
6032
+ const huGua = decodeGua(huYaoString, dayGZ, true, false);
6033
+ const shenSha = buildShenShaMap(
6034
+ lunar.dayGanZhi.tian,
6035
+ lunar.dayGanZhi.di,
6036
+ lunar.monthGanZhi.di,
6037
+ [
6038
+ { guaKey: "benGua", yaoList: benGua.yaoList },
6039
+ { guaKey: "zhiGua", yaoList: zhiGua.yaoList },
6040
+ { guaKey: "huGua", yaoList: huGua.yaoList }
6041
+ ]
6042
+ );
5814
6043
  const dongYaoCount = countMovingYao(yaoString);
5815
6044
  const explanation = buildExplanation(
5816
6045
  yaoString,
@@ -5836,6 +6065,7 @@ function decodePan(yaoString, options) {
5836
6065
  dayKong,
5837
6066
  hourKong,
5838
6067
  monthJian,
6068
+ shenSha,
5839
6069
  benGua,
5840
6070
  zhiGua,
5841
6071
  huGua,
@@ -5885,6 +6115,7 @@ exports.JIAZI_60 = JIAZI_60;
5885
6115
  exports.JIEQI_NAMES = JIEQI_NAMES;
5886
6116
  exports.LIU_QIN = LIU_QIN;
5887
6117
  exports.LIU_SHOU = LIU_SHOU;
6118
+ exports.NAYIN_60 = NAYIN_60;
5888
6119
  exports.TIAN_GAN = TIAN_GAN;
5889
6120
  exports.WU_XING = WU_XING;
5890
6121
  exports.WU_XING_STARS = WU_XING_STARS;
@@ -5900,9 +6131,11 @@ exports.getGuaName = getGuaName;
5900
6131
  exports.getHourGanZhi = getHourGanZhi;
5901
6132
  exports.getHuGua = getHuGua;
5902
6133
  exports.getMovingYaoPositions = getMovingYaoPositions;
6134
+ exports.getNaYin = getNaYin;
5903
6135
  exports.getWuXingRelation = getWuXingRelation;
5904
6136
  exports.getZhiGua = getZhiGua;
5905
6137
  exports.isMovingYao = isMovingYao;
6138
+ exports.lueshifa = lueshifa;
5906
6139
  exports.manualQiGua = manualQiGua;
5907
6140
  exports.numberArrayQiGua = numberArrayQiGua;
5908
6141
  exports.rotateList = rotateList;