Files
Jyotisha/jyotish-app/analysis-deep.js
T
2026-06-25 18:33:26 +08:00

429 lines
23 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Jyotish Deep Analysis Engine v1.0
* 核心计算:Raman功能吉凶 · PACDARES · 宫位互影响 · 分盘交叉验证 · Vargottama · 频率分析
*/
import { SIGNS, SIGNS_CN, SIGN_LORDS, PLANET_CN, PLANET_SYMBOLS, EXALTATION, DEBILITATION, PLANET_ASPECTS, getPlanetStatus } from './jyotish-engine.js';
import { VARGA_DEFS } from './jyotish-advanced.js';
// ============================================================================
// 一、B.V. Raman 功能吉凶星表
// ============================================================================
export const RAMAN_TABLES = {
Aries: { best:['Jupiter'], good:['Mars','Sun'], bad:['Saturn','Mercury','Venus'], worst:['Mercury'], neutral:[], yk:null, maraka:['Saturn','Mercury'] },
Taurus: { best:['Saturn'], good:['Mercury','Mars','Sun'], bad:['Jupiter','Venus','Moon'], worst:[], neutral:['Venus'], yk:null, maraka:['Mercury','Jupiter'] },
Gemini: { best:['Venus'], good:[], bad:['Mars'], worst:[], neutral:['Moon','Mercury'], yk:null, maraka:['Moon','Jupiter'] },
Cancer: { best:['Mars'], good:['Jupiter'], bad:['Saturn','Mercury'], worst:[], neutral:['Venus'], yk:'Mars', maraka:['Sun','Saturn'] },
Leo: { best:['Mars'], good:['Sun','Jupiter'], bad:['Venus','Saturn'], worst:[], neutral:['Moon'], yk:'Mars', maraka:['Mercury','Venus'] },
Virgo: { best:['Venus'], good:[], bad:['Mars','Jupiter','Moon'], worst:['Mars'], neutral:['Mercury'], yk:null, maraka:['Venus','Mars'] },
Libra: { best:['Saturn'], good:['Mercury','Venus'], bad:['Jupiter','Sun','Mars'], worst:['Jupiter'], neutral:['Moon'], yk:'Saturn', maraka:['Mars','Jupiter'] },
Scorpio: { best:['Jupiter'], good:['Moon','Sun','Mars'], bad:['Venus','Mercury'], worst:['Venus'], neutral:[], yk:null, maraka:['Jupiter','Venus'] },
Sagittarius: { best:['Mars','Sun'], good:[], bad:['Venus','Saturn','Mercury'], worst:[], neutral:['Jupiter','Moon'], yk:null, maraka:['Saturn','Mercury'] },
Capricorn: { best:['Venus'], good:['Mercury','Saturn'], bad:['Mars','Jupiter','Moon'], worst:['Mars'], neutral:['Sun'], yk:'Venus', maraka:['Saturn','Moon'] },
Aquarius: { best:['Venus'], good:['Sun','Mars'], bad:['Jupiter','Moon'], worst:[], neutral:['Mercury'], yk:'Venus', maraka:['Mercury','Sun'] },
Pisces: { best:['Moon','Mars'], good:[], bad:['Saturn','Sun','Venus','Mercury'], worst:[], neutral:['Jupiter'], yk:null, maraka:['Venus','Mars'] },
};
export const HOUSE_DOMAINS = {
1: {name:'自我·身体·人格',themes:['身体健康','人格特质','人生方向']},
2: {name:'财富·语言·家庭',themes:['财富积累','语言能力','家庭背景']},
3: {name:'沟通·兄弟·勇气',themes:['沟通表达','兄弟姐妹','勇气行动']},
4: {name:'家庭·房产·母亲',themes:['母亲','房产土地','内心安全感']},
5: {name:'创造·子女·恋爱',themes:['子女','恋爱','创造力','投资']},
6: {name:'职场·健康·服务',themes:['职场冲突','疾病健康','敌人对手']},
7: {name:'婚姻·伴侣·合作',themes:['婚姻关系','商业合作','伴侣特质']},
8: {name:'转化·遗产·神秘',themes:['死亡转化','遗产继承','他人资源']},
9: {name:'宗教·哲学·导师',themes:['长途旅行','导师上师','父亲']},
10: {name:'事业·社会·权威',themes:['事业职业','社会地位','职业成就']},
11: {name:'收入·朋友·愿望',themes:['收入财富','朋友社交','愿望实现']},
12: {name:'灵性·损失·解脱',themes:['灵性修行','隐藏事物','外国事务']},
};
// ============================================================================
// 二、工具函数
// ============================================================================
const P_NAMES = ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn','Rahu','Ketu'];
export function getFunctionalNature(p, asc) {
const t = RAMAN_TABLES[asc]; if(!t) return 'neutral';
if(t.yk===p) return 'yogakaraka';
if(t.best.includes(p)) return 'best_benefic';
if(t.good.includes(p)) return 'benefic';
if(t.neutral.includes(p)) return 'neutral';
if(t.worst.includes(p)) return 'worst';
if(t.bad.includes(p)) return 'malefic';
return 'neutral';
}
export function getHousesRuled(p, asc) {
const ai=SIGNS.indexOf(asc); const h=[];
for(let i=1;i<=12;i++) if(SIGN_LORDS[SIGNS[(ai+i-1)%12]]===p) h.push(i);
return h;
}
export function getHouseType(h) {
if([1,4,7,10].includes(h)) return 'Kendra角宫';
if([5,9].includes(h)) return 'Trikona三方';
if([3,6,10,11].includes(h)) return 'Upachaya成长';
if([6,8,12].includes(h)) return 'Dusthana凶宫';
return '普通';
}
function vargaSI(degIS, si, d) {
const sw=30/d, seg=Math.floor(degIS/sw);
switch(d){
case 1:return si; case 2:return(si%2===0)?(degIS<15?4:3):(degIS<15?3:4);
case 3:return(si+seg*4)%12; case 4:return(si+seg)%12;
case 7:return(si%2===0)?seg%12:(seg+6)%12;
case 9:return[0,3,6,9].includes(si)?(si+seg)%12:[1,4,7,10].includes(si)?(si+8+seg)%12:(si+4+seg)%12;
case 10:return(si%2===0?(9+seg):seg)%12;
case 12:return(si+seg)%12; case 16:return seg%12; case 20:return(si+seg)%12;
case 24:return(4+seg)%12;
case 30:return si%2===0?(seg<5?0:seg<10?5:seg<18?2:seg<25?6:8):(seg<5?4:seg<10?11:seg<18?8:seg<25?1:6);
case 40:return(si%2===0?seg:seg+3)%12;
case 45:return((si%3===0?0:si%3===1?4:8)+seg)%12;
case 60:return seg%12; default:return(si+seg)%12;
}
}
function planetStatus(pn, sign) {
return getPlanetStatus(pn, sign);
}
// ============================================================================
// 三、PACDARES 八维分析
// ============================================================================
export function computePACDARES(planets, asc) {
const ai=SIGNS.indexOf(asc), t=RAMAN_TABLES[asc];
const r={P:[],A:[],C:[],D:[],Ar:[],R:[],E:[],S:[]};
// P: Position
for(const pn of P_NAMES){const p=planets[pn];if(!p||p.error)continue;
r.P.push({planet:pn,pcn:PLANET_CN[pn],sign:p.sign,scn:p.sign_cn,house:p.house,deg:p.degree_in_sign?.toFixed(2),status:p.status,retro:p.retrograde,fn:getFunctionalNature(pn,asc),hr:getHousesRuled(pn,asc),ht:p.house?getHouseType(p.house):'',combust:p.combust||false});
}
// A: Aspect received
for(const pn of P_NAMES){const p=planets[pn];if(!p||p.error||p.house==null)continue;
const recv=[];
for(const f of P_NAMES){if(f===pn)continue;const fp=planets[f];if(!fp||fp.error||fp.house==null)continue;
for(const off of(PLANET_ASPECTS[f]||[7])){
if(((fp.house-1+off)%12)+1===p.house){
const fn=getFunctionalNature(f,asc);
recv.push({from:f,fcn:PLANET_CN[f],type:off===7?'对冲':(off===5||off===9)?'三分':off===4?'四分':off===8?'八分':`${off}`,fn,impact:['best_benefic','benefic','yogakaraka'].includes(fn)?'吉':['malefic','worst'].includes(fn)?'凶':'中'});
}
}
}
if(recv.length)r.A.push({planet:pn,pcn:PLANET_CN[pn],house:p.house,received:recv});
}
// C: Conjunction
for(let i=0;i<P_NAMES.length;i++){const p1=planets[P_NAMES[i]];if(!p1||p1.error)continue;
for(let j=i+1;j<P_NAMES.length;j++){const p2=planets[P_NAMES[j]];if(!p2||p2.error||p1.house!==p2.house)continue;
const f1=getFunctionalNature(P_NAMES[i],asc),f2=getFunctionalNature(P_NAMES[j],asc);
const poll=(['malefic','worst'].includes(f1)&&['best_benefic','benefic','yogakaraka'].includes(f2))||(['best_benefic','benefic','yogakaraka'].includes(f1)&&['malefic','worst'].includes(f2));
r.C.push({p1:P_NAMES[i],p1cn:PLANET_CN[P_NAMES[i]],f1,p2:P_NAMES[j],p2cn:PLANET_CN[P_NAMES[j]],f2,house:p1.house,pollution:poll});
}
}
// D: Dhana Yoga
const wH=[2,5,9,11],wL=new Set(wH.map(h=>SIGN_LORDS[SIGNS[(ai+h-1)%12]]));
const wl=[...wL];
for(let i=0;i<wl.length;i++)for(let j=i+1;j<wl.length;j++){
const pi=planets[wl[i]],pj=planets[wl[j]];if(!pi||!pj||pi.error||pj.error)continue;
if(pi.house===pj.house&&wH.includes(pi.house))r.D.push({type:'财富宫主同宫',lcn:`${PLANET_CN[wl[i]]}+${PLANET_CN[wl[j]]}`,house:pi.house,str:'中强'});
const hi=(ai+pi.house-1)%12,hj=(ai+pj.house-1)%12;
if(SIGN_LORDS[SIGNS[hi]]===wl[j]&&SIGN_LORDS[SIGNS[hj]]===wl[i])r.D.push({type:'财富宫主互换',lcn:`${PLANET_CN[wl[i]]}+${PLANET_CN[wl[j]]}`,house:`${pi.house}${pj.house}`,str:'强'});
}
// Ar: Arishta
for(const pn of['Saturn','Mars','Rahu']){const p=planets[pn];if(!p||p.error||![1,4,7,10].includes(p.house))continue;
const fn=getFunctionalNature(pn,asc);if(['malefic','worst'].includes(fn))r.Ar.push({type:'凶星角宫',pcn:PLANET_CN[pn],house:p.house,desc:`${PLANET_CN[pn]}(凶星)在角宫H${p.house}`});
}
const moon=planets.Moon;if(moon&&!moon.error){
const h2=moon.house===1?12:moon.house-1,h12=moon.house===12?1:moon.house+1;
if(!P_NAMES.some(pn=>planets[pn]&&!planets[pn].error&&planets[pn].house===h2)&&!P_NAMES.some(pn=>planets[pn]&&!planets[pn].error&&planets[pn].house===h12))
r.Ar.push({type:'Kemadruma',pcn:'月亮',house:moon.house,desc:'月亮2/12宫无星 — 情绪孤独'});
}
for(const h of[6,8,12]){const lord=SIGN_LORDS[SIGNS[(ai+h-1)%12]];const lp=planets[lord];if(!lp||lp.error||![6,8,12].includes(lp.house))continue;
r.Ar.push({type:'凶宫主落凶宫',pcn:PLANET_CN[lord],house:lp.house,desc:`H${h}主落H${lp.house}`});
}
// R: Raja Yoga
const kL=new Set([1,4,7,10].map(h=>SIGN_LORDS[SIGNS[(ai+h-1)%12]]));
const tL=new Set([1,5,9].map(h=>SIGN_LORDS[SIGNS[(ai+h-1)%12]]));
for(const k of kL)for(const tt of tL){if(k===tt)continue;const pk=planets[k],pt=planets[tt];if(!pk||!pt||pk.error||pt.error||pk.house!==pt.house)continue;
r.R.push({type:'Raja Yoga',lcn:`${PLANET_CN[k]}+${PLANET_CN[tt]}`,house:pk.house,str:[1,4,7,10].includes(pk.house)?'极强':[5,9].includes(pk.house)?'强':'中'});
}
if(t?.yk){const yp=planets[t.yk];if(yp&&!yp.error)r.R.push({type:'Yogakaraka',lcn:PLANET_CN[t.yk],house:yp.house,str:yp.status==='入庙'||yp.status==='入旺'?'极强':'中强'});}
// E: Exchange
for(let h1=1;h1<=12;h1++){const l1=SIGN_LORDS[SIGNS[(ai+h1-1)%12]];const lp1=planets[l1];if(!lp1||lp1.error||!lp1.house)continue;
const h2=lp1.house;const l2=SIGN_LORDS[SIGNS[(ai+h2-1)%12]];if(l2===l1)continue;const lp2=planets[l2];if(!lp2||lp2.error||lp2.house!==h1||h1>=h2)continue;
r.E.push({type:'Parivartana',houses:[h1,h2],lcn:`${PLANET_CN[l1]}+${PLANET_CN[l2]}`,desc:`H${h1}主(${PLANET_CN[l1]})在H${h2}↔H${h2}主(${PLANET_CN[l2]})在H${h1}`,
nature:[6,8,12].includes(h1)&&[6,8,12].includes(h2)?'Vipareeta':[6,8,12].includes(h1)||[6,8,12].includes(h2)?'混合':'吉'});
}
// S: Special
for(const pn of P_NAMES){const p=planets[pn];if(!p||p.error)continue;
if(p.retrograde)r.S.push({type:'逆行',pcn:PLANET_CN[pn],desc:`${PLANET_CN[pn]}逆行 — 能量内化`});
if(p.combust)r.S.push({type:'燃烧',pcn:PLANET_CN[pn],desc:`${PLANET_CN[pn]}燃烧(${p.combust_degree}°)`});
if(p.status==='落陷')r.S.push({type:'落陷',pcn:PLANET_CN[pn],desc:`${PLANET_CN[pn]}落陷在${p.sign_cn}`});
if(p.status==='入旺')r.S.push({type:'入旺',pcn:PLANET_CN[pn],desc:`${PLANET_CN[pn]}入旺在${p.sign_cn}`});
}
return r;
}
// ============================================================================
// 四、宫位互影响矩阵
// ============================================================================
export function computeHouseInfluence(planets, asc) {
const ai=SIGNS.indexOf(asc), matrix=[];
for(let h=1;h<=12;h++){
const si=(ai+h-1)%12,sign=SIGNS[si],lord=SIGN_LORDS[sign];
const items=[];
// 宫内
for(const pn of P_NAMES){const p=planets[pn];if(!p||p.error||p.house!==h)continue;
const fn=getFunctionalNature(pn,asc);
const sc=fn==='yogakaraka'?3:fn==='best_benefic'?2:fn==='benefic'?1:fn==='neutral'?0:fn==='malefic'?-1:fn==='worst'?-2:0;
items.push({src:`${PLANET_CN[pn]}(宫内)`,fn,sc,detail:PLANET_CN[pn]+fn});
}
// 相位
for(const f of P_NAMES){const fp=planets[f];if(!fp||fp.error||fp.house==null)continue;
for(const off of(PLANET_ASPECTS[f]||[7])){
if(((fp.house-1+off)%12)+1===h){const fn=getFunctionalNature(f,asc);
const sc=fn==='yogakaraka'?2:fn==='best_benefic'?1.5:fn==='benefic'?1:fn==='neutral'?0:fn==='malefic'?-1:fn==='worst'?-1.5:0;
items.push({src:`${PLANET_CN[f]}(${off}宫相位)`,fn,sc,detail:PLANET_CN[f]});break;}
}
}
// 宫主
const lp=planets[lord];if(lp&&!lp.error&&lp.house){
const sc=[1,4,5,7,9,10,11].includes(lp.house)?1:-0.5;
items.push({src:`宫主星${PLANET_CN[lord]}(H${lp.house})`,fn:getFunctionalNature(lord,asc),sc,detail:`${PLANET_CN[lord]}${lp.status}在H${lp.house}`});
}
const total=Math.round(items.reduce((s,i)=>s+i.sc,0)*10)/10;
matrix.push({house:h,sign,scn:SIGNS_CN[sign],lord,lcn:PLANET_CN[lord]||lord,items,total,
verdict:total>=2?'强力':total>=0.5?'吉':total>=-0.5?'中':total>=-2?'弱':'受克'});
}
return matrix;
}
// ============================================================================
// 五、Vargottama 检测
// ============================================================================
export function detectVargottama(planets) {
const r=[];
for(const pn of P_NAMES){const p=planets[pn];if(!p||p.error)continue;
const psi=SIGNS.indexOf(p.sign),d9si=vargaSI(p.degree_in_sign,psi,9);
if(p.sign===SIGNS[d9si])r.push({planet:pn,pcn:PLANET_CN[pn],d1:p.sign_cn,d9:SIGNS_CN[SIGNS[d9si]]});
}
return r;
}
// ============================================================================
// 六、行星频率分析
// ============================================================================
export function computeFrequency(planets) {
const r={};
for(const pn of P_NAMES){const p=planets[pn];if(!p||p.error)continue;
const psi=SIGNS.indexOf(p.sign),freq={};
for(const v of VARGA_DEFS){const vsi=v.d===1?psi:vargaSI(p.degree_in_sign,psi,v.d);freq[SIGNS[vsi]]=(freq[SIGNS[vsi]]||0)+1;}
const sorted=Object.entries(freq).sort((a,b)=>b[1]-a[1]);
const top=sorted[0],total=VARGA_DEFS.length,pct=Math.round(top[1]/total*100);
r[pn]={planet:pn,pcn:PLANET_CN[pn],topSign:top[0],topScn:SIGNS_CN[top[0]],count:top[1],pct,
level:pct>=50?'极高频':pct>=40?'高频':pct>=30?'中高频':pct>=20?'中等':'低频',
dist:sorted.slice(0,3).map(([s,c])=>({s,scn:SIGNS_CN[s],c,p:Math.round(c/total*100)}))};
}
return r;
}
// ============================================================================
// 七、D1 × D9 × D10 三角验证
// ============================================================================
export function computeTriangle(planets) {
const r=[];
for(const pn of P_NAMES){const p=planets[pn];if(!p||p.error)continue;
const psi=SIGNS.indexOf(p.sign);
const d9si=vargaSI(p.degree_in_sign,psi,9),d10si=vargaSI(p.degree_in_sign,psi,10);
const d9s=SIGNS[d9si],d10s=SIGNS[d10si];
const d1st=p.status,d9st=planetStatus(pn,d9s),d10st=planetStatus(pn,d10s);
let v='一致';
if(['入庙','入旺'].includes(d1st)&&['落陷'].includes(d9st))v='D1强D9弱(承诺未兑现)';
else if(['落陷'].includes(d1st)&&['入庙','入旺'].includes(d9st))v='D1弱D9强(绝地反击)';
else if(['入庙','入旺'].includes(d1st)&&['入庙','入旺'].includes(d9st))v='双重强势';
else if(['落陷'].includes(d1st)&&['落陷'].includes(d9st))v='双重弱势';
const vg=p.sign===d9s;
r.push({planet:pn,pcn:PLANET_CN[pn],d1sign:p.sign_cn,d1st,d9sign:SIGNS_CN[d9s],d9st,d10sign:SIGNS_CN[d10s],d10st,verdict:v,vargottama:vg});
}
return r;
}
// ============================================================================
// 八、Raman 宫位六步法(综合评估每宫吉凶)
// ============================================================================
export function computeRamanHouseScore(planets, asc) {
const ai=SIGNS.indexOf(asc), scores=[];
for(let h=1;h<=12;h++){
const si=(ai+h-1)%12,sign=SIGNS[si],lord=SIGN_LORDS[sign];
let pos=0,neg=0,details=[];
// Step1: 宫内行星
for(const pn of P_NAMES){const p=planets[pn];if(!p||p.error||p.house!==h)continue;
const fn=getFunctionalNature(pn,asc);
if(['best_benefic','benefic','yogakaraka'].includes(fn)){pos+=2;details.push(`${PLANET_CN[pn]}(吉)在宫内`);}
else if(['malefic','worst'].includes(fn)){neg+=2;details.push(`${PLANET_CN[pn]}(凶)在宫内`);}
else{pos+=0.5;details.push(`~${PLANET_CN[pn]}(中性)在宫内`);}
}
// Step2: 相位
for(const f of P_NAMES){const fp=planets[f];if(!fp||fp.error||fp.house==null)continue;
for(const off of(PLANET_ASPECTS[f]||[7])){
if(((fp.house-1+off)%12)+1===h){
const fn=getFunctionalNature(f,asc);
if(['best_benefic','benefic','yogakaraka'].includes(fn)){pos+=1;details.push(`${PLANET_CN[f]}吉相位`);}
else if(['malefic','worst'].includes(fn)){neg+=1;details.push(`${PLANET_CN[f]}凶相位`);}
break;
}
}
}
// Step3: 宫主星
const lp=planets[lord];
if(lp&&!lp.error&&lp.house){
if([1,4,5,7,9,10,11].includes(lp.house)){pos+=1.5;details.push(`✓宫主${PLANET_CN[lord]}在H${lp.house}(${lp.status})`);}
else if([6,8,12].includes(lp.house)){neg+=1.5;details.push(`✗宫主${PLANET_CN[lord]}在H${lp.house}(凶宫)`);}
else{details.push(`~宫主${PLANET_CN[lord]}在H${lp.house}`);}
if(lp.status==='入庙'||lp.status==='入旺'){pos+=1;details.push(`✓宫主${lp.status}`);}
if(lp.status==='落陷'){neg+=1;details.push(`✗宫主落陷`);}
}
const net=Math.round((pos-neg)*10)/10;
scores.push({house:h,sign,scn:SIGNS_CN[sign],lord,lcn:PLANET_CN[lord]||lord,pos,neg,net,details,
grade:net>=3?'A':net>=1?'B':net>=-1?'C':net>=-3?'D':'F'});
}
return scores;
}
// ============================================================================
// 七、Trikona-Kendra 三方四正映射分析
// ============================================================================
const SIGNS_LIST = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo','Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces'];
const LORD_OF = {Aries:'Mars',Taurus:'Venus',Gemini:'Mercury',Cancer:'Moon',Leo:'Sun',Virgo:'Mercury',Libra:'Venus',Scorpio:'Mars',Sagittarius:'Jupiter',Capricorn:'Saturn',Aquarius:'Saturn',Pisces:'Jupiter'};
function lordOfHouse(ascSign, h) {
const ai = SIGNS_LIST.indexOf(ascSign);
const signIdx = (ai + h - 1) % 12;
return LORD_OF[SIGNS_LIST[signIdx]];
}
function signOfHouse(ascSign, h) {
const ai = SIGNS_LIST.indexOf(ascSign);
return SIGNS_LIST[(ai + h - 1) % 12];
}
/**
* 计算三方四正映射分析
* Trikona(1-5-9) 和 Kendra(1-4-7-10) 宫主星间的互落关系
*/
export function computeTrikonaKendra(planets, ascSign) {
const trikonaHouses = [1, 5, 9];
const kendraHouses = [1, 4, 7, 10];
const allKeyHouses = [1, 4, 5, 7, 9, 10];
// 计算每个关键宫的主星及其落宫
const houseLords = {};
for (const h of allKeyHouses) {
const lord = lordOfHouse(ascSign, h);
const lp = planets[lord];
houseLords[h] = {
house: h,
lord,
lord_cn: PLANET_CN[lord],
sign: signOfHouse(ascSign, h),
sign_cn: SIGNS_CN[signOfHouse(ascSign, h)],
lordInHouse: lp && !lp.error ? lp.house : null,
lordStatus: lp && !lp.error ? lp.status : null,
lordRetro: lp && !lp.error ? lp.retrograde : false,
};
}
// 三方映射:1→5→9 的主星互落
const trikonaConnections = [];
for (let i = 0; i < trikonaHouses.length; i++) {
for (let j = i + 1; j < trikonaHouses.length; j++) {
const h1 = trikonaHouses[i], h2 = trikonaHouses[j];
const l1 = houseLords[h1], l2 = houseLords[h2];
// 同宫
if (l1.lordInHouse === h2 && l2.lordInHouse === h1) {
trikonaConnections.push({ type: 'mutual', from: h1, to: h2, desc: `${l1.lord_cn}(${h1}主)↔${l2.lord_cn}(${h2}主) 互换`, quality: 'excellent' });
} else if (l1.lordInHouse === h2) {
trikonaConnections.push({ type: 'one-way', from: h1, to: h2, desc: `${l1.lord_cn}(${h1}主)落入${h2}`, quality: 'good' });
} else if (l2.lordInHouse === h1) {
trikonaConnections.push({ type: 'one-way', from: h2, to: h1, desc: `${l2.lord_cn}(${h2}主)落入${h1}`, quality: 'good' });
}
// 同宫(两主星在同一宫)
if (l1.lordInHouse && l1.lordInHouse === l2.lordInHouse) {
trikonaConnections.push({ type: 'conjunction', from: h1, to: h2, desc: `${l1.lord_cn}(${h1}主)+${l2.lord_cn}(${h2}主)同在H${l1.lordInHouse}`, quality: 'strong' });
}
}
}
// 四正映射:1-4-7-10 的主星互落
const kendraConnections = [];
for (let i = 0; i < kendraHouses.length; i++) {
for (let j = i + 1; j < kendraHouses.length; j++) {
const h1 = kendraHouses[i], h2 = kendraHouses[j];
const l1 = houseLords[h1], l2 = houseLords[h2];
if (l1.lordInHouse === h2 && l2.lordInHouse === h1) {
kendraConnections.push({ type: 'mutual', from: h1, to: h2, desc: `${l1.lord_cn}(${h1}主)↔${l2.lord_cn}(${h2}主) 互换`, quality: 'excellent' });
} else if (l1.lordInHouse === h2) {
kendraConnections.push({ type: 'one-way', from: h1, to: h2, desc: `${l1.lord_cn}(${h1}主)落入${h2}`, quality: 'good' });
} else if (l2.lordInHouse === h1) {
kendraConnections.push({ type: 'one-way', from: h2, to: h1, desc: `${l2.lord_cn}(${h2}主)落入${h1}`, quality: 'good' });
}
if (l1.lordInHouse && l1.lordInHouse === l2.lordInHouse) {
kendraConnections.push({ type: 'conjunction', from: h1, to: h2, desc: `${l1.lord_cn}(${h1}主)+${l2.lord_cn}(${h2}主)同在H${l1.lordInHouse}`, quality: 'strong' });
}
}
}
// 三方×四正交叉连接
const crossConnections = [];
for (const th of trikonaHouses) {
for (const kh of kendraHouses) {
if (th === kh) continue; // 跳过1宫(同属两组)
const tl = houseLords[th], kl = houseLords[kh];
// 三方主落入四正
if (tl.lordInHouse === kh) {
crossConnections.push({ type: 'trikona-to-kendra', from: th, to: kh, desc: `${tl.lord_cn}(${th}主/三方)→H${kh}(四正)`, quality: 'raja' });
}
// 四正主落入三方
if (kl.lordInHouse === th) {
crossConnections.push({ type: 'kendra-to-trikona', from: kh, to: th, desc: `${kl.lord_cn}(${kh}主/四正)→H${th}(三方)`, quality: 'raja' });
}
// 互换 = 大 Raja Yoga
if (tl.lordInHouse === kh && kl.lordInHouse === th) {
crossConnections.push({ type: 'mutual-raja', from: th, to: kh, desc: `${tl.lord_cn}(${th}主)↔${kl.lord_cn}(${kh}主) 互换 = Raja Yoga`, quality: 'maha-raja' });
}
}
}
// 整体评估
const totalPositive = trikonaConnections.filter(c => c.quality !== 'poor').length
+ kendraConnections.filter(c => c.quality !== 'poor').length
+ crossConnections.length;
const hasMahaRaja = crossConnections.some(c => c.quality === 'maha-raja');
const hasRaja = crossConnections.some(c => c.quality === 'raja' || c.quality === 'maha-raja');
let overallRating = '普通';
if (hasMahaRaja) overallRating = '卓越 — 大帝王格局';
else if (hasRaja && totalPositive >= 4) overallRating = '优秀 — 强力 Raja Yoga';
else if (totalPositive >= 4) overallRating = '良好 — 多重三方四正连接';
else if (totalPositive >= 2) overallRating = '中等 — 有一定三方四正连接';
else if (totalPositive >= 1) overallRating = '一般 — 少量连接';
return {
houseLords,
trikonaConnections,
kendraConnections,
crossConnections,
overallRating,
hasMahaRaja,
hasRaja,
};
}