Files
Jyotisha/scripts/yoga_expansion.py

222 lines
7.9 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Yoga规则扩展模块 v1.0
基于 dashaflow (MIT) yoga.py 补充检测规则,用于提升F1
新增检测:
- Kemadruma Yoga (孤月)
- Adhi Yoga (吉星护卫)
- Amala Yoga (10宫吉星)
- Saraswati Yoga (智慧三杰)
- Lakshmi Yoga (财富女神)
- Graha Yuddha (行星战争)
- Gandanta (业力节点)
"""
from typing import Dict, List
from aspects import TIGHT_ORB_DEGREE
SIGNS = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo',
'Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces']
KENDRA = {1,4,7,10}
TRIKONA = {1,5,9}
DUSTHANA = {6,8,12}
BENEFICS = {"Jupiter","Venus","Mercury"}
MALEFICS = {"Saturn","Mars","Sun","Rahu","Ketu"}
GANDANTA_JUNCTIONS = [(3,4),(7,8),(11,0)] # Cancer→Leo, Scorpio→Sag, Pisces→Aries
GANDANTA_ORB = 3.3333
def detect_kemadruma(planets: Dict) -> Dict:
"""Kemadruma Yoga: Moon孤立,无行星在2宫或12宫"""
moon = planets.get('Moon', {})
moon_sign = moon.get('sign', '')
if moon_sign not in SIGNS:
return {'present': False}
moon_idx = SIGNS.index(moon_sign)
sign_2nd = (moon_idx + 1) % 12
sign_12th = (moon_idx - 1) % 12
has_support = False
for pn, pd in planets.items():
if pn in ('Sun','Moon','Rahu','Ketu'):
continue
p_sign = pd.get('sign', '')
p_idx = SIGNS.index(p_sign) if p_sign in SIGNS else -1
if p_idx in (sign_2nd, sign_12th):
has_support = True
break
return {
'present': not has_support,
'name': 'Kemadruma Yoga',
'description': 'Moon孤立无援—人生孤独感强、自我依赖' if not has_support else 'Kemadruma已解除',
'planets': ['Moon'],
}
def detect_adhi_yoga(planets: Dict) -> List[Dict]:
"""Adhi Yoga: 吉星在6/7/8宫从月亮"""
moon = planets.get('Moon', {})
moon_sign = moon.get('sign', '')
if moon_sign not in SIGNS:
return []
moon_idx = SIGNS.index(moon_sign)
adhi_planets = []
target = {6,7,8}
for pn in BENEFICS:
pd = planets.get(pn)
if not pd:
continue
p_sign = pd.get('sign', '')
p_idx = SIGNS.index(p_sign) if p_sign in SIGNS else -1
house_from_moon = ((p_idx - moon_idx) % 12) + 1
if house_from_moon in target:
adhi_planets.append(pn)
if len(adhi_planets) >= 2:
return [{'name': 'Adhi Yoga', 'planets': adhi_planets,
'description': f'吉星{",".join(adhi_planets)}在6/7/8宫—宿命式成功'}]
return []
def detect_amala_yoga(planets: Dict, asc_sign: str) -> List[Dict]:
"""Amala Yoga: 天然吉星在10宫"""
asc_idx = SIGNS.index(asc_sign) if asc_sign in SIGNS else 0
yogas = []
for pn in BENEFICS:
pd = planets.get(pn)
if not pd:
continue
p_sign = pd.get('sign', '')
p_idx = SIGNS.index(p_sign) if p_sign in SIGNS else -1
house = ((p_idx - asc_idx) % 12) + 1
if house == 10:
yogas.append({'name': 'Amala Yoga', 'planet': pn,
'description': f'{pn}在10宫—善行得声望、晚年福报'})
return yogas
def detect_saraswati_yoga(planets: Dict, asc_sign: str) -> Dict:
"""Saraswati Yoga: Jupiter+Venus+Mercury在kendra/trikona/2宫 + Jupiter强"""
asc_idx = SIGNS.index(asc_sign) if asc_sign in SIGNS else 0
good_houses = KENDRA | TRIKONA | {2}
ok = []
for pn in ("Jupiter","Venus","Mercury"):
pd = planets.get(pn)
if not pd:
continue
p_sign = pd.get('sign', '')
p_idx = SIGNS.index(p_sign) if p_sign in SIGNS else -1
house = ((p_idx - asc_idx) % 12) + 1
if house in good_houses:
ok.append(pn)
if len(ok) == 3:
jup = planets.get('Jupiter', {})
jup_strong = jup.get('dignity', '') in ('own','exalted') or jup.get('house') in KENDRA
if jup_strong:
return {'present': True, 'name': 'Saraswati Yoga', 'planets': ok,
'description': '智慧三杰聚吉宫—博学多才、表达能力卓越'}
return {'present': False}
def detect_lakshmi_yoga(planets: Dict, asc_sign: str) -> Dict:
"""Lakshmi Yoga: 9宫主在own/exalted + Venus在own/exalted kendra"""
asc_idx = SIGNS.index(asc_sign) if asc_sign in SIGNS else 0
sign_lords = {'Aries':'Mars','Taurus':'Venus','Gemini':'Mercury','Cancer':'Moon',
'Leo':'Sun','Virgo':'Mercury','Libra':'Venus','Scorpio':'Mars',
'Sagittarius':'Jupiter','Capricorn':'Saturn','Aquarius':'Saturn','Pisces':'Jupiter'}
# 9宫主
h9_sign = SIGNS[(asc_idx + 8) % 12]
h9_lord = sign_lords[h9_sign]
h9_data = planets.get(h9_lord, {})
h9_strong = h9_data.get('dignity', '') in ('own','exalted')
# Venus
venus = planets.get('Venus', {})
venus_house = venus.get('house', 0)
venus_strong = venus_house in KENDRA and venus.get('dignity', '') in ('own','exalted')
if h9_strong and venus_strong:
return {'present': True, 'name': 'Lakshmi Yoga', 'planets': [h9_lord, 'Venus'],
'description': '9宫主吉+Venus强—巨大财富与繁荣'}
return {'present': False}
def detect_graha_yuddha(planets: Dict) -> List[Dict]:
"""Graha Yuddha: 两颗行星相距<1°"""
war_planets = ['Mars','Mercury','Jupiter','Venus','Saturn']
wars = []
for i in range(len(war_planets)):
for j in range(i+1, len(war_planets)):
p1, p2 = war_planets[i], war_planets[j]
d1, d2 = planets.get(p1,{}), planets.get(p2,{})
if not d1 or not d2:
continue
lon1 = d1.get('degree', 0) % 360
lon2 = d2.get('degree', 0) % 360
diff = abs(lon1 - lon2) % 360
if diff > 180:
diff = 360 - diff
if diff <= TIGHT_ORB_DEGREE:
winner = p1 if lon1 > lon2 else p2
loser = p2 if winner == p1 else p1
wars.append({
'name': 'Graha Yuddha', 'planet1': p1, 'planet2': p2,
'separation': round(diff, 3), 'winner': winner, 'loser': loser,
'description': f'{p1}-{p2}行星战争({diff:.2f}°)—{loser}被削弱',
})
return wars
def detect_gandanta(planets: Dict) -> List[Dict]:
"""Gandanta: 行星在水火交界3°20'范围内"""
points = []
for pn, pd in planets.items():
lon = pd.get('degree', 0) % 360
sign_idx = int(lon / 30) % 12
deg = lon % 30
for water_idx, fire_idx in GANDANTA_JUNCTIONS:
if sign_idx == water_idx and deg >= (30 - GANDANTA_ORB):
points.append({'name': 'Gandanta', 'planet': pn, 'sign': SIGNS[sign_idx],
'degree': round(deg, 1), 'junction': f'{SIGNS[water_idx]}-{SIGNS[fire_idx]}',
'description': f'{pn}在水火交界—业力节点、灵性转化'})
if sign_idx == fire_idx and deg <= GANDANTA_ORB:
points.append({'name': 'Gandanta', 'planet': pn, 'sign': SIGNS[sign_idx],
'degree': round(deg, 1), 'junction': f'{SIGNS[water_idx]}-{SIGNS[fire_idx]}',
'description': f'{pn}在水火交界—业力节点、灵性转化'})
return points
def detect_all_yogas(planets: Dict, asc_sign: str = 'Aries') -> List[Dict]:
"""检测所有扩展Yoga"""
results = []
r = detect_kemadruma(planets)
if r.get('present'):
results.append(r)
results.extend(detect_adhi_yoga(planets))
results.extend(detect_amala_yoga(planets, asc_sign))
r = detect_saraswati_yoga(planets, asc_sign)
if r.get('present'):
results.append(r)
r = detect_lakshmi_yoga(planets, asc_sign)
if r.get('present'):
results.append(r)
results.extend(detect_graha_yuddha(planets))
results.extend(detect_gandanta(planets))
return results