dsh-metaphysics 0.19.0 → 0.20.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 +22 -10
- package/README.md +5 -3
- package/client.js +315 -51
- package/engine/bazi.js +2 -1
- package/engine/geju.js +67 -46
- package/engine/geo.js +1 -1
- package/engine/index.js +1 -0
- package/engine/interpret.js +1365 -1356
- package/engine/tiaohou.js +228 -0
- package/package.json +2 -2
package/engine/geju.js
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
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';
|
|
9
11
|
|
|
10
12
|
const STEMS = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
|
|
11
13
|
const BRANCHES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
|
|
@@ -197,38 +199,37 @@ function specialPatterns(pillars, dayStem, monthBranch, strength, wuxing, gods)
|
|
|
197
199
|
|
|
198
200
|
// ── 用神 / 喜忌 ──────────────────────────────────────────────
|
|
199
201
|
|
|
200
|
-
const TIAOHOU = {
|
|
201
|
-
亥: ['火', '冬月寒水当令,最喜火暖局'],
|
|
202
|
-
子: ['火', '仲冬水旺,最喜火暖局'],
|
|
203
|
-
丑: ['火', '季冬湿寒,喜火解冻'],
|
|
204
|
-
巳: ['水', '初夏火旺,喜水制衡'],
|
|
205
|
-
午: ['水', '仲夏火炎,最喜水调候'],
|
|
206
|
-
未: ['水', '季夏燥土,喜水润局'],
|
|
207
|
-
寅: ['火', '初春尚寒,得火而发'],
|
|
208
|
-
卯: ['火', '仲春木旺,喜火吐秀'],
|
|
209
|
-
辰: ['金', '季春湿土,喜金水疏泄'],
|
|
210
|
-
申: ['火', '初秋金旺,喜火炼金'],
|
|
211
|
-
酉: ['火', '仲秋金锐,喜火制'],
|
|
212
|
-
戌: ['水', '季秋燥土,喜水润泽'],
|
|
213
|
-
};
|
|
214
202
|
|
|
215
|
-
function yongshenOf(dayStem, monthBranch, strength, wuxing) {
|
|
203
|
+
function yongshenOf(dayStem, monthBranch, strength, wuxing, branches = []) {
|
|
216
204
|
const roles = elementRoles(dayStem);
|
|
217
205
|
const me = roles.me;
|
|
218
206
|
const weak = strength.level === '身弱' || strength.level === '偏弱';
|
|
219
207
|
const strong = strength.level === '身强' || strength.level === '偏强';
|
|
220
208
|
|
|
221
|
-
const
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
const
|
|
209
|
+
const baseFavor = weak ? [roles.inMe, me] : strong ? [roles.shi, roles.cai, roles.guan] : [roles.inMe, me, roles.shi];
|
|
210
|
+
const baseAvoid = weak ? [roles.shi, roles.cai, roles.guan] : strong ? [me, roles.inMe] : [roles.guan, roles.cai];
|
|
211
|
+
let favor = baseFavor.slice();
|
|
212
|
+
let avoid = baseAvoid.slice();
|
|
213
|
+
|
|
214
|
+
// 先判气候,再决定要不要让调候压过扶抑
|
|
215
|
+
const climate = judgeClimate({ monthBranch, dayStem, wuxing, branches });
|
|
216
|
+
const arb = arbitrate(climate, baseFavor, baseAvoid);
|
|
217
|
+
if (climate.need && climate.priority === '调候优先') {
|
|
218
|
+
const needEl = climate.need.element;
|
|
219
|
+
if (!favor.includes(needEl)) favor.unshift(needEl);
|
|
220
|
+
avoid = avoid.filter((e) => e !== needEl);
|
|
221
|
+
if (climate.avoidElement && !avoid.includes(climate.avoidElement)) avoid.unshift(climate.avoidElement);
|
|
222
|
+
}
|
|
229
223
|
|
|
230
224
|
const roleName = (el) => (el === me ? '比劫' : el === roles.inMe ? '印'
|
|
231
225
|
: el === roles.shi ? '食伤' : el === roles.cai ? '财' : '官杀');
|
|
226
|
+
const expand = {
|
|
227
|
+
比劫: ['比肩', '劫财'], 印: ['正印', '偏印'], 食伤: ['食神', '伤官'],
|
|
228
|
+
财: ['正财', '偏财'], 官杀: ['正官', '七杀'],
|
|
229
|
+
};
|
|
230
|
+
const favorTenGods = Array.from(new Set(favor.flatMap((el) => expand[roleName(el)] || [])));
|
|
231
|
+
const avoidTenGods = Array.from(new Set(avoid.flatMap((el) => expand[roleName(el)] || [])));
|
|
232
|
+
|
|
232
233
|
const detail = [
|
|
233
234
|
`日主${dayStem}${me},${strength.level}(${strength.score} 分):`,
|
|
234
235
|
weak
|
|
@@ -236,24 +237,22 @@ function yongshenOf(dayStem, monthBranch, strength, wuxing) {
|
|
|
236
237
|
: strong
|
|
237
238
|
? `喜${roles.shi}(食伤)泄秀、${roles.cai}(财)耗、${roles.guan}(官杀)制;忌${me}(比劫)与${roles.inMe}(印)再助。`
|
|
238
239
|
: '身中和,先取调候与通关,喜忌随大运流年动态变化。',
|
|
239
|
-
|
|
240
|
-
|
|
240
|
+
`调候:${climate.label}(${(climate.evidence || []).join('、')})${climate.need ? `,取${climate.need.stem}${climate.need.element},忌${climate.avoidElement}` : ',气候平和'}。`,
|
|
241
|
+
arb.text,
|
|
242
|
+
arb.clash ? '取舍:本盘按「调候优先、扶抑次之」取用;若你更认同扶抑派,可把调候当环境偏好,强弱判断不受影响。' : '',
|
|
241
243
|
].filter(Boolean).join('');
|
|
242
244
|
|
|
243
245
|
return {
|
|
244
|
-
method: '扶抑为主 +
|
|
246
|
+
method: climate.priority === '调候优先' ? '调候优先 + 扶抑校验' : '扶抑为主 + 调候校验',
|
|
245
247
|
level: strength.level,
|
|
246
248
|
favor,
|
|
247
249
|
avoid,
|
|
248
250
|
favorRoles: favor.map(roleName),
|
|
249
251
|
avoidRoles: avoid.map(roleName),
|
|
250
|
-
favorTenGods
|
|
251
|
-
avoidTenGods
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
? ['比肩', '劫财', '正印', '偏印']
|
|
255
|
-
: ['正官', '七杀', '正财', '偏财'],
|
|
256
|
-
tiaohou,
|
|
252
|
+
favorTenGods,
|
|
253
|
+
avoidTenGods,
|
|
254
|
+
tiaohou: { ...climate, ...arb, element: climate.need ? climate.need.element : null },
|
|
255
|
+
climate,
|
|
257
256
|
detail,
|
|
258
257
|
};
|
|
259
258
|
}
|
|
@@ -274,33 +273,55 @@ export function scoreLuck(ganzhi, frame) {
|
|
|
274
273
|
const branch = ganzhi[1];
|
|
275
274
|
const stemEl = STEM_ELEMENT[stem];
|
|
276
275
|
const branchEl = BRANCH_ELEMENT[branch];
|
|
276
|
+
const th = ys.tiaohou || ys.climate || null;
|
|
277
277
|
const scoreEl = (el) => {
|
|
278
278
|
let s = 0;
|
|
279
279
|
if (ys.favor.includes(el)) s += 1;
|
|
280
280
|
if (ys.avoid.includes(el)) s -= 1;
|
|
281
|
-
if (ys.tiaohou && el === ys.tiaohou.element) s += 0.4;
|
|
282
281
|
return s;
|
|
283
282
|
};
|
|
284
283
|
let score = scoreEl(stemEl) * 1.0 + scoreEl(branchEl) * 0.8;
|
|
285
284
|
for (const h of (HIDDEN[branch] || []).slice(1)) {
|
|
286
285
|
score += scoreEl(STEM_ELEMENT[h]) * 0.2;
|
|
287
286
|
}
|
|
288
|
-
score = Math.round(score * 10) / 10;
|
|
289
|
-
|
|
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
287
|
|
|
294
|
-
const tier = TIERS.find(([min]) => total >= min)[1];
|
|
295
288
|
const reasons = [];
|
|
296
289
|
if (ys.favor.includes(stemEl)) reasons.push(`天干${stem}${stemEl}为用神`);
|
|
297
290
|
else if (ys.avoid.includes(stemEl)) reasons.push(`天干${stem}${stemEl}为忌神`);
|
|
298
291
|
else reasons.push(`天干${stem}${stemEl}不喜不忌`);
|
|
299
292
|
if (ys.favor.includes(branchEl)) reasons.push(`地支${branch}${branchEl}助用`);
|
|
300
293
|
else if (ys.avoid.includes(branchEl)) reasons.push(`地支${branch}${branchEl}助忌`);
|
|
301
|
-
|
|
302
|
-
|
|
294
|
+
|
|
295
|
+
// 调候修正:不是"有没有这个五行",而是"这一年是给命局添了一把火,还是泼了一盆水"
|
|
296
|
+
if (th && th.need && th.priority === '调候优先') {
|
|
297
|
+
const needEl = th.need.element;
|
|
298
|
+
const oppEl = th.avoidElement;
|
|
299
|
+
const cold = String(th.kind).includes('寒');
|
|
300
|
+
if (stemEl === needEl) {
|
|
301
|
+
score += 1.1;
|
|
302
|
+
reasons.push(`${th.label}之局得${stem}${stemEl},${cold ? '解冻' : '得润'}`);
|
|
303
|
+
} else if (stemEl === oppEl) {
|
|
304
|
+
score -= 0.9;
|
|
305
|
+
reasons.push(`${th.label}之局再逢${stem}${stemEl},${cold ? '冻木难生' : '燥上加燥'}`);
|
|
306
|
+
}
|
|
307
|
+
if (branchEl === needEl) {
|
|
308
|
+
score += 0.9;
|
|
309
|
+
reasons.push(`地支${branch}${branchEl}合调候`);
|
|
310
|
+
} else if (branchEl === oppEl) {
|
|
311
|
+
score -= 1.0;
|
|
312
|
+
reasons.push(`地支${branch}${branchEl}加重${th.label}`);
|
|
313
|
+
}
|
|
314
|
+
if (cold && (stemEl === '水' || branchEl === '水')) score -= 0.3;
|
|
315
|
+
} else if (th && th.need && (stemEl === th.need.element || branchEl === th.need.element)) {
|
|
316
|
+
reasons.push(`合调候(${th.need.element})`);
|
|
303
317
|
}
|
|
318
|
+
|
|
319
|
+
const godStem = tenGod(frame.dayStem, stem);
|
|
320
|
+
const bonus = ys.favorTenGods.includes(godStem) ? 0.3 : 0;
|
|
321
|
+
let total = Math.round((score + bonus) * 10) / 10;
|
|
322
|
+
total = Math.max(-4, Math.min(4, total));
|
|
323
|
+
|
|
324
|
+
const tier = TIERS.find(([min]) => total >= min)[1];
|
|
304
325
|
return { ganzhi, score: total, tier, stemTenGod: godStem, reasons, note: `${godStem}运,${tier}(${total})` };
|
|
305
326
|
}
|
|
306
327
|
|
|
@@ -319,7 +340,7 @@ export function analyzeBaziFrame({ pillars, dayStem, monthBranch, strength, wuxi
|
|
|
319
340
|
const month = monthPattern(pillars, dayStem, monthBranch);
|
|
320
341
|
const special = specialPatterns(pillars, dayStem, monthBranch, strength, wuxing, gods);
|
|
321
342
|
const geju = [month, ...special];
|
|
322
|
-
const yongshen = yongshenOf(dayStem, monthBranch, strength, wuxing);
|
|
343
|
+
const yongshen = yongshenOf(dayStem, monthBranch, strength, wuxing, Object.values(pillars).map((p) => p.branch));
|
|
323
344
|
return {
|
|
324
345
|
geju,
|
|
325
346
|
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.20.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
|
@@ -110,6 +110,7 @@ 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}` : ''}` : '—'}`,
|
|
113
114
|
`十神权重:${Object.entries(r.frame.godStats.groups).map(([k, v]) => `${k}${v}`).join(' ')}`,
|
|
114
115
|
] : [];
|
|
115
116
|
return [
|