UNPKG

bazi-mcp

Version:

[![smithery badge](https://smithery.ai/badge/@cantian-ai/bazi-mcp)](https://smithery.ai/server/@cantian-ai/bazi-mcp)

562 lines (504 loc) 16 kB
import { EightChar, SixtyCycle } from 'tyme4ts'; type ZhuData = { 天干: string; 地支: string; }; type RelationInfo = { 柱: string; [key: string]: any; }; type Relation = { [zhu: string]: { 天干: { [key: string]: RelationInfo[] }; 地支: { [key: string]: RelationInfo[] }; [key: string]: any; // 如拱、双冲、双合等 }; }; function addToRelation(target: { [key: string]: RelationInfo[] }, relation_type: string, info: RelationInfo) { if (!target[relation_type]) { target[relation_type] = []; } target[relation_type].push(info); } function buildZhuItem(sixtyCycle: SixtyCycle) { return { 天干: sixtyCycle.getHeavenStem().toString(), 地支: sixtyCycle.getEarthBranch().toString(), }; } export function getRelation(eightChar: EightChar) { const zhuObject = { 年: buildZhuItem(eightChar.getYear()), 月: buildZhuItem(eightChar.getMonth()), 日: buildZhuItem(eightChar.getDay()), 时: buildZhuItem(eightChar.getHour()), }; return calculateRelation(zhuObject); } function calculateRelation(zhuObject: { [zhu in '年' | '月' | '日' | '时']: ZhuData }): Relation { const relation: Relation = {}; // 初始化 for (const zhu in zhuObject) { relation[zhu] = { 天干: {}, 地支: {}, }; } for (const zhu1 in zhuObject) { const data1 = zhuObject[zhu1]; const seen_combinations = new Set<string>(); for (const zhu2 in zhuObject) { if (zhu1 === zhu2) continue; const data2 = zhuObject[zhu2]; // 天干冲 const gan_chong_result = getGanChong(data1['天干'], data2['天干']); if (gan_chong_result) { addToRelation(relation[zhu1]['天干'], '冲', { 柱: zhu2, 知识点: `${data1['天干']}${data2['天干']}相${gan_chong_result}`, }); } // 天干合 const gan_he_result = getGanHe(data1['天干'], data2['天干']); if (gan_he_result) { addToRelation(relation[zhu1]['天干'], '合', { 柱: zhu2, 知识点: `${data1['天干']}${data2['天干']}合${gan_he_result}`, 元素: gan_he_result, }); } // 地支冲 const zhi_chong_result = getZhiChong(data1['地支'], data2['地支']); if (zhi_chong_result) { addToRelation(relation[zhu1]['地支'], '冲', { 柱: zhu2, 知识点: `${data1['地支']}${data2['地支']}相${zhi_chong_result}`, }); } // 地支害 const zhi_hai_result = getZhiHai(data1['地支'], data2['地支']); if (zhi_hai_result) { addToRelation(relation[zhu1]['地支'], '害', { 柱: zhu2, 知识点: `${data1['地支']}${data2['地支']} 相${zhi_hai_result}`, 元素: zhi_hai_result, }); } // 地支暗合 const zhi_anhe_result = getZhiAnHe(data1['地支'], data2['地支']); if (zhi_anhe_result) { addToRelation(relation[zhu1]['地支'], '暗合', { 柱: zhu2, 知识点: `${data1['地支']}${data2['地支']}${zhi_anhe_result}`, 元素: zhi_anhe_result, }); } // 地支合 const zhi_he_result = getZhiHe(data1['地支'], data2['地支']); if (zhi_he_result) { addToRelation(relation[zhu1]['地支'], '合', { 柱: zhu2, 知识点: `${data1['地支']}${data2['地支']}合${zhi_he_result}`, 元素: zhi_he_result, }); } // 地支刑(二元) const zhi_xing_result = getZhiXing(data1['地支'], data2['地支']); if (zhi_xing_result) { addToRelation(relation[zhu1]['地支'], '刑', { 柱: zhu2, 知识点: `${data1['地支']}${data2['地支']}相${zhi_xing_result}`, }); } let sanhe_flag = false; // 三合、三会、三刑 for (const zhu3 in zhuObject) { if (zhu3 === zhu1 || zhu3 === zhu2) continue; const data3 = zhuObject[zhu3]; // 构建三柱组合标识(不可重复) const combinationKey = [zhu1, zhu2, zhu3].sort().join('_'); if (seen_combinations.has(combinationKey)) continue; // 三合 const sanhe_result = getSanHe([data1['地支'], data2['地支'], data3['地支']]); if (sanhe_result) { addToRelation(relation[zhu1]['地支'], '三合', { 柱: zhu2 + zhu3, 柱1: zhu2, 柱2: zhu3, 知识点: `${data1['地支']}${data2['地支']}${data3['地支']}三合${sanhe_result}`, 元素: sanhe_result, }); seen_combinations.add(combinationKey); sanhe_flag = true; } // 三会 const sanhui_result = getSanHui([data1['地支'], data2['地支'], data3['地支']]); if (sanhui_result) { addToRelation(relation[zhu1]['地支'], '三会', { 柱: zhu2 + zhu3, 柱1: zhu2, 柱2: zhu3, 知识点: `${data1['地支']}${data2['地支']}${data3['地支']}三会${sanhui_result}`, 元素: sanhui_result, }); seen_combinations.add(combinationKey); } // 三刑 const sanxing_result = getSanXing([data1['地支'], data2['地支'], data3['地支']]); if (sanxing_result) { addToRelation(relation[zhu1]['地支'], '三刑', { 柱: zhu2 + zhu3, 柱1: zhu2, 柱2: zhu3, 知识点: `${data1['地支']}${data2['地支']}${data3['地支']}三刑`, }); seen_combinations.add(combinationKey); } } // 半三合 if (!sanhe_flag) { const half_sanhe_result = getHalfSanHe(data1['地支'], data2['地支']); if (half_sanhe_result) { addToRelation(relation[zhu1]['地支'], '半合', { 柱: zhu2, 知识点: `${data1['地支']}${data2['地支']}半合${half_sanhe_result}`, 元素: half_sanhe_result, }); } } // 拱与夹 if (data1['天干'] === data2['天干']) { const gong_jia_result = getGongJia(data1['天干'], data1['地支'], data2['天干'], data2['地支']); if (gong_jia_result) { relation[zhu1]['拱'] = { 柱: zhu2, 拱: gong_jia_result, 知识点: `${zhu1}${zhu2}拱${gong_jia_result}`, }; } } } } // 进阶:双合/双冲之类 for (const zhu in relation) { const data = relation[zhu]; const gan_chong = data['天干']['冲'] || []; const zhi_chong = data['地支']['冲'] || []; for (const gan_relation of gan_chong) { for (const zhi_relation of zhi_chong) { if (zhi_relation['柱'] === gan_relation['柱']) { if (!relation[zhu]['双冲']) { relation[zhu]['双冲'] = []; } relation[zhu]['双冲'].push({ 柱: gan_relation['柱'], 知识点: `${zhu}与${gan_relation['柱']}双冲`, }); } } } const gan_he = data['天干']['合'] || []; const zhi_he = data['地支']['合'] || []; for (const gan_relation of gan_he) { for (const zhi_relation of zhi_he) { if (zhi_relation['柱'] === gan_relation['柱']) { if (!relation[zhu]['双合']) { relation[zhu]['双合'] = []; } relation[zhu]['双合'].push({ 柱: gan_relation['柱'], 知识点: `${zhu}与${gan_relation['柱']}双合`, }); } } } } return relation; } ////////////////////////////////////////////////天干相冲//////////////////////////////////////////////// const GAN_CHONG = { 甲庚: '冲', 乙辛: '冲', 丙壬: '冲', 丁癸: '冲', 庚甲: '冲', 辛乙: '冲', 壬丙: '冲', 癸丁: '冲', 甲戊: '尅', 乙己: '尅', 丙庚: '尅', 丁辛: '尅', 戊壬: '尅', 己癸: '尅', 戊甲: '尅', 己乙: '尅', 庚丙: '尅', 辛丁: '尅', 壬戊: '尅', 癸己: '尅', }; // 返回两个天干相冲结果,没有相冲关系返回undefined function getGanChong(gan1: string, gan2: string) { const joined = gan1 + gan2; if (GAN_CHONG[joined]) { return GAN_CHONG[joined]; } } ////////////////////////////////////////////////天干五合//////////////////////////////////////////////// const GAN_WUHE = { 甲己: '土', 乙庚: '金', 丙辛: '水', 丁壬: '木', 戊癸: '火', 己甲: '土', 庚乙: '金', 辛丙: '水', 壬丁: '木', 癸戊: '火', }; // 返回两个天干相合结果,没有相合关系返回undefined function getGanHe(gan1: string, gan2: string) { const gan_str = gan1 + gan2; if (GAN_WUHE[gan_str]) { return GAN_WUHE[gan_str]; } } ////////////////////////////////////////////////地支相冲//////////////////////////////////////////////// const ZHI_CHONG = { 子午: '冲', 丑未: '冲', 寅申: '冲', 卯酉: '冲', 辰戌: '冲', 巳亥: '冲', 午子: '冲', 未丑: '冲', 申寅: '冲', 酉卯: '冲', 戌辰: '冲', 亥巳: '冲', }; function getZhiChong(zhi1: string, zhi2: string) { const zhi_str = zhi1 + zhi2; if (ZHI_CHONG[zhi_str]) { return ZHI_CHONG[zhi_str]; } } ////////////////////////////////////////////////地支相害(穿)//////////////////////////////////////////////// const HAI_CHONG = { 子未: '害', 丑午: '害', 寅巳: '害', 卯辰: '害', 申亥: '害', 酉戌: '害', 未子: '害', 午丑: '害', 巳寅: '害', 辰卯: '害', 亥申: '害', 戌酉: '害', }; function getZhiHai(zhi1: string, zhi2: string) { const zhi_str = zhi1 + zhi2; if (HAI_CHONG['zhi_str']) { return HAI_CHONG[zhi_str]; } } ////////////////////////////////////////////////地支暗合//////////////////////////////////////////////// const ZHI_ANHE = { 寅丑: '暗合', 午亥: '暗合', 卯申: '暗合', 丑寅: '暗合', 亥午: '暗合', 申卯: '暗合', }; function getZhiAnHe(zhi1: string, zhi2: string) { const zhi_str = zhi1 + zhi2; if (ZHI_ANHE['zhi_str']) { return ZHI_ANHE['zhi_str']; } } ////////////////////////////////////////////////地支六合//////////////////////////////////////////////// const ZHI_LIUHE = { 子丑: '土', 寅亥: '木', 卯戌: '火', 辰酉: '金', 巳申: '水', 午未: '火', 丑子: '土', 亥寅: '木', 戌卯: '火', 酉辰: '金', 申巳: '水', 未午: '火', }; function getZhiHe(zhi1: string, zhi2: string) { const zhi_str = zhi1 + zhi2; if (ZHI_LIUHE['zhi_str']) { return ZHI_LIUHE['zhi_str']; } } ////////////////////////////////////////////////地支自刑//////////////////////////////////////////////// const ZHI_XING = { 子卯: '刑', 卯子: '刑', 辰辰: '刑', 午午: '刑', 酉酉: '刑', 亥亥: '刑', }; function getZhiXing(zhi1, zhi2) { const zhi_str = zhi1 + zhi2; if (ZHI_XING['zhi_str']) { return ZHI_XING['zhi_str']; } } ////////////////////////////////////////////////地支三合//////////////////////////////////////////////// const ZHI_SANHE: { [key: string]: string } = { 申子辰: '水', 寅午戌: '火', 巳酉丑: '金', 亥卯未: '木', }; // 判断是否三合局,如果是,返回三合局所代表的五行 function getSanHe(zhiTriad: string[]) { // 将输入的地支三元素组合转为Set,去重 const zhiTriadSet = new Set(zhiTriad); // 如果长度不等于3,说明有重复地支,不符合三合局 if (zhiTriadSet.size !== 3) { return; } // 遍历所有的三合局规则 for (const sanhe in ZHI_SANHE) { const sanheSet = new Set(sanhe.split('')); // 如果集合完全匹配,则符合三合局 if (zhiTriadSet.size === sanheSet.size && [...zhiTriadSet].every((zhi) => sanheSet.has(zhi))) { return ZHI_SANHE[sanhe]; } } } ////////////////////////////////////////////////地支三会//////////////////////////////////////////////// const ZHI_SANHUI: { [key: string]: string } = { 寅卯辰: '木', 巳午未: '火', 申酉戌: '金', 亥子丑: '水', }; // 判断是否三会局,如果是,返回三会局所代表的五行 function getSanHui(zhiTriad: string[]) { // 去重 const zhiTriadSet = new Set(zhiTriad); // 长度不等于3,不符合三会局 if (zhiTriadSet.size !== 3) { return; } // 遍历所有的三会局规则 for (const sanhui in ZHI_SANHUI) { const sanhuiSet = new Set(sanhui.split('')); // 集合完全匹配 if (zhiTriadSet.size === sanhuiSet.size && [...zhiTriadSet].every((zhi) => sanhuiSet.has(zhi))) { return ZHI_SANHUI[sanhui]; } } } ////////////////////////////////////////////////地支三刑//////////////////////////////////////////////// const ZHI_SAN_XING: { [key: string]: string } = { 寅巳申: '寅巳申三刑', 丑未戌: '丑未戌三刑', }; // 判断是否三刑,如果是,返回三刑的说明,否则返回 false function getSanXing(zhiTriad: string[]): string | undefined { // 去重 const zhiTriadSet = new Set(zhiTriad); // 如果长度不等于3,说明有重复地支,不符合三刑局 if (zhiTriadSet.size !== 3) { return; } // 遍历所有的三刑局规则 for (const sanxing in ZHI_SAN_XING) { const sanxingSet = new Set(sanxing.split('')); // 完全匹配,符合三刑局 if (zhiTriadSet.size === sanxingSet.size && [...zhiTriadSet].every((zhi) => sanxingSet.has(zhi))) { return ZHI_SAN_XING[sanxing]; } } } ////////////////////////////////////////////////地支半合//////////////////////////////////////////////// const HALF_SANHE: { [key: string]: string } = { 申子: '水', 子辰: '水', 寅午: '火', 午戌: '火', 巳酉: '金', 酉丑: '金', 亥卯: '木', 卯未: '木', 子申: '水', 辰子: '水', 午寅: '火', 戌午: '火', 酉巳: '金', 丑酉: '金', 卯亥: '木', 未卯: '木', }; // 判断是否地支半三合,如果是返回半合结果,否则返回 false function getHalfSanHe(zhi1: string, zhi2: string): string | false { const zhiStr = zhi1 + zhi2; if (zhiStr in HALF_SANHE) { return HALF_SANHE[zhiStr]; } else { return false; } } ////////////////////////////////////////////////拱夹//////////////////////////////////////////////// // 地支顺序表 const ZHI_ORDER: string[] = ['寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥', '子', '丑']; // 判断天干地支是否符合拱、夹关系 function getGongJia(gan1: string, zhi1: string, gan2: string, zhi2: string): string | false { // 天干必须相同 if (gan1 !== gan2) { return false; } // 拱规则:三合 for (const group of Object.keys(ZHI_SANHE)) { if (group.includes(zhi1) && group.includes(zhi2)) { const [head, middle, tail] = group.split(''); if ((zhi1 === head && zhi2 === tail) || (zhi1 === tail && zhi2 === head)) { return middle; } } } // 拱规则:三会 for (const group of Object.keys(ZHI_SANHUI)) { if (group.includes(zhi1) && group.includes(zhi2)) { const [head, middle, tail] = group.split(''); if ((zhi1 === head && zhi2 === tail) || (zhi1 === tail && zhi2 === head)) { return middle; } } } // 夹规则:顺序隔位 const idx1 = ZHI_ORDER.indexOf(zhi1); const idx2 = ZHI_ORDER.indexOf(zhi2); if (idx1 !== -1 && idx2 !== -1) { const diff = Math.abs(idx1 - idx2); // 差为2,正常夹 if (diff === 2) { const middle_zhi = ZHI_ORDER[(Math.min(idx1, idx2) + 1) % ZHI_ORDER.length]; return middle_zhi; } // 数组边界:12-2=10,环形夹 if (diff === 10) { const middle_zhi = ZHI_ORDER[(Math.max(idx1, idx2) + 1) % ZHI_ORDER.length]; return middle_zhi; } } return false; }