dsh-metaphysics 0.62.4 → 0.63.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/qimen.js CHANGED
@@ -32,6 +32,15 @@ const JU_NUMBERS_MAP = {
32
32
  寒露: '693', 立冬: '693', 处暑: '147', 霜降: '582', 小雪: '582', 大雪: '471',
33
33
  };
34
34
 
35
+ /** 供测试与文档引用:二十四节气三元局数的只读视图(B14 与《奇门遁甲元灵经》卷一逐格对拍) */
36
+ export const QIMEN_JU_TABLE = Object.freeze({ ...JU_NUMBERS_MAP });
37
+
38
+ /** 供测试与文档引用:九星八门定宫的只读视图(B14 与元灵经卷一、秘笈总序逐宫对拍) */
39
+ export const QIMEN_BASE_TABLES = Object.freeze({
40
+ stars: Object.freeze({ ...BASIC_STARS }),
41
+ gates: Object.freeze({ ...BASIC_GATES }),
42
+ });
43
+
35
44
  // ── 历法适配:把本插件的时间轴转成奇门引擎要的 snapshot ──
36
45
  const xunOf = (gz) => {
37
46
  const idx = indexOfGz(gz);
@@ -0,0 +1,218 @@
1
+ // 八大地支结构(炳祥课堂口径)
2
+ //
3
+ // 定位:只做「静态结构 + 宫位权重」的只读标注,不改旺衰、不改喜忌。
4
+ // 课堂口径来源:炳祥八字课程笔记(内部资料,不随包发布);
5
+ // 这里把「四个地支落在身强阵营还是身弱阵营」按年支定 A 档(年支那一档记 A,另一档记 B),
6
+ // 于是八种排布可以写成 AAAA / ABBB / AAAB / AABA / ABAA / AABB / ABAB / ABBA,
7
+ // 再从「异类那一支的宫位权重」读出静态从格概率:
8
+ // 年支独异 11 分 → 80% 从;时支独异 16 分 → 50-60% 从;
9
+ // 日支独异 27 分 → 20% 从;月支独异 36 分 → 99% 不从。
10
+ //
11
+ // 注意:这是**静态**概率。三合、三会、六合把某一支「合走」之后,阵营可能整体改写,
12
+ // 那时要以合化后的阵营重新排结构;课堂原话也是「静态条件之外,生克合化的演化会更多」。
13
+
14
+ const STEM_ELEMENT = {
15
+ 甲: '木', 乙: '木', 丙: '火', 丁: '火', 戊: '土',
16
+ 己: '土', 庚: '金', 辛: '金', 壬: '水', 癸: '水',
17
+ };
18
+ const GENERATES = { 木: '火', 火: '土', 土: '金', 金: '水', 水: '木' };
19
+
20
+ /** 干支宫位权重:天干合计 10、地支合计 90(日干为参照点,计 0) */
21
+ export const PILLAR_WEIGHT = {
22
+ stem: { year: 2, month: 5, day: 0, hour: 3 },
23
+ branch: { year: 11, month: 36, day: 27, hour: 16 },
24
+ };
25
+
26
+ /** 藏干固定百分比(课堂口径,同一支内合计 100) */
27
+ export const HIDDEN_PCT = {
28
+ 子: { 癸: 100 },
29
+ 丑: { 己: 20, 辛: 40, 癸: 40 },
30
+ 寅: { 甲: 65, 丙: 30, 戊: 5 },
31
+ 卯: { 乙: 100 },
32
+ 辰: { 戊: 45, 乙: 30, 癸: 25 },
33
+ 巳: { 丙: 65, 戊: 20, 庚: 15 },
34
+ 午: { 丁: 90, 己: 10 },
35
+ 未: { 己: 55, 丁: 40, 乙: 5 },
36
+ 申: { 庚: 65, 壬: 30, 戊: 5 },
37
+ 酉: { 辛: 100 },
38
+ 戌: { 戊: 55, 辛: 25, 丁: 20 },
39
+ 亥: { 壬: 70, 甲: 30 },
40
+ };
41
+
42
+ // 得地 / 半得地 / 不得地。数值只作标签与说明,不参与旺衰打分。
43
+ // 半得地一律不计入身强阵营(课堂:土日主见辰「不能当得地看」;戌对火日主须午戌合/寅午戌才得地)。
44
+ const SUPPORT = {
45
+ 木: { 得地: '亥子寅卯', 半: { 辰: 0.5, 未: 0.2 } },
46
+ 火: { 得地: '寅卯巳午未', 半: { 戌: 0 } },
47
+ 土: { 得地: '巳午未戌', 半: { 寅: 0.2, 辰: 0.5 } },
48
+ 金: { 得地: '丑酉申戌辰', 半: { 未: 0.1, 巳: 0.1 } },
49
+ 水: { 得地: '申亥子丑', 半: { 辰: 0.5 } },
50
+ };
51
+
52
+ const PILLARS = ['year', 'month', 'day', 'hour'];
53
+ const LABEL = { year: '年', month: '月', day: '日', hour: '时' };
54
+
55
+ /** 静态从格概率(%)。null 表示课堂只说「有可能」,未给数字。 */
56
+ const CONG_PROB = {
57
+ AAAA: { prob: 99, range: '≈99%', word: '四支同党,基本为真从' },
58
+ ABBB: { prob: 80, range: '≈80%', word: '年支独异,极易从' },
59
+ AAAB: { prob: 55, range: '50-60%', word: '时支独异,从与不从各半' },
60
+ AABA: { prob: 20, range: '≈20%', word: '日支独异,不易从' },
61
+ ABAA: { prob: 1, range: '≈1%', word: '月支独异,几乎不从' },
62
+ AABB: { prob: 0, range: '0%', word: '二对二最平衡,无从格' },
63
+ ABAB: { prob: 0, range: '0%', word: '二对二,无从格' },
64
+ ABBA: { prob: null, range: '原文未给', word: '二对二,原文只说「有可能从格」' },
65
+ };
66
+
67
+ /** 日主在某一支的得地程度 */
68
+ export function supportOf(dayStem, branch) {
69
+ const el = STEM_ELEMENT[dayStem];
70
+ const table = SUPPORT[el];
71
+ if (!table) return { level: '不得地', weight: 0 };
72
+ if (table.得地.includes(branch)) return { level: '得地', weight: 1 };
73
+ const half = table.半[branch];
74
+ if (half !== undefined) return { level: '半得地', weight: half };
75
+ return { level: '不得地', weight: 0 };
76
+ }
77
+
78
+ /** 天干属于身强阵营(印比)还是身弱阵营(食伤财官杀) */
79
+ function stemCamp(dayStem, stem) {
80
+ const me = STEM_ELEMENT[dayStem];
81
+ const el = STEM_ELEMENT[stem];
82
+ if (!me || !el) return 'B';
83
+ return el === me || GENERATES[el] === me ? 'A' : 'B';
84
+ }
85
+
86
+ /**
87
+ * 八大地支结构。
88
+ * @param {object} pillars 四柱(时柱可为 null)
89
+ * @param {string} dayStem 日干
90
+ */
91
+ export function branchStructureOf(pillars, dayStem) {
92
+ const keys = PILLARS.filter((k) => pillars[k] && pillars[k].branch);
93
+ if (!keys.length) return null;
94
+
95
+ const yearCamp = branchCamp(pillars.year.branch, dayStem);
96
+ const branches = keys.map((k) => {
97
+ const branch = pillars[k].branch;
98
+ const sup = supportOf(dayStem, branch);
99
+ const camp = sup.weight >= 1 ? 'A' : 'B';
100
+ return {
101
+ key: k,
102
+ label: LABEL[k],
103
+ branch,
104
+ camp: camp === yearCamp ? 'A' : 'B',
105
+ support: sup.level,
106
+ supportWeight: sup.weight,
107
+ weight: PILLAR_WEIGHT.branch[k],
108
+ };
109
+ });
110
+
111
+ // 年支那一档记 A;四支不足时(无时辰)只报实际存在的三支,code 相应变短
112
+ const code = branches.map((b) => b.camp).join('');
113
+ const prob = CONG_PROB[code] || null;
114
+
115
+ const aCamp = supportOf(dayStem, pillars.year.branch).weight >= 1 ? '身强阵营' : '身弱阵营';
116
+ const bCamp = aCamp === '身强阵营' ? '身弱阵营' : '身强阵营';
117
+
118
+ const sum = (camp, field) => branches.filter((b) => b.camp === camp).reduce((n, b) => n + b[field], 0);
119
+ const branchBal = { A: sum('A', 'weight'), B: sum('B', 'weight') };
120
+ const branchStrong = aCamp === '身强阵营' ? branchBal.A : branchBal.B;
121
+ const branchWeak = aCamp === '身强阵营' ? branchBal.B : branchBal.A;
122
+
123
+ const stemKeys = ['year', 'month', 'hour'].filter((k) => pillars[k] && pillars[k].stem);
124
+ const stemBal = { A: 0, B: 0 };
125
+ for (const k of stemKeys) stemBal[stemCamp(dayStem, pillars[k].stem)] += PILLAR_WEIGHT.stem[k];
126
+
127
+ const weakDays = branches.filter((b) => b.support !== '得地');
128
+ const aCount = branches.filter((b) => b.camp === 'A').length;
129
+ const bCount = branches.length - aCount;
130
+ // 异类=人数少的那一档;二对二没有异类
131
+ const minority = aCount === 0 || bCount === 0 ? null : aCount < bCount ? 'A' : bCount < aCount ? 'B' : null;
132
+ const odd = minority ? branches.filter((b) => b.camp === minority) : [];
133
+
134
+ const balance = {
135
+ aCamp,
136
+ branch: branchBal,
137
+ stem: stemBal,
138
+ total: { A: branchBal.A + stemBal.A, B: branchBal.B + stemBal.B },
139
+ };
140
+
141
+ const shapeText = !minority
142
+ ? (aCount === branches.length || bCount === branches.length
143
+ ? `四支同党(均属${aCamp})`
144
+ : `二对二:${branches.filter((b) => b.camp === 'A').map((b) => b.label).join('、')}支属${aCamp},`
145
+ + `${branches.filter((b) => b.camp === 'B').map((b) => b.label).join('、')}支属${bCamp}`)
146
+ : `异类为${odd.map((b) => `${b.label}支(${b.weight} 分)`).join('、')}`;
147
+
148
+ const detail = [
149
+ `四支${code}(${branches.map((b) => `${b.label}支${b.branch}${b.support}`).join('、')})`,
150
+ shapeText,
151
+ `地支 身强${branchStrong} : 身弱${branchWeak},加天干 ${balance.total[aCamp === '身强阵营' ? 'A' : 'B']}:${balance.total[aCamp === '身强阵营' ? 'B' : 'A']}`,
152
+ ];
153
+
154
+ // 没有时柱时概率表不适用:AAAA 之类需要四支齐全才成立
155
+ const complete = branches.length === 4;
156
+
157
+ return {
158
+ code,
159
+ complete,
160
+ aCamp,
161
+ congProb: complete && prob ? prob.prob : null,
162
+ congRange: complete && prob ? prob.range : '三柱不判结构概率',
163
+ word: complete && prob ? prob.word : '时柱暂缺,只列三支结构,不套从格概率',
164
+ branches,
165
+ balance,
166
+ odd: odd.map((b) => ({ key: b.key, label: b.label, branch: b.branch, weight: b.weight })),
167
+ halfSupported: weakDays.filter((b) => b.support === '半得地').map((b) => `${b.label}支${b.branch}`),
168
+ detail: detail.join(';'),
169
+ method: '按年支定 A 档,四支分两营;异类支的宫位权重决定静态从格概率(炳祥课堂口径,非唯一流派)',
170
+ note: '静态结构。三合/三会/六合把某一支合走时,应以合化后的阵营重排;概率只作提示,不直接断从格。',
171
+ };
172
+ }
173
+
174
+ function branchCamp(branch, dayStem) {
175
+ return supportOf(dayStem, branch).weight >= 1 ? 'A' : 'B';
176
+ }
177
+
178
+ /**
179
+ * 宫位权重的百分比视图(炳祥课程第 2.1 节)。
180
+ *
181
+ * 天干合计 10(年 2 / 月 5 / 日 0 / 时 3),地支合计 90(年 11 / 月 36 / 日 27 / 时 16),
182
+ * 两表相加正好 100,所以「按柱合计」本身就是百分比:年 13%、月 41%、日 27%、时 19%。
183
+ * 这里只做展示层换算,不改 bazi.js 的旺衰评分,也不参与取格与喜忌。
184
+ *
185
+ * 三柱模式(未给出生时刻)时,时柱是正午占位:权重照表列出,但合计只有 81,
186
+ * 不重新归一化——权重是固定口径,归一化会把占位时柱洗成实有。
187
+ */
188
+ export function weightPctOf(pillars = {}, { hourUnknown = false } = {}) {
189
+ const present = PILLARS.filter((k) => pillars[k] && pillars[k].branch);
190
+ const complete = !hourUnknown && present.length === 4;
191
+ // 三柱模式:时柱是正午占位,不计入合计;其余缺柱同理
192
+ const counted = complete ? PILLARS.slice() : present.filter((k) => !(k === 'hour' && hourUnknown));
193
+ const stem = {};
194
+ const branch = {};
195
+ const pct = {};
196
+ for (const k of PILLARS) {
197
+ stem[k] = PILLAR_WEIGHT.stem[k];
198
+ branch[k] = PILLAR_WEIGHT.branch[k];
199
+ pct[k] = counted.includes(k) ? stem[k] + branch[k] : 0;
200
+ }
201
+ const totalStem = counted.reduce((n, k) => n + stem[k], 0);
202
+ const totalBranch = counted.reduce((n, k) => n + branch[k], 0);
203
+ const all = totalStem + totalBranch;
204
+ const rows = counted.map((k) => `${LABEL[k]} ${pct[k]}%(干${stem[k]}+支${branch[k]})`).join('、');
205
+ const missing = counted.length < 4 ? `;${PILLARS.filter((k) => !counted.includes(k)).map((k) => LABEL[k]).join('、')}柱未计` : '';
206
+ return {
207
+ complete,
208
+ stem,
209
+ branch,
210
+ pct,
211
+ total: { stem: totalStem, branch: totalBranch, all },
212
+ detail: `宫位权重合计 ${all}:${rows}${missing}`,
213
+ method: '炳祥课程宫位权重:天干 2/5/0/3、地支 11/36/27/16,合计 100;日干为参照点计 0。只作展示换算,不改旺衰评分',
214
+ note: complete
215
+ ? '月令 41% 为四柱之首(课堂称事业宫);日 27%、时 19%、年 13%'
216
+ : '时柱暂缺,权重表照列,但本盘合计不足 100;不重新归一化',
217
+ };
218
+ }
@@ -77,10 +77,14 @@ export function buildXiaoliuren(input) {
77
77
  const info = PALACES[p3];
78
78
  return {
79
79
  system: 'xiaoliuren',
80
- meta: { mode: mode === 'time' ? '时间起课' : '报数起课', question: input.question || null, originText },
80
+ // question 放在顶层,不放进 meta:句柄按设计不收 question(它不影响盘面),
81
+ // 放进 meta 会让「句柄回放」与「原始调用」的 meta 不一致(回放后变 null),
82
+ // 看起来像盘变了。其余体系也都是把 question 挂顶层。
83
+ meta: { mode: mode === 'time' ? '时间起课' : '报数起课', originText },
84
+ question: input.question || null,
81
85
  steps: [p1, p2, p3],
82
86
  result: { palace: p3, luck: info.luck, text: info.text },
83
- sources: ['掌诀口径:通行马前课(白话提炼,非原文照录)'],
87
+ sources: ['掌诀口径:通行马前口诀(白话提炼,非原文照录)'],
84
88
  disclaimer: '小六壬为轻量快占,属民俗娱乐参考;重要事项请综合现实信息判断。',
85
89
  };
86
90
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-metaphysics",
3
- "version": "0.62.4",
3
+ "version": "0.63.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": [