dsh-metaphysics 0.19.0 → 0.21.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/CHANGELOG.md +35 -7
- package/README.md +10 -4
- package/client.js +1376 -51
- package/engine/bazi.js +2 -1
- package/engine/geju.js +84 -46
- package/engine/geo.js +1 -1
- package/engine/index.js +3 -1
- package/engine/interpret.js +1370 -1356
- package/engine/qiongtong.js +1025 -0
- package/engine/tiaohou.js +228 -0
- package/index.js +1 -1
- package/package.json +2 -2
package/engine/bazi.js
CHANGED
package/engine/geju.js
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
//
|
|
3
3
|
// 定位:在 bazi.js 算出四柱、五行、旺衰之后,给出
|
|
4
4
|
// 1) 格局(月令格 + 特殊格 + 常见组合);
|
|
5
|
-
// 2) 用神 /
|
|
6
|
-
// 3)
|
|
5
|
+
// 2) 用神 / 喜忌(调候优先 + 扶抑校验;气候平和时仍以扶抑为主);
|
|
6
|
+
// 3) 大运、流年的吉凶倾向评分(含调候修正项)。
|
|
7
7
|
// 取舍:格局派别差异极大,这里只给「规则可复核」的判定,evidence 写明依据;
|
|
8
|
-
//
|
|
8
|
+
// 调候由 tiaohou.js 独立判定,与扶抑冲突时显式裁决并写明理由,不静默取用。
|
|
9
|
+
|
|
10
|
+
import { judgeClimate, arbitrate } from './tiaohou.js';
|
|
11
|
+
import { qtOf } from './qiongtong.js';
|
|
9
12
|
|
|
10
13
|
const STEMS = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
|
|
11
14
|
const BRANCHES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
|
|
@@ -197,38 +200,39 @@ function specialPatterns(pillars, dayStem, monthBranch, strength, wuxing, gods)
|
|
|
197
200
|
|
|
198
201
|
// ── 用神 / 喜忌 ──────────────────────────────────────────────
|
|
199
202
|
|
|
200
|
-
const TIAOHOU = {
|
|
201
|
-
亥: ['火', '冬月寒水当令,最喜火暖局'],
|
|
202
|
-
子: ['火', '仲冬水旺,最喜火暖局'],
|
|
203
|
-
丑: ['火', '季冬湿寒,喜火解冻'],
|
|
204
|
-
巳: ['水', '初夏火旺,喜水制衡'],
|
|
205
|
-
午: ['水', '仲夏火炎,最喜水调候'],
|
|
206
|
-
未: ['水', '季夏燥土,喜水润局'],
|
|
207
|
-
寅: ['火', '初春尚寒,得火而发'],
|
|
208
|
-
卯: ['火', '仲春木旺,喜火吐秀'],
|
|
209
|
-
辰: ['金', '季春湿土,喜金水疏泄'],
|
|
210
|
-
申: ['火', '初秋金旺,喜火炼金'],
|
|
211
|
-
酉: ['火', '仲秋金锐,喜火制'],
|
|
212
|
-
戌: ['水', '季秋燥土,喜水润泽'],
|
|
213
|
-
};
|
|
214
203
|
|
|
215
|
-
function yongshenOf(dayStem, monthBranch, strength, wuxing) {
|
|
204
|
+
function yongshenOf(dayStem, monthBranch, strength, wuxing, branches = []) {
|
|
216
205
|
const roles = elementRoles(dayStem);
|
|
217
206
|
const me = roles.me;
|
|
218
207
|
const weak = strength.level === '身弱' || strength.level === '偏弱';
|
|
219
208
|
const strong = strength.level === '身强' || strength.level === '偏强';
|
|
220
209
|
|
|
221
|
-
const
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
210
|
+
const baseFavor = weak ? [roles.inMe, me] : strong ? [roles.shi, roles.cai, roles.guan] : [roles.inMe, me, roles.shi];
|
|
211
|
+
const baseAvoid = weak ? [roles.shi, roles.cai, roles.guan] : strong ? [me, roles.inMe] : [roles.guan, roles.cai];
|
|
212
|
+
let favor = baseFavor.slice();
|
|
213
|
+
let avoid = baseAvoid.slice();
|
|
214
|
+
|
|
215
|
+
// 先判气候,再决定要不要让调候压过扶抑
|
|
216
|
+
const climate = judgeClimate({ monthBranch, dayStem, wuxing, branches });
|
|
217
|
+
// 《穷通宝鉴》120 格:同一「日主 + 月令」的取用顺序。只作展示与加权,不覆盖气候调候
|
|
218
|
+
const qt = qtOf(dayStem, monthBranch);
|
|
219
|
+
const arb = arbitrate(climate, baseFavor, baseAvoid);
|
|
220
|
+
if (climate.need && climate.priority === '调候优先') {
|
|
221
|
+
const needEl = climate.need.element;
|
|
222
|
+
if (!favor.includes(needEl)) favor.unshift(needEl);
|
|
223
|
+
avoid = avoid.filter((e) => e !== needEl);
|
|
224
|
+
if (climate.avoidElement && !avoid.includes(climate.avoidElement)) avoid.unshift(climate.avoidElement);
|
|
225
|
+
}
|
|
229
226
|
|
|
230
227
|
const roleName = (el) => (el === me ? '比劫' : el === roles.inMe ? '印'
|
|
231
228
|
: el === roles.shi ? '食伤' : el === roles.cai ? '财' : '官杀');
|
|
229
|
+
const expand = {
|
|
230
|
+
比劫: ['比肩', '劫财'], 印: ['正印', '偏印'], 食伤: ['食神', '伤官'],
|
|
231
|
+
财: ['正财', '偏财'], 官杀: ['正官', '七杀'],
|
|
232
|
+
};
|
|
233
|
+
const favorTenGods = Array.from(new Set(favor.flatMap((el) => expand[roleName(el)] || [])));
|
|
234
|
+
const avoidTenGods = Array.from(new Set(avoid.flatMap((el) => expand[roleName(el)] || [])));
|
|
235
|
+
|
|
232
236
|
const detail = [
|
|
233
237
|
`日主${dayStem}${me},${strength.level}(${strength.score} 分):`,
|
|
234
238
|
weak
|
|
@@ -236,24 +240,24 @@ function yongshenOf(dayStem, monthBranch, strength, wuxing) {
|
|
|
236
240
|
: strong
|
|
237
241
|
? `喜${roles.shi}(食伤)泄秀、${roles.cai}(财)耗、${roles.guan}(官杀)制;忌${me}(比劫)与${roles.inMe}(印)再助。`
|
|
238
242
|
: '身中和,先取调候与通关,喜忌随大运流年动态变化。',
|
|
239
|
-
|
|
240
|
-
'
|
|
243
|
+
`调候:${climate.label}(${(climate.evidence || []).join('、')})${climate.need ? `,取${climate.need.stem}${climate.need.element},忌${climate.avoidElement}` : ',气候平和'}。`,
|
|
244
|
+
qt ? `穷通宝鉴:${qt.month}${dayStem}${me},用神${qt.yong.join('、')}(首取${qt.primary})。` : '',
|
|
245
|
+
arb.text,
|
|
246
|
+
arb.clash ? '取舍:本盘按「调候优先、扶抑次之」取用;若你更认同扶抑派,可把调候当环境偏好,强弱判断不受影响。' : '',
|
|
241
247
|
].filter(Boolean).join('');
|
|
242
248
|
|
|
243
249
|
return {
|
|
244
|
-
method: '扶抑为主 +
|
|
250
|
+
method: climate.priority === '调候优先' ? '调候优先 + 扶抑校验' : '扶抑为主 + 调候校验',
|
|
245
251
|
level: strength.level,
|
|
246
252
|
favor,
|
|
247
253
|
avoid,
|
|
248
254
|
favorRoles: favor.map(roleName),
|
|
249
255
|
avoidRoles: avoid.map(roleName),
|
|
250
|
-
favorTenGods
|
|
251
|
-
avoidTenGods
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
: ['正官', '七杀', '正财', '偏财'],
|
|
256
|
-
tiaohou,
|
|
256
|
+
favorTenGods,
|
|
257
|
+
avoidTenGods,
|
|
258
|
+
tiaohou: { ...climate, ...arb, element: climate.need ? climate.need.element : null },
|
|
259
|
+
climate,
|
|
260
|
+
qt,
|
|
257
261
|
detail,
|
|
258
262
|
};
|
|
259
263
|
}
|
|
@@ -274,33 +278,67 @@ export function scoreLuck(ganzhi, frame) {
|
|
|
274
278
|
const branch = ganzhi[1];
|
|
275
279
|
const stemEl = STEM_ELEMENT[stem];
|
|
276
280
|
const branchEl = BRANCH_ELEMENT[branch];
|
|
281
|
+
const th = ys.tiaohou || ys.climate || null;
|
|
277
282
|
const scoreEl = (el) => {
|
|
278
283
|
let s = 0;
|
|
279
284
|
if (ys.favor.includes(el)) s += 1;
|
|
280
285
|
if (ys.avoid.includes(el)) s -= 1;
|
|
281
|
-
if (ys.tiaohou && el === ys.tiaohou.element) s += 0.4;
|
|
282
286
|
return s;
|
|
283
287
|
};
|
|
284
288
|
let score = scoreEl(stemEl) * 1.0 + scoreEl(branchEl) * 0.8;
|
|
285
289
|
for (const h of (HIDDEN[branch] || []).slice(1)) {
|
|
286
290
|
score += scoreEl(STEM_ELEMENT[h]) * 0.2;
|
|
287
291
|
}
|
|
288
|
-
score = Math.round(score * 10) / 10;
|
|
289
292
|
|
|
290
|
-
const godStem = tenGod(frame.dayStem, stem);
|
|
291
|
-
const bonus = ys.favorTenGods.includes(godStem) ? 0.3 : 0;
|
|
292
|
-
const total = Math.round((score + bonus) * 10) / 10;
|
|
293
|
-
|
|
294
|
-
const tier = TIERS.find(([min]) => total >= min)[1];
|
|
295
293
|
const reasons = [];
|
|
296
294
|
if (ys.favor.includes(stemEl)) reasons.push(`天干${stem}${stemEl}为用神`);
|
|
297
295
|
else if (ys.avoid.includes(stemEl)) reasons.push(`天干${stem}${stemEl}为忌神`);
|
|
298
296
|
else reasons.push(`天干${stem}${stemEl}不喜不忌`);
|
|
299
297
|
if (ys.favor.includes(branchEl)) reasons.push(`地支${branch}${branchEl}助用`);
|
|
300
298
|
else if (ys.avoid.includes(branchEl)) reasons.push(`地支${branch}${branchEl}助忌`);
|
|
301
|
-
|
|
302
|
-
|
|
299
|
+
|
|
300
|
+
// 调候修正:不是"有没有这个五行",而是"这一年是给命局添了一把火,还是泼了一盆水"
|
|
301
|
+
if (th && th.need && th.priority === '调候优先') {
|
|
302
|
+
const needEl = th.need.element;
|
|
303
|
+
const oppEl = th.avoidElement;
|
|
304
|
+
const cold = String(th.kind).includes('寒');
|
|
305
|
+
if (stemEl === needEl) {
|
|
306
|
+
score += 1.1;
|
|
307
|
+
reasons.push(`${th.label}之局得${stem}${stemEl},${cold ? '解冻' : '得润'}`);
|
|
308
|
+
} else if (stemEl === oppEl) {
|
|
309
|
+
score -= 0.9;
|
|
310
|
+
reasons.push(`${th.label}之局再逢${stem}${stemEl},${cold ? '冻木难生' : '燥上加燥'}`);
|
|
311
|
+
}
|
|
312
|
+
if (branchEl === needEl) {
|
|
313
|
+
score += 0.9;
|
|
314
|
+
reasons.push(`地支${branch}${branchEl}合调候`);
|
|
315
|
+
} else if (branchEl === oppEl) {
|
|
316
|
+
score -= 1.0;
|
|
317
|
+
reasons.push(`地支${branch}${branchEl}加重${th.label}`);
|
|
318
|
+
}
|
|
319
|
+
if (cold && (stemEl === '水' || branchEl === '水')) score -= 0.3;
|
|
320
|
+
} else if (th && th.need && (stemEl === th.need.element || branchEl === th.need.element)) {
|
|
321
|
+
reasons.push(`合调候(${th.need.element})`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const godStem = tenGod(frame.dayStem, stem);
|
|
325
|
+
const bonus = ys.favorTenGods.includes(godStem) ? 0.3 : 0;
|
|
326
|
+
// 穷通宝鉴:天干正好是这一格的首取/次取时,再给一点「按书取用」的分
|
|
327
|
+
// 只在该干不属于忌神时加分——书里的取用与气候调候不同向时,以调候为先
|
|
328
|
+
const qt = ys.qt || null;
|
|
329
|
+
if (qt && qt.primary && !ys.avoid.includes(STEM_ELEMENT[qt.primary])) {
|
|
330
|
+
if (stem === qt.primary) {
|
|
331
|
+
score += 0.3;
|
|
332
|
+
reasons.push(`天干${stem}正是${qt.branch}月${qt.stem}日的首取(穷通宝鉴)`);
|
|
333
|
+
} else if (qt.yong.includes(stem) && !ys.avoid.includes(STEM_ELEMENT[stem])) {
|
|
334
|
+
score += 0.15;
|
|
335
|
+
reasons.push(`天干${stem}为穷通宝鉴次取`);
|
|
336
|
+
}
|
|
303
337
|
}
|
|
338
|
+
let total = Math.round((score + bonus) * 10) / 10;
|
|
339
|
+
total = Math.max(-4, Math.min(4, total));
|
|
340
|
+
|
|
341
|
+
const tier = TIERS.find(([min]) => total >= min)[1];
|
|
304
342
|
return { ganzhi, score: total, tier, stemTenGod: godStem, reasons, note: `${godStem}运,${tier}(${total})` };
|
|
305
343
|
}
|
|
306
344
|
|
|
@@ -319,7 +357,7 @@ export function analyzeBaziFrame({ pillars, dayStem, monthBranch, strength, wuxi
|
|
|
319
357
|
const month = monthPattern(pillars, dayStem, monthBranch);
|
|
320
358
|
const special = specialPatterns(pillars, dayStem, monthBranch, strength, wuxing, gods);
|
|
321
359
|
const geju = [month, ...special];
|
|
322
|
-
const yongshen = yongshenOf(dayStem, monthBranch, strength, wuxing);
|
|
360
|
+
const yongshen = yongshenOf(dayStem, monthBranch, strength, wuxing, Object.values(pillars).map((p) => p.branch));
|
|
323
361
|
return {
|
|
324
362
|
geju,
|
|
325
363
|
primary: month,
|
package/engine/geo.js
CHANGED
|
@@ -143,7 +143,7 @@ const DETAIL = /(区|县|镇|乡|村|小区|街道|路|号|巷|弄|组|队)/;
|
|
|
143
143
|
|
|
144
144
|
/** OpenStreetMap Nominatim:免密钥、支持中文。失败返回 null。
|
|
145
145
|
* UA 按 OSM 使用政策要求写明应用与联系方式(改版本时同步这里)。 */
|
|
146
|
-
const USER_AGENT = 'dsh-metaphysics/0.
|
|
146
|
+
const USER_AGENT = 'dsh-metaphysics/0.21.0 (+https://github.com/xuanfu-web/dsh-metaphysics)';
|
|
147
147
|
|
|
148
148
|
async function onlineLookup(text, { timeoutMs = 2500 } = {}) {
|
|
149
149
|
if (typeof fetch !== 'function') return null;
|
package/engine/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import { buildZeri } from './zeri.js';
|
|
|
21
21
|
import { buildChenggu } from './chenggu.js';
|
|
22
22
|
import { buildXiaoliuren } from './xiaoliuren.js';
|
|
23
23
|
import { buildStar } from './star.js';
|
|
24
|
-
import { buildNumerology } from './numerology.js';
|
|
24
|
+
import { buildNumerology } from './numerology.js';
|
|
25
25
|
import { buildConvert } from './convert.js';
|
|
26
26
|
import { buildTrend } from './trend.js';
|
|
27
27
|
import { buildFengshui } from './fengshui.js';
|
|
@@ -110,6 +110,8 @@ function baziText(r) {
|
|
|
110
110
|
` ${r.frame.geju.map((g) => `${g.name}[${g.kind}]`).join(' ')}`,
|
|
111
111
|
`用神:喜${r.frame.yongshen.favor.join('')}(${r.frame.yongshen.favorTenGods.join('、')}) 忌${r.frame.yongshen.avoid.join('')}(${r.frame.yongshen.avoidTenGods.join('、')}) ${r.frame.yongshen.method}`,
|
|
112
112
|
` ${r.frame.yongshen.detail}`,
|
|
113
|
+
`寒暖:${r.climate ? `${r.climate.label}(${(r.climate.evidence || []).join('、')})${r.climate.need ? ` 调候取${r.climate.need.stem}${r.climate.need.element} 忌${r.climate.avoidElement}` : ''}` : '—'}` +
|
|
114
|
+
(r.frame.yongshen.qt ? ` 穷通:${r.frame.yongshen.qt.month}${r.frame.yongshen.qt.stem}取${r.frame.yongshen.qt.yong.join('、')}(首取${r.frame.yongshen.qt.primary})` : ''),
|
|
113
115
|
`十神权重:${Object.entries(r.frame.godStats.groups).map(([k, v]) => `${k}${v}`).join(' ')}`,
|
|
114
116
|
] : [];
|
|
115
117
|
return [
|