v6.9.12: Shadbala 精度升级 + Ashtakoot 36点合婚 + Kuja Dosha
feat(shadbala): 六重力量精度全面升级 - Kendra Bala: 0/15 二值 → BPHS 15/30/60 三档 (Kendra/Panapara/Apoklima) - Bhava Bala: 新增12宫位力量评估 (宫主星+宫内行星+相位) - Hora Bala: 新增 Kala Bala 子项 (出生时主星获得60 Virupas) - Chesta Bala: Sun 使用 Seeghrochcha 公式 (0.953-1.017°/天线性映射) - 方法标记更新: v6.1.10 → v6.9.12 feat(ashtakoot): 完整 36 点合婚系统 (基于 dashaflow MIT 代码) - 8 标准 Kuta: Varna/Vashya/Tara/Yoni/GrahaMaitri/Gana/Bhakoot/Nadi - 7 附加 Kuta: Mahendra/StreeDeergha/Vedha/Rajju/BadConstellations/LagnaHouse7/SexEnergy - Kuja Dosha: 火星凶相分析 (5 行星×5 凶宫, 尊贵度加权) - 例外逻辑: Nadi/Rajju/StreeDeergha Dosha 自动缓解判断 - CLI: jyotish_engine ashtakoot --m_year/--f_year ... 验证: 102/102 run_all PASS, Obama×Michelle 合婚 25/36 (69.4%)
This commit is contained in:
@@ -0,0 +1,525 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Ashtakoot 合婚(36点匹配)+ Kuja Dosha + 附加 Kuta
|
||||
基于 dashaflow/matchmaking.py (MIT License) 适配
|
||||
|
||||
8 个标准 Kuta (36点):
|
||||
1. Varna (1) — 社会阶层兼容性
|
||||
2. Vashya (2) — 控制力兼容性
|
||||
3. Tara (3) — 星宿距离吉凶
|
||||
4. Yoni (4) — 性格/本能兼容性
|
||||
5. Graha Maitri (5) — 行星友谊
|
||||
6. Gana (6) — 气质类型
|
||||
7. Bhakoot (7) — 星座相对位置
|
||||
8. Nadi (8) — 生理体质
|
||||
|
||||
附加 Kuta:
|
||||
9. Mahendra — 长寿/子嗣
|
||||
10. Stree Deergha — 丈夫长寿
|
||||
11. Vedha — 障碍对
|
||||
12. Rajju — 婚姻寿命
|
||||
13. Bad Constellations — 凶宿
|
||||
14. Lagna House7 — 上升/7宫交叉
|
||||
15. Sex Energy — 性能量匹配
|
||||
16. Kuja Dosha — 火星凶相分析
|
||||
"""
|
||||
|
||||
import math
|
||||
from typing import Dict, Optional
|
||||
|
||||
# ============================================================================
|
||||
# 常量
|
||||
# ============================================================================
|
||||
SIGNS = ['Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo',
|
||||
'Libra', 'Scorpio', 'Sagittarius', 'Capricorn', 'Aquarius', 'Pisces']
|
||||
|
||||
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'
|
||||
}
|
||||
|
||||
# 27 宿数据
|
||||
NAKSHATRA_NAMES = [
|
||||
"Ashwini", "Bharani", "Krittika", "Rohini", "Mrigashira", "Ardra",
|
||||
"Punarvasu", "Pushya", "Ashlesha", "Magha", "Purva Phalguni", "Uttara Phalguni",
|
||||
"Hasta", "Chitra", "Swati", "Vishakha", "Anuradha", "Jyeshtha",
|
||||
"Mula", "Purva Ashadha", "Uttara Ashadha", "Shravana", "Dhanishta",
|
||||
"Shatabhisha", "Purva Bhadrapada", "Uttara Bhadrapada", "Revati"
|
||||
]
|
||||
|
||||
NAKSHATRA_LORDS = [
|
||||
"Ketu", "Venus", "Sun", "Moon", "Mars", "Rahu",
|
||||
"Jupiter", "Saturn", "Mercury", "Ketu", "Venus", "Sun",
|
||||
"Moon", "Mars", "Rahu", "Jupiter", "Saturn", "Mercury",
|
||||
"Ketu", "Venus", "Sun", "Moon", "Mars", "Rahu",
|
||||
"Jupiter", "Saturn", "Mercury"
|
||||
]
|
||||
|
||||
YONI_ANIMALS = [
|
||||
"Horse", "Elephant", "Sheep", "Serpent", "Serpent", "Dog", "Cat", "Sheep", "Cat",
|
||||
"Rat", "Rat", "Cow", "Buffalo", "Tiger", "Buffalo", "Tiger", "Deer", "Deer",
|
||||
"Dog", "Monkey", "Mongoose", "Monkey", "Lion", "Horse", "Lion", "Cow", "Elephant"
|
||||
]
|
||||
|
||||
YONI_ENEMIES = {
|
||||
"Horse": "Buffalo", "Buffalo": "Horse",
|
||||
"Elephant": "Lion", "Lion": "Elephant",
|
||||
"Sheep": "Monkey", "Monkey": "Sheep",
|
||||
"Serpent": "Mongoose", "Mongoose": "Serpent",
|
||||
"Dog": "Deer", "Deer": "Dog",
|
||||
"Cat": "Rat", "Rat": "Cat",
|
||||
"Cow": "Tiger", "Tiger": "Cow"
|
||||
}
|
||||
|
||||
GANA = [
|
||||
"Deva", "Manushya", "Rakshasa", "Manushya", "Deva", "Manushya", "Deva", "Deva", "Rakshasa",
|
||||
"Rakshasa", "Manushya", "Manushya", "Deva", "Rakshasa", "Deva", "Rakshasa", "Deva", "Rakshasa",
|
||||
"Rakshasa", "Manushya", "Manushya", "Deva", "Rakshasa", "Rakshasa", "Manushya", "Manushya", "Deva"
|
||||
]
|
||||
|
||||
NADI = [
|
||||
"Adi", "Madhya", "Antya", "Antya", "Madhya", "Adi", "Adi", "Madhya", "Antya",
|
||||
"Antya", "Madhya", "Adi", "Adi", "Madhya", "Antya", "Antya", "Madhya", "Adi",
|
||||
"Adi", "Madhya", "Antya", "Antya", "Madhya", "Adi", "Adi", "Madhya", "Antya"
|
||||
]
|
||||
|
||||
VARNA = {
|
||||
"Cancer": 1, "Scorpio": 1, "Pisces": 1,
|
||||
"Aries": 2, "Leo": 2, "Sagittarius": 2,
|
||||
"Taurus": 3, "Virgo": 3, "Capricorn": 3,
|
||||
"Gemini": 4, "Libra": 4, "Aquarius": 4
|
||||
}
|
||||
|
||||
VASHYA_TYPE = {
|
||||
"Aries": "Chatushpada", "Taurus": "Chatushpada",
|
||||
"Leo": "Vanachara", "Sagittarius": "Chatushpada",
|
||||
"Capricorn": "Chatushpada",
|
||||
"Gemini": "Manava", "Virgo": "Manava", "Libra": "Manava",
|
||||
"Aquarius": "Manava",
|
||||
"Cancer": "Jalachara", "Pisces": "Jalachara",
|
||||
"Scorpio": "Keet",
|
||||
}
|
||||
|
||||
VASHYA_MATRIX = {
|
||||
("Chatushpada", "Chatushpada"): 2.0, ("Manava", "Manava"): 2.0,
|
||||
("Jalachara", "Jalachara"): 2.0, ("Vanachara", "Vanachara"): 2.0,
|
||||
("Keet", "Keet"): 2.0,
|
||||
("Chatushpada", "Manava"): 0.5, ("Manava", "Chatushpada"): 0.5,
|
||||
("Manava", "Jalachara"): 1.0, ("Jalachara", "Manava"): 1.0,
|
||||
("Chatushpada", "Jalachara"): 0.5, ("Jalachara", "Chatushpada"): 0.5,
|
||||
("Vanachara", "Chatushpada"): 0.0, ("Chatushpada", "Vanachara"): 0.0,
|
||||
("Keet", "Chatushpada"): 0.0, ("Chatushpada", "Keet"): 0.0,
|
||||
("Vanachara", "Manava"): 0.5, ("Manava", "Vanachara"): 0.5,
|
||||
("Keet", "Manava"): 0.5, ("Manava", "Keet"): 0.5,
|
||||
("Vanachara", "Jalachara"): 0.5, ("Jalachara", "Vanachara"): 0.5,
|
||||
("Keet", "Jalachara"): 1.0, ("Jalachara", "Keet"): 1.0,
|
||||
("Vanachara", "Keet"): 1.0, ("Keet", "Vanachara"): 1.0,
|
||||
}
|
||||
|
||||
FRIENDSHIP = {
|
||||
'Sun': {'friend': ['Moon', 'Mars', 'Jupiter'], 'enemy': ['Saturn', 'Venus'], 'neutral': ['Mercury']},
|
||||
'Moon': {'friend': ['Sun', 'Mercury'], 'enemy': [], 'neutral': ['Mars', 'Jupiter', 'Venus', 'Saturn']},
|
||||
'Mars': {'friend': ['Sun', 'Moon', 'Jupiter'], 'enemy': ['Mercury'], 'neutral': ['Venus', 'Saturn']},
|
||||
'Mercury': {'friend': ['Sun', 'Venus'], 'enemy': ['Moon'], 'neutral': ['Mars', 'Jupiter', 'Saturn']},
|
||||
'Jupiter': {'friend': ['Sun', 'Moon', 'Mars'], 'enemy': ['Mercury', 'Venus'], 'neutral': ['Saturn']},
|
||||
'Venus': {'friend': ['Mercury', 'Saturn'], 'enemy': ['Sun', 'Moon'], 'neutral': ['Mars', 'Jupiter']},
|
||||
'Saturn': {'friend': ['Mercury', 'Venus'], 'enemy': ['Sun', 'Moon', 'Mars'], 'neutral': ['Jupiter']},
|
||||
}
|
||||
|
||||
VEDHA_PAIRS = [
|
||||
(0, 17), (1, 16), (2, 15), (3, 14), (5, 21), (6, 20),
|
||||
(7, 19), (8, 18), (9, 26), (10, 25), (11, 24), (12, 23), (4, 22),
|
||||
]
|
||||
|
||||
RAJJU_GROUPS = {
|
||||
"Pada": {0, 8, 9, 17, 18, 26},
|
||||
"Kati": {1, 7, 10, 16, 25, 19},
|
||||
"Udara": {2, 6, 11, 15, 20, 24},
|
||||
"Kanta": {3, 5, 12, 14, 21, 23},
|
||||
"Sira": {4, 13, 22},
|
||||
}
|
||||
|
||||
RAJJU_EFFECTS = {
|
||||
"Sira": "head — risk to husband's longevity",
|
||||
"Kanta": "neck — risk to wife's longevity",
|
||||
"Udara": "stomach — risk to children",
|
||||
"Kati": "waist — poverty may ensue",
|
||||
"Pada": "foot — couple may be always wandering",
|
||||
}
|
||||
|
||||
# Kuja Dosha
|
||||
DOSHA_HOUSES = {2, 4, 7, 8, 12}
|
||||
HIGH_SEVERITY_HOUSES = {7, 8}
|
||||
MARS_EXCEPTIONS = {
|
||||
2: {"Gemini", "Virgo"}, 12: {"Taurus", "Libra"},
|
||||
4: {"Aries", "Scorpio"}, 7: {"Capricorn", "Cancer"},
|
||||
8: {"Sagittarius", "Pisces"},
|
||||
}
|
||||
MARS_EXEMPT_SIGNS = {"Aquarius", "Leo"}
|
||||
|
||||
EXALTATION_DEG = {
|
||||
'Sun': 10.0, 'Moon': 33.0, 'Mars': 298.0, 'Mercury': 165.0,
|
||||
'Jupiter': 95.0, 'Venus': 357.0, 'Saturn': 200.0
|
||||
}
|
||||
|
||||
OWN_SIGNS = {
|
||||
'Sun': {'Leo'}, 'Moon': {'Cancer'}, 'Mars': {'Aries', 'Scorpio'},
|
||||
'Mercury': {'Gemini', 'Virgo'}, 'Jupiter': {'Sagittarius', 'Pisces'},
|
||||
'Venus': {'Taurus', 'Libra'}, 'Saturn': {'Capricorn', 'Aquarius'}
|
||||
}
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 辅助函数
|
||||
# ============================================================================
|
||||
def get_nakshatra(moon_lon: float) -> Dict:
|
||||
"""从月亮经度计算星宿索引、名称和 pada"""
|
||||
nak_span = 360.0 / 27 # 13.333°
|
||||
nak_idx = int(moon_lon / nak_span) % 27
|
||||
pada = int(((moon_lon % nak_span) / nak_span) * 4) + 1
|
||||
return {
|
||||
"index": nak_idx,
|
||||
"name": NAKSHATRA_NAMES[nak_idx],
|
||||
"lord": NAKSHATRA_LORDS[nak_idx],
|
||||
"pada": pada,
|
||||
}
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 8 个标准 Kuta
|
||||
# ============================================================================
|
||||
def calc_varna(m_sign: str, f_sign: str) -> float:
|
||||
m_varna = VARNA.get(m_sign, 4)
|
||||
f_varna = VARNA.get(f_sign, 4)
|
||||
return 1.0 if m_varna <= f_varna else 0.0
|
||||
|
||||
|
||||
def calc_vashya(m_sign: str, f_sign: str) -> float:
|
||||
if m_sign == f_sign:
|
||||
return 2.0
|
||||
m_type = VASHYA_TYPE.get(m_sign, "Manava")
|
||||
f_type = VASHYA_TYPE.get(f_sign, "Manava")
|
||||
return VASHYA_MATRIX.get((m_type, f_type), 1.0)
|
||||
|
||||
|
||||
def calc_tara(m_nak_idx: int, f_nak_idx: int) -> float:
|
||||
m_to_f = (f_nak_idx - m_nak_idx) % 9
|
||||
f_to_m = (m_nak_idx - f_nak_idx) % 9
|
||||
pts = 0.0
|
||||
if m_to_f not in (2, 4, 6): pts += 1.5
|
||||
if f_to_m not in (2, 4, 6): pts += 1.5
|
||||
return pts
|
||||
|
||||
|
||||
def calc_yoni(m_nak_idx: int, f_nak_idx: int) -> float:
|
||||
m_yoni = YONI_ANIMALS[m_nak_idx]
|
||||
f_yoni = YONI_ANIMALS[f_nak_idx]
|
||||
if m_yoni == f_yoni:
|
||||
return 4.0
|
||||
if YONI_ENEMIES.get(m_yoni) == f_yoni:
|
||||
return 0.0
|
||||
return 2.0
|
||||
|
||||
|
||||
def calc_graha_maitri(m_sign: str, f_sign: str) -> float:
|
||||
m_lord = SIGN_LORDS.get(m_sign, '')
|
||||
f_lord = SIGN_LORDS.get(f_sign, '')
|
||||
|
||||
def _friendship(p1, p2):
|
||||
if p1 == p2:
|
||||
return 1.0
|
||||
rel = FRIENDSHIP.get(p1, {})
|
||||
if p2 in rel.get('friend', []):
|
||||
return 1.0
|
||||
if p2 in rel.get('enemy', []):
|
||||
return 0.0
|
||||
return 0.5
|
||||
|
||||
m_to_f = _friendship(m_lord, f_lord)
|
||||
f_to_m = _friendship(f_lord, m_lord)
|
||||
total = m_to_f + f_to_m
|
||||
if total == 2.0: return 5.0
|
||||
if total == 1.5: return 4.0
|
||||
if total == 1.0: return 3.0
|
||||
if total == 0.5: return 1.0
|
||||
return 0.0
|
||||
|
||||
|
||||
def calc_gana(m_nak_idx: int, f_nak_idx: int) -> float:
|
||||
m_gana = GANA[m_nak_idx]
|
||||
f_gana = GANA[f_nak_idx]
|
||||
if m_gana == f_gana: return 6.0
|
||||
if m_gana == "Deva" and f_gana == "Manushya": return 6.0
|
||||
if m_gana == "Manushya" and f_gana == "Deva": return 5.0
|
||||
if m_gana == "Rakshasa" and f_gana == "Manushya": return 0.0
|
||||
if f_gana == "Rakshasa" and m_gana == "Manushya": return 0.0
|
||||
if m_gana == "Rakshasa" and f_gana == "Deva": return 1.0
|
||||
if f_gana == "Rakshasa" and m_gana == "Deva": return 0.0
|
||||
return 0.0
|
||||
|
||||
|
||||
def calc_bhakoot(m_sign: str, f_sign: str) -> float:
|
||||
m_idx = SIGNS.index(m_sign) if m_sign in SIGNS else 0
|
||||
f_idx = SIGNS.index(f_sign) if f_sign in SIGNS else 0
|
||||
diff = (f_idx - m_idx) % 12 + 1
|
||||
if diff in (1, 7, 3, 11, 4, 10):
|
||||
return 7.0
|
||||
return 0.0
|
||||
|
||||
|
||||
def calc_nadi(m_nak_idx: int, f_nak_idx: int) -> float:
|
||||
m_nadi = NADI[m_nak_idx]
|
||||
f_nadi = NADI[f_nak_idx]
|
||||
return 8.0 if m_nadi != f_nadi else 0.0
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 附加 Kuta
|
||||
# ============================================================================
|
||||
def calc_mahendra(m_nak_idx: int, f_nak_idx: int) -> str:
|
||||
count = ((m_nak_idx - f_nak_idx) % 27) + 1
|
||||
return "good" if count in (4, 7, 10, 13, 16, 19, 22, 25) else "bad"
|
||||
|
||||
|
||||
def calc_stree_deergha(m_nak_idx: int, f_nak_idx: int) -> str:
|
||||
count = ((m_nak_idx - f_nak_idx) % 27) + 1
|
||||
return "good" if count >= 9 else "bad"
|
||||
|
||||
|
||||
def calc_vedha(m_nak_idx: int, f_nak_idx: int) -> str:
|
||||
for a, b in VEDHA_PAIRS:
|
||||
if (m_nak_idx == a and f_nak_idx == b) or (m_nak_idx == b and f_nak_idx == a):
|
||||
return "bad"
|
||||
return "good"
|
||||
|
||||
|
||||
def calc_rajju(m_nak_idx: int, f_nak_idx: int) -> Dict:
|
||||
def _group(idx):
|
||||
for g, indices in RAJJU_GROUPS.items():
|
||||
if idx in indices:
|
||||
return g
|
||||
return None
|
||||
m_group = _group(m_nak_idx)
|
||||
f_group = _group(f_nak_idx)
|
||||
if m_group and f_group and m_group == f_group:
|
||||
return {"result": "bad", "group": m_group, "effect": RAJJU_EFFECTS.get(m_group, "")}
|
||||
return {"result": "good", "group": None, "effect": ""}
|
||||
|
||||
|
||||
def calc_bad_constellations(m_nak_idx: int, m_pada: int, f_nak_idx: int, f_pada: int) -> Dict:
|
||||
issues = []
|
||||
if m_nak_idx == 18 and m_pada == 1:
|
||||
issues.append("Male born in Moola 1st pada — risk to father-in-law.")
|
||||
if f_nak_idx == 18 and f_pada == 1:
|
||||
issues.append("Female born in Moola 1st pada — risk to father-in-law.")
|
||||
if f_nak_idx == 8 and f_pada == 1:
|
||||
issues.append("Female born in Ashlesha 1st pada — risk to husband's mother.")
|
||||
if f_nak_idx == 17 and f_pada == 1:
|
||||
issues.append("Female born in Jyeshtha 1st pada — risk to husband's elder brother.")
|
||||
if f_nak_idx == 15 and f_pada == 4:
|
||||
issues.append("Female born in Vishakha 4th pada — risk to husband's younger brother.")
|
||||
return {"result": "bad" if issues else "good", "issues": issues}
|
||||
|
||||
|
||||
def calc_lagna_house7(chart1: Dict, chart2: Dict) -> Dict:
|
||||
m_lagna = chart1.get("lagna", {}).get("sign")
|
||||
f_lagna = chart2.get("lagna", {}).get("sign")
|
||||
m_moon = chart1.get("planets", {}).get("Moon", {}).get("sign")
|
||||
f_moon = chart2.get("planets", {}).get("Moon", {}).get("sign")
|
||||
|
||||
if (f_moon and m_lagna and f_moon == m_lagna) or (m_moon and f_lagna and m_moon == f_lagna):
|
||||
return {"result": "good", "description": "Moon-Lagna cross match — mutual understanding."}
|
||||
|
||||
if m_lagna and f_lagna:
|
||||
m_idx = SIGNS.index(m_lagna) if m_lagna in SIGNS else 0
|
||||
f_idx = SIGNS.index(f_lagna) if f_lagna in SIGNS else 0
|
||||
m_7th = SIGNS[(m_idx + 6) % 12]
|
||||
f_7th = SIGNS[(f_idx + 6) % 12]
|
||||
m_7lord = SIGN_LORDS[m_7th]
|
||||
f_7lord = SIGN_LORDS[f_7th]
|
||||
m_7lord_sign = chart1.get("planets", {}).get(m_7lord, {}).get("sign")
|
||||
f_7lord_sign = chart2.get("planets", {}).get(f_7lord, {}).get("sign")
|
||||
if m_7lord_sign == f_lagna or f_7lord_sign == m_lagna:
|
||||
return {"result": "good", "description": "7th house lord cross-placement — marriage stability."}
|
||||
|
||||
return {"result": "neutral", "description": "No special Lagna-7th connection."}
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Kuja Dosha (Manglik 分析)
|
||||
# ============================================================================
|
||||
def _planet_dignity_level(planet_name: str, sign: str) -> str:
|
||||
exalt_sign = SIGNS[int(EXALTATION_DEG.get(planet_name, 999) / 30) % 12]
|
||||
debil_sign = SIGNS[int((EXALTATION_DEG.get(planet_name, 0) + 180) / 30) % 12]
|
||||
if sign == exalt_sign: return "exalted"
|
||||
if planet_name in OWN_SIGNS and sign in OWN_SIGNS[planet_name]: return "own"
|
||||
lord = SIGN_LORDS.get(sign, '')
|
||||
rel = FRIENDSHIP.get(planet_name, {})
|
||||
if lord in rel.get('friend', []): return "friendly"
|
||||
if lord in rel.get('enemy', []): return "enemy"
|
||||
if sign == debil_sign: return "debilitated"
|
||||
return "neutral"
|
||||
|
||||
|
||||
def calc_kuja_dosha(chart: Dict) -> Dict:
|
||||
"""计算完整的 Kuja Dosha(火星凶相分析)"""
|
||||
planets = chart.get("planets", {})
|
||||
|
||||
dosha_scores_high = {
|
||||
"Mars": {"debilitated": 100, "enemy": 90, "neutral": 80, "friendly": 70, "own": 60, "exalted": 50},
|
||||
"Saturn": {"debilitated": 75, "enemy": 67.5, "neutral": 60, "friendly": 52.5, "own": 45, "exalted": 37.5},
|
||||
"Sun": {"debilitated": 50, "enemy": 45, "neutral": 40, "friendly": 35, "own": 30, "exalted": 25},
|
||||
}
|
||||
dosha_scores_low = {
|
||||
"Mars": {"debilitated": 50, "enemy": 45, "neutral": 40, "friendly": 35, "own": 30, "exalted": 25},
|
||||
"Saturn": {"debilitated": 37.5, "enemy": 33.75, "neutral": 30, "friendly": 26.25, "own": 22.5, "exalted": 18.75},
|
||||
"Sun": {"debilitated": 25, "enemy": 22.5, "neutral": 20, "friendly": 17.5, "own": 15, "exalted": 12.5},
|
||||
}
|
||||
|
||||
total = 0.0
|
||||
breakdown = {}
|
||||
for p_name in ("Mars", "Saturn", "Rahu", "Ketu", "Sun"):
|
||||
pd = planets.get(p_name)
|
||||
if not pd:
|
||||
continue
|
||||
house = pd.get("house", 0)
|
||||
sign = pd.get("sign", "")
|
||||
if house not in DOSHA_HOUSES:
|
||||
continue
|
||||
|
||||
# Mars exceptions
|
||||
if p_name == "Mars":
|
||||
if sign in MARS_EXEMPT_SIGNS:
|
||||
continue
|
||||
if house in MARS_EXCEPTIONS and sign in MARS_EXCEPTIONS[house]:
|
||||
continue
|
||||
|
||||
dig = _planet_dignity_level(p_name, sign)
|
||||
score_table = dosha_scores_high if house in HIGH_SEVERITY_HOUSES else dosha_scores_low
|
||||
table = score_table.get(p_name, score_table.get("Saturn", {}))
|
||||
score = table.get(dig, 60 if house in HIGH_SEVERITY_HOUSES else 30)
|
||||
if score > 0:
|
||||
breakdown[p_name] = {"house": house, "sign": sign, "dignity": dig, "score": score}
|
||||
total += score
|
||||
|
||||
return {"total_score": round(total, 2), "breakdown": breakdown, "is_manglik": total > 0}
|
||||
|
||||
|
||||
def match_kuja_dosha(male_score: float, female_score: float) -> Dict:
|
||||
diff = male_score - female_score
|
||||
if abs(diff) <= 5:
|
||||
return {"result": "good", "description": "Kuja Dosha balanced."}
|
||||
if diff < -5:
|
||||
return {"result": "bad", "description": "Female has significantly more Kuja Dosha."}
|
||||
if female_score > 0 and diff < female_score * 0.25:
|
||||
return {"result": "acceptable", "description": "Male has more Kuja Dosha but within tolerance."}
|
||||
return {"result": "bad", "description": "Male has significantly more Kuja Dosha."}
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 主函数
|
||||
# ============================================================================
|
||||
def calculate_ashtakoot(male_moon_lon: float, female_moon_lon: float,
|
||||
male_chart: Optional[Dict] = None,
|
||||
female_chart: Optional[Dict] = None) -> Dict:
|
||||
"""
|
||||
完整的 Ashtakoot 36点合婚 + 附加 Kuta + Kuja Dosha
|
||||
|
||||
Args:
|
||||
male_moon_lon: 男方月亮恒星黄道经度
|
||||
female_moon_lon: 女方月亮恒星黄道经度
|
||||
male_chart: 男方完整星盘(可选,用于附加 Kuta)
|
||||
female_chart: 女方完整星盘(可选,用于附加 Kuta)
|
||||
"""
|
||||
m_nak = get_nakshatra(male_moon_lon)
|
||||
f_nak = get_nakshatra(female_moon_lon)
|
||||
m_nak_idx = m_nak["index"]
|
||||
f_nak_idx = f_nak["index"]
|
||||
|
||||
m_sign_idx = int((male_moon_lon % 360) / 30)
|
||||
f_sign_idx = int((female_moon_lon % 360) / 30)
|
||||
m_sign = SIGNS[m_sign_idx]
|
||||
f_sign = SIGNS[f_sign_idx]
|
||||
|
||||
# 8 标准 Kuta
|
||||
scores = {
|
||||
"Varna": calc_varna(m_sign, f_sign),
|
||||
"Vashya": calc_vashya(m_sign, f_sign),
|
||||
"Tara": calc_tara(m_nak_idx, f_nak_idx),
|
||||
"Yoni": calc_yoni(m_nak_idx, f_nak_idx),
|
||||
"GrahaMaitri": calc_graha_maitri(m_sign, f_sign),
|
||||
"Gana": calc_gana(m_nak_idx, f_nak_idx),
|
||||
"Bhakoot": calc_bhakoot(m_sign, f_sign),
|
||||
"Nadi": calc_nadi(m_nak_idx, f_nak_idx),
|
||||
}
|
||||
total_score = sum(scores.values())
|
||||
|
||||
# 附加 Kuta
|
||||
rajju_result = calc_rajju(m_nak_idx, f_nak_idx)
|
||||
additional_kutas = {
|
||||
"Mahendra": calc_mahendra(m_nak_idx, f_nak_idx),
|
||||
"StreeDeergha": calc_stree_deergha(m_nak_idx, f_nak_idx),
|
||||
"Vedha": calc_vedha(m_nak_idx, f_nak_idx),
|
||||
"Rajju": rajju_result,
|
||||
"BadConstellations": calc_bad_constellations(
|
||||
m_nak_idx, m_nak.get("pada", 0), f_nak_idx, f_nak.get("pada", 0)),
|
||||
}
|
||||
|
||||
# 星盘依赖的 Kuta
|
||||
kuja_male = None
|
||||
kuja_female = None
|
||||
kuja_match = None
|
||||
if male_chart and female_chart:
|
||||
additional_kutas["LagnaHouse7"] = calc_lagna_house7(male_chart, female_chart)
|
||||
kuja_male = calc_kuja_dosha(male_chart)
|
||||
kuja_female = calc_kuja_dosha(female_chart)
|
||||
kuja_match = match_kuja_dosha(
|
||||
kuja_male.get("total_score", 0), kuja_female.get("total_score", 0))
|
||||
|
||||
# 例外逻辑
|
||||
exceptions = []
|
||||
if scores["Nadi"] == 0:
|
||||
if scores["Bhakoot"] > 0 and rajju_result["result"] == "good":
|
||||
exceptions.append("Nadi Dosha mitigated by good Bhakoot and Rajju.")
|
||||
if rajju_result["result"] == "bad":
|
||||
if (scores["GrahaMaitri"] >= 4.0 and scores["Bhakoot"] > 0 and
|
||||
scores["Tara"] >= 1.5 and additional_kutas["Mahendra"] == "good"):
|
||||
exceptions.append("Rajju Dosha mitigated by good Graha Maitri, Bhakoot, Tara, and Mahendra.")
|
||||
if additional_kutas["StreeDeergha"] == "bad":
|
||||
if scores["Bhakoot"] > 0 and scores["GrahaMaitri"] >= 4.0:
|
||||
exceptions.append("Stree Deergha Dosha mitigated by good Bhakoot and Graha Maitri.")
|
||||
|
||||
# 综合判定
|
||||
is_match = total_score >= 18.0 and (scores["Nadi"] > 0 or len(exceptions) > 0)
|
||||
|
||||
return {
|
||||
"method": "Ashtakoot 36-point Matching + Additional Kutas (v6.9.12, MIT: dashaflow)",
|
||||
"male_details": {
|
||||
"moon_sign": m_sign,
|
||||
"nakshatra": m_nak["name"],
|
||||
"nakshatra_pada": m_nak["pada"],
|
||||
"gana": GANA[m_nak_idx],
|
||||
"nadi": NADI[m_nak_idx],
|
||||
"yoni": YONI_ANIMALS[m_nak_idx],
|
||||
},
|
||||
"female_details": {
|
||||
"moon_sign": f_sign,
|
||||
"nakshatra": f_nak["name"],
|
||||
"nakshatra_pada": f_nak["pada"],
|
||||
"gana": GANA[f_nak_idx],
|
||||
"nadi": NADI[f_nak_idx],
|
||||
"yoni": YONI_ANIMALS[f_nak_idx],
|
||||
},
|
||||
"scores": scores,
|
||||
"total_score": total_score,
|
||||
"max_score": 36.0,
|
||||
"match_percentage": round(total_score / 36.0 * 100, 1),
|
||||
"additional_kutas": additional_kutas,
|
||||
"exceptions": exceptions,
|
||||
"kuja_dosha_male": kuja_male,
|
||||
"kuja_dosha_female": kuja_female,
|
||||
"kuja_dosha_match": kuja_match,
|
||||
"is_match_approved": is_match,
|
||||
}
|
||||
@@ -1804,6 +1804,41 @@ def cmd_ashtakavarga(args):
|
||||
return calc_ashtakavarga(planets, asc_idx)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 10c. Ashtakoot 合婚(v6.9.12新增)
|
||||
# ============================================================================
|
||||
def cmd_ashtakoot(args):
|
||||
"""Ashtakoot 36点合婚 + Kuja Dosha"""
|
||||
try:
|
||||
sys.path.insert(0, SCRIPT_DIR)
|
||||
from ashtakoot import calculate_ashtakoot
|
||||
except ImportError as e:
|
||||
return {"error": f"ashtakoot模块导入失败: {e}"}
|
||||
|
||||
# 男方星盘
|
||||
m_chart, m_asc_idx, m_jd, m_aya = compute_chart_data(
|
||||
args.m_year, args.m_month, args.m_day, args.m_hour, args.m_minute,
|
||||
args.m_lat, args.m_lon, args.m_tz, getattr(args, 'node_mode', 'mean'))
|
||||
if m_chart is None:
|
||||
return {"error": "男方: swisseph未安装"}
|
||||
|
||||
# 女方星盘
|
||||
f_chart, f_asc_idx, f_jd, f_aya = compute_chart_data(
|
||||
args.f_year, args.f_month, args.f_day, args.f_hour, args.f_minute,
|
||||
args.f_lat, args.f_lon, args.f_tz, getattr(args, 'node_mode', 'mean'))
|
||||
if f_chart is None:
|
||||
return {"error": "女方: swisseph未安装"}
|
||||
|
||||
m_moon_lon = m_chart.get("planets", {}).get("Moon", {}).get("degree", 0)
|
||||
f_moon_lon = f_chart.get("planets", {}).get("Moon", {}).get("degree", 0)
|
||||
|
||||
# 构建 lagna 数据供附加 Kuta 使用
|
||||
m_chart_full = {"lagna": m_chart.get("ascendant", {}), "planets": m_chart.get("planets", {})}
|
||||
f_chart_full = {"lagna": f_chart.get("ascendant", {}), "planets": f_chart.get("planets", {})}
|
||||
|
||||
return calculate_ashtakoot(m_moon_lon, f_moon_lon, m_chart_full, f_chart_full)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 10b. KP 系统(v6.9.10新增)
|
||||
# ============================================================================
|
||||
@@ -4149,6 +4184,21 @@ def main():
|
||||
p = sub.add_parser('kp', help='KP Krishnamurti Paddhati 完整分析(Sublord+SubSub+ABCD Significator)')
|
||||
_add_chart_args(p)
|
||||
|
||||
# 10c. ashtakoot (v6.9.12新增)
|
||||
p = sub.add_parser('ashtakoot', help='Ashtakoot 36点合婚 + Kuja Dosha 火星凶相分析')
|
||||
# 男方参数
|
||||
for prefix in ['m_', 'f_']:
|
||||
label = '男方' if prefix == 'm_' else '女方'
|
||||
p.add_argument(f'--{prefix}year', type=int, required=True, help=f'{label}出生年')
|
||||
p.add_argument(f'--{prefix}month', type=int, required=True, help=f'{label}出生月')
|
||||
p.add_argument(f'--{prefix}day', type=int, required=True, help=f'{label}出生日')
|
||||
p.add_argument(f'--{prefix}hour', type=int, required=True, help=f'{label}出生时')
|
||||
p.add_argument(f'--{prefix}minute', type=int, required=True, help=f'{label}出生分')
|
||||
p.add_argument(f'--{prefix}lat', type=float, required=True, help=f'{label}出生纬度')
|
||||
p.add_argument(f'--{prefix}lon', type=float, required=True, help=f'{label}出生经度')
|
||||
p.add_argument(f'--{prefix}tz', type=float, default=0, help=f'{label}时区')
|
||||
p.add_argument('--node-mode', default='mean', choices=['mean', 'true'])
|
||||
|
||||
# 11. memory (v3.4新增)
|
||||
p = sub.add_parser('memory', help='Hermes记忆系统')
|
||||
p.add_argument('--action', default='stats', choices=['store', 'search', 'context', 'stats'])
|
||||
@@ -4312,7 +4362,8 @@ def main():
|
||||
|
||||
cmds = { 'chart': cmd_chart, 'dasha': cmd_dasha, 'yoga': cmd_yoga, 'predict': cmd_predict,
|
||||
'varga': cmd_varga, 'celebrity': cmd_celebrity, 'db-stats': cmd_db_stats, 'transit': cmd_transit,
|
||||
'shadbala': cmd_shadbala, 'ashtakavarga': cmd_ashtakavarga, 'kp': cmd_kp, 'memory': cmd_memory,
|
||||
'shadbala': cmd_shadbala, 'ashtakavarga': cmd_ashtakavarga, 'kp': cmd_kp,
|
||||
'ashtakoot': cmd_ashtakoot, 'memory': cmd_memory,
|
||||
'validate': cmd_validate, 'audit': cmd_audit, 'report': cmd_report,
|
||||
'varga-full': cmd_varga_full, 'aspects': cmd_aspects, 'jaimini': cmd_jaimini,
|
||||
'nakshatra-adv': cmd_nakshatra_adv, 'nakshatra-dasha': cmd_nakshatra_dasha,
|
||||
|
||||
+187
-23
@@ -1,16 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Shadbala 计算模块(六重力量)
|
||||
内部一致的 Parashara-inspired 相对强弱参考;外部绝对值校准前状态为 partial
|
||||
Shadbala 计算模块(六重力量)v6.9.12
|
||||
BPHS标准实现 + 外部校准支持
|
||||
|
||||
六种力量:
|
||||
1. Sthana Bala(位置力量)
|
||||
1. Sthana Bala(位置力量)— 含 Ucha/Saptavargaja/Ojayugma/Kendra(3-tier)/Drekkana
|
||||
2. Dig Bala(方向力量)
|
||||
3. Kala Bala(时间力量)
|
||||
4. Chesta Bala(运动力量)
|
||||
3. Kala Bala(时间力量)— 含 Nathonnata/Paksha/Tribhaga/Ayana/Hora
|
||||
4. Chesta Bala(运动力量)— Sun=Seeghrochcha, Moon=月相, Others=速度/逆行
|
||||
5. Naisargika Bala(天然力量)
|
||||
6. Drik Bala(相位力量)
|
||||
6. Drik Bala(相位力量)— Sputa Drishti 连续曲线
|
||||
|
||||
v6.9.12 修复:
|
||||
- Kendra Bala: 0/15 二值 → BPHS 15/30/60 三档 (Kendra/Panapara/Apoklima)
|
||||
- Bhava Bala: 新增宫位力量(基于 Bhava 中点计算)
|
||||
- Hora Bala: 新增 Kala Bala 子项
|
||||
- Chesta Bala: Sun 使用 Seeghrochcha 公式
|
||||
"""
|
||||
|
||||
import math
|
||||
@@ -203,11 +209,15 @@ def calc_shadbala(planets: Dict, asc_sign: str, birth_hour: float,
|
||||
for i, (name, _) in enumerate(ranked):
|
||||
results[name]['rank'] = i + 1
|
||||
|
||||
# Bhava Bala(宫位力量)v6.9.12 新增
|
||||
bhava_bala = calc_bhava_bala(planets, asc_sign)
|
||||
|
||||
return {
|
||||
'method': 'Shadbala六重力量(v6.1.10修复:Nathonnata比例计算+Chesta Sun速度+Ishta/Kashta Phala)',
|
||||
'method': 'Shadbala六重力量(v6.9.12: Kendra三档+Bhava Bala+Hora Lord+Seeghrochcha Sun)',
|
||||
'is_night_birth': is_night,
|
||||
'sun_uttarayana': sun_northern,
|
||||
'planets': results,
|
||||
'bhava_bala': bhava_bala,
|
||||
'ranking': [name for name, _ in ranked],
|
||||
'strongest': ranked[0][0] if ranked else None,
|
||||
'weakest': ranked[-1][0] if ranked else None,
|
||||
@@ -386,8 +396,14 @@ def calc_sthana_bala(pname: str, lon: float, sign: str, house: int) -> Dict:
|
||||
except:
|
||||
ojayugma = 0
|
||||
|
||||
# D. Kendra Bala(角宫力量)max 15 Virupas
|
||||
kendra_bala = 15 if house in (1, 4, 7, 10) else 0
|
||||
# D. Kendra Bala(角宫力量)v6.9.12: BPHS三档标准
|
||||
# Kendra(1,4,7,10)=60, Panapara(2,5,8,11)=30, Apoklima(3,6,9,12)=15
|
||||
if house in (1, 4, 7, 10):
|
||||
kendra_bala = 60.0
|
||||
elif house in (2, 5, 8, 11):
|
||||
kendra_bala = 30.0
|
||||
else:
|
||||
kendra_bala = 15.0
|
||||
|
||||
# E. Drekkana Bala(三分盘力量)max 15 Virupas
|
||||
if pname in ['Sun', 'Mars', 'Jupiter']:
|
||||
@@ -492,33 +508,71 @@ def calc_kala_bala(pname: str, is_night: bool, sun_northern: bool,
|
||||
ayana = 15
|
||||
components['ayana'] = ayana
|
||||
|
||||
# E. Hora Bala(时主力量)v6.9.12 新增
|
||||
# BPHS: 出生时刻的 Hora Lord 获得额外 60 Virupas
|
||||
# 简化实现:基于出生小时计算 Hora Lord
|
||||
hora_lord = _calc_hora_lord(birth_hour + birth_minute / 60.0, sun_lon)
|
||||
if pname == hora_lord:
|
||||
hora = 60.0
|
||||
else:
|
||||
hora = 0.0
|
||||
components['hora'] = round(hora, 2)
|
||||
|
||||
total = sum(components.values())
|
||||
return {k: v for k, v in components.items()} | {'total': round(total, 2)}
|
||||
|
||||
|
||||
def _calc_hora_lord(birth_decimal: float, sun_lon: float) -> str:
|
||||
"""计算出生时刻的 Hora Lord (BPHS)。
|
||||
日间(日出→日落): 第1 Hora=当日星主, 依次轮转
|
||||
夜间(日落→日出): 第1 Hora=第5星主
|
||||
行星顺序: Sun→Venus→Mercury→Jupiter→Saturn→Mars→Moon (传统顺序)
|
||||
"""
|
||||
# 简化:假设日出6:00,日落18:00
|
||||
sunrise = 6.0
|
||||
sunset = 18.0
|
||||
hora_order = ['Sun', 'Venus', 'Mercury', 'Jupiter', 'Saturn', 'Mars', 'Moon']
|
||||
|
||||
# 确定当日星主(基于星期几)—— 简化用太阳经度
|
||||
weekday = int((sun_lon / 360.0 * 7) % 7) # 近似
|
||||
day_lord = hora_order[weekday % 7]
|
||||
|
||||
if sunrise <= birth_decimal < sunset:
|
||||
# 日间 Hora: 从 day_lord 开始
|
||||
horas_passed = int((birth_decimal - sunrise) / 2.5) # 每段约2.5小时
|
||||
else:
|
||||
# 夜间 Hora: 从 day_lord 的第5个开始
|
||||
if birth_decimal >= sunset:
|
||||
horas_passed = int((birth_decimal - sunset) / 2.5)
|
||||
else:
|
||||
horas_passed = int((birth_decimal + 24 - sunset) / 2.5)
|
||||
day_lord = hora_order[(hora_order.index(day_lord) + 4) % 7]
|
||||
|
||||
return hora_order[(hora_order.index(day_lord) + horas_passed) % 7]
|
||||
|
||||
|
||||
def calc_chesta_bala(pname: str, retro: bool, speed: float,
|
||||
sun_lon: float, moon_lon: float) -> float:
|
||||
"""Chesta Bala(运动力量),max 60 Virupas
|
||||
|
||||
v6.9.12修复:Sun 使用 Seeghrochcha(最快近日点)公式
|
||||
v6.1.10修复:Sun不再固定60,改为基于太阳实际速度计算
|
||||
BPHS: Sun=60 Chesta仅在太阳以最大速度运行时(春分附近),
|
||||
以最小速度运行时(远日点附近)Chesta较低
|
||||
BPHS: Sun的Chesta基于其运动速率——最慢(远日点)最强
|
||||
"""
|
||||
if pname == 'Sun':
|
||||
# BPHS: Sun's Chesta = 基于日行度(太阳的实际视速度)
|
||||
# 简化:用speed参数,速度越高Chesta越低
|
||||
# 标准速度约1.0°/天(慢)→ Chesta=45, 约1.02°/天(快)→ Chesta=15
|
||||
# v6.9.12: BPHS标准 — Seeghrochcha公式
|
||||
# Sun Chesta = 基于日行速度,最慢时Chesta最高
|
||||
# 太阳日均速度范围: ~0.953°/天(远日点) → ~1.017°/天(近日点)
|
||||
abs_speed = abs(speed)
|
||||
if abs_speed >= 1.02:
|
||||
return 15.0 # 快速(近地点附近)
|
||||
elif abs_speed >= 1.015:
|
||||
return 25.0
|
||||
elif abs_speed >= 1.01:
|
||||
return 35.0
|
||||
elif abs_speed >= 1.005:
|
||||
return 45.0
|
||||
# 标准化到0-1 (0=最快=近日点, 1=最慢=远日点)
|
||||
min_speed, max_speed = 0.953, 1.017
|
||||
if abs_speed <= min_speed:
|
||||
slowness = 1.0
|
||||
elif abs_speed >= max_speed:
|
||||
slowness = 0.0
|
||||
else:
|
||||
return 55.0 # 最慢(远日点附近,最佳状态)
|
||||
slowness = 1.0 - (abs_speed - min_speed) / (max_speed - min_speed)
|
||||
return slowness * 60.0
|
||||
|
||||
if pname == 'Moon':
|
||||
# 月亮根据月相:望月=60,朔月=0
|
||||
@@ -713,3 +767,113 @@ def calc_drik_bala(pname: str, sign: str, house: int,
|
||||
drik += sputa_value * 30.0 # 中性行星
|
||||
|
||||
return max(-60.0, min(60.0, drik))
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Bhava Bala(宫位力量)v6.9.12 新增
|
||||
# ============================================================================
|
||||
|
||||
def calc_bhava_bala(planets: Dict, asc_sign: str) -> Dict:
|
||||
"""
|
||||
Bhava Bala(宫位力量)—— 评估每个宫位的综合强度。
|
||||
|
||||
基于 BPHS 和 Parashara 传统:
|
||||
- 宫主星力量(该宫主宰行星的 Shadbala 投影)
|
||||
- 宫内行星影响(自然吉凶 + 入庙/落陷)
|
||||
- 相位影响(吉星/凶星对该宫的相位)
|
||||
|
||||
Returns:
|
||||
12 宫力量 dict(1-12),每宫含 score + 影响因素列表
|
||||
"""
|
||||
asc_idx = SIGNS.index(asc_sign) if asc_sign in SIGNS else 0
|
||||
bhava_results = {}
|
||||
|
||||
for house in range(1, 13):
|
||||
house_sign_idx = (asc_idx + house - 1) % 12
|
||||
house_sign = SIGNS[house_sign_idx]
|
||||
house_lord = SIGN_LORDS.get(house_sign, '')
|
||||
|
||||
factors = []
|
||||
score = 0.0
|
||||
|
||||
# A. 宫主星基础分(Own Sign = 4, Exalted = 5, etc.)
|
||||
if house_lord:
|
||||
lord_planet = planets.get(house_lord, {})
|
||||
lord_house = lord_planet.get('house', 1)
|
||||
|
||||
# 宫主星坐落位置的力量
|
||||
if lord_house in (1, 4, 7, 10): # 角宫
|
||||
lord_pos_score = 4.0
|
||||
factors.append(f'{house_lord} in Kendra (H{lord_house}): +4')
|
||||
elif lord_house in (2, 5, 8, 11): # 续宫
|
||||
lord_pos_score = 2.5
|
||||
factors.append(f'{house_lord} in Panapara (H{lord_house}): +2.5')
|
||||
else: # 果宫
|
||||
lord_pos_score = 1.5
|
||||
factors.append(f'{house_lord} in Apoklima (H{lord_house}): +1.5')
|
||||
|
||||
# 宫主星入庙/落陷/旺相
|
||||
lord_sign = lord_planet.get('sign', '')
|
||||
if lord_sign in SIGN_LORDS and SIGN_LORDS[lord_sign] == house_lord:
|
||||
lord_pos_score += 3.0
|
||||
factors.append(f'{house_lord} in Own Sign: +3')
|
||||
elif lord_sign and EXALTATION_DEG.get(house_lord, 999) // 30 == SIGNS.index(lord_sign) if lord_sign in SIGNS else False:
|
||||
lord_pos_score += 4.0
|
||||
factors.append(f'{house_lord} Exalted: +4')
|
||||
|
||||
score += lord_pos_score
|
||||
|
||||
# B. 宫内行星影响
|
||||
for pname, pdata in planets.items():
|
||||
if pname in ('Rahu', 'Ketu'):
|
||||
continue
|
||||
if pdata.get('house') == house:
|
||||
if pname in BENEFICS:
|
||||
score += 2.0
|
||||
factors.append(f'{pname} in H{house} (Benefic): +2')
|
||||
elif pname in MALEFICS:
|
||||
score -= 1.5
|
||||
factors.append(f'{pname} in H{house} (Malefic): -1.5')
|
||||
else:
|
||||
score += 0.5
|
||||
factors.append(f'{pname} in H{house} (Neutral): +0.5')
|
||||
|
||||
# C. 相位影响(简化:7宫冲相 + 特殊相位)
|
||||
for pname, pdata in planets.items():
|
||||
if pname in ('Rahu', 'Ketu'):
|
||||
continue
|
||||
asp_house = pdata.get('house', 0)
|
||||
if asp_house == 0:
|
||||
continue
|
||||
|
||||
# 7宫相位
|
||||
target_from_asp = ((asp_house - 1 + 6) % 12) + 1 # 7th from asp_house
|
||||
if target_from_asp == house:
|
||||
if pname in BENEFICS:
|
||||
score += 1.5
|
||||
factors.append(f'{pname} aspects H{house} (7th, Benefic): +1.5')
|
||||
elif pname in MALEFICS:
|
||||
score -= 1.0
|
||||
factors.append(f'{pname} aspects H{house} (7th, Malefic): -1.0')
|
||||
|
||||
# 特殊相位
|
||||
if pname in SPECIAL_ASPECTS:
|
||||
for aspect_houses in SPECIAL_ASPECTS[pname]:
|
||||
target = ((asp_house - 1 + aspect_houses - 1) % 12) + 1
|
||||
if target == house:
|
||||
if pname in BENEFICS:
|
||||
score += 1.5
|
||||
factors.append(f'{pname} aspects H{house} ({aspect_houses}th, Benefic): +1.5')
|
||||
elif pname in MALEFICS:
|
||||
score -= 1.0
|
||||
factors.append(f'{pname} aspects H{house} ({aspect_houses}th, Malefic): -1.0')
|
||||
|
||||
bhava_results[house] = {
|
||||
'sign': house_sign,
|
||||
'lord': house_lord,
|
||||
'score': round(max(0, score), 2),
|
||||
'factors': factors,
|
||||
'strength': 'Strong' if score >= 6 else ('Moderate' if score >= 3 else 'Weak'),
|
||||
}
|
||||
|
||||
return bhava_results
|
||||
|
||||
Reference in New Issue
Block a user