Files
Jyotisha/scripts/marriage_counting.py
T
Jesse_ChenandClaude Opus 5.5 ba97fd5980 fix(marriage): counting method reports a neutral geometry index only; real Parivartana no longer crashes (BUG-1183/1184)
TASK-upstream-sync4-20261002 T1 (upstream yinduzhanxing 0afa2780 for the
idea; wording is ours, in Chinese, per decision 2). _interpret_marriage_count
and _marriage_recommendations no longer state relationship counts, order,
quality or loyalty; the Marriage-counting evidence is always neutral and
carries only geometric_index (the dead D9-quality line in the adjudication is
removed). The count is computed before the Parivartana override (the old order
raised UnboundLocalError) and the exchange check now tests a real exchange
instead of a conjunction with the dispositor.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
2026-10-02 14:01:13 +08:00

393 lines
15 KiB
Python
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.
"""
Marriage Counting Method (婚姻计数法) —— Bhrigu 体系 v7.0
Jyotish Vedic Astrology Skill
来源:bhrigu-pada-dasha-marriage-counting.md
算法:D1 第7宫主在 D1 的星座(A) → D9 的星座(B) → 从A数到B(含首尾)= 关系几何指数
产品口径(TASK-upstream-sync4-20261002 决策 2,BUG-1183):本模块只输出几何指数,
不输出婚姻或关系的次数、先后与质量断语。字段 marriage_count 为兼容旧消费方保留,
值与 geometric_index 相同。
v7.0 新增:
- Parivartana自动重算(原为TODO)
- D9 Venus/Mars/7宫主完整状态评估
- Upapada Loga整合
"""
from typing import Dict, Optional, List
# 星座名称
SIGN_CN = ['白羊座','金牛座','双子座','巨蟹座','狮子座','处女座',
'天秤座','天蝎座','射手座','摩羯座','水瓶座','双鱼座']
def sign_of(lon: float) -> int:
"""从黄道经度计算星座序号 (0-11)"""
return int((lon % 360) / 30)
def marriage_counting_method(
d1_house7_lord: str,
d1_planet_lons: Dict[str, float],
d9_planet_lons: Dict[str, float],
d1_houses: Optional[Dict] = None,
d9_houses: Optional[Dict] = None,
parivartana_check: bool = True
) -> Dict:
"""
婚姻计数法 (Marriage Counting Method)
参数:
d1_house7_lord: D1 第7宫主星名称 (如 'Venus')
d1_planet_lons: D1 行星经度字典
d9_planet_lons: D9 行星经度字典
d1_houses: D1 宫位数据 (可选,用于 Parivartana 检查)
d9_houses: D9 宫位数据 (可选)
parivartana_check: 是否检查 Parivartana (默认 True)
返回:
dict: {
'method': 'Marriage Counting Method',
'd1_7th_lord': str,
'point_A': {'sign': int, 'sign_cn': str}, # D1 中7宫主星座
'point_B': {'sign': int, 'sign_cn': str}, # D9 中7宫主星座
'distance': int, # 从A数到B (包含A和B)
'geometric_index': int, # 关系几何指数(不是婚姻或关系次数)
'marriage_count': int, # 兼容字段,同 geometric_index
'interpretation': str, # 解读
'parivartana': dict or None, # Parivartana 检查结果
'warnings': list,
}
"""
warnings = []
# Step 1: 找到 D1 第7宫主
if not d1_house7_lord:
return {'error': '未提供 D1 第7宫主', 'method': 'Marriage Counting Method'}
# Step 2: 找到 7宫主在 D1 中的星座 (A)
if d1_house7_lord not in d1_planet_lons:
warnings.append(f"D1 中未找到 {d1_house7_lord} 的位置")
return {'error': f"D1 中未找到 {d1_house7_lord}", 'method': 'Marriage Counting Method'}
d1_lon = d1_planet_lons[d1_house7_lord]
point_A = sign_of(d1_lon)
# Step 3: 找到 7宫主在 D9 中的星座 (B)
if d1_house7_lord not in d9_planet_lons:
warnings.append(f"D9 中未找到 {d1_house7_lord} 的位置")
return {'error': f"D9 中未找到 {d1_house7_lord}", 'method': 'Marriage Counting Method'}
d9_lon = d9_planet_lons[d1_house7_lord]
point_B = sign_of(d9_lon)
# Step 4: 计数 (从 A 到 B,包含 A 和 B)。须在 Parivartana 重算之前求出:
# 旧代码先在警告里引用 distance 再赋值,有交换时抛 UnboundLocalError(BUG-1184)。
if point_B >= point_A:
distance = point_B - point_A + 1
else:
# 跨越白羊座0°: 从 A 到双鱼座(11) + 从白羊座(0) 到 B
distance = (11 - point_A + 1) + (point_B + 1)
# Parivartana 检查
parivartana = None
if parivartana_check and d1_houses:
parivartana = _check_parivartana(
d1_house7_lord, point_A, d1_houses, d1_planet_lons
)
if parivartana and parivartana.get('has_parivartana'):
# v7.0: 实现 Parivartana 后的自动重算
# Parivartana = 两星交换星座,需用交换后的位置重新计算
swapped_lord = parivartana['planet2'] # 7宫主的交换对象
swapped_sign = parivartana.get('planet2_sign', point_A)
# 用交换后的星座作为新的 Point A
point_A_swapped = swapped_sign
# 重新计算距离
if point_B >= point_A_swapped:
distance_swapped = point_B - point_A_swapped + 1
else:
distance_swapped = (11 - point_A_swapped + 1) + (point_B + 1)
warnings.append(
f"Parivartana(行星交换)!{d1_house7_lord} 与 {swapped_lord} 交换。"
f" 原计数={distance},交换后计数={distance_swapped}。"
f" 以交换后计数为准。"
)
# 使用交换后的结果
distance = distance_swapped
marriage_count = distance
# 解读
interpretation = _interpret_marriage_count(marriage_count, point_A, point_B)
return {
'method': 'Marriage Counting Method (Bhrigu)',
'd1_7th_lord': d1_house7_lord,
'point_A': {
'sign': point_A,
'sign_cn': SIGN_CN[point_A],
'degree': d1_lon,
},
'point_B': {
'sign': point_B,
'sign_cn': SIGN_CN[point_B],
'degree': d9_lon,
},
'distance': distance,
'geometric_index': marriage_count,
'marriage_count': marriage_count,
'interpretation': interpretation,
'parivartana': parivartana,
'warnings': warnings,
}
def _check_parivartana(
lord: str,
lord_sign: int,
d1_houses: Dict,
d1_planet_lons: Dict[str, float]
) -> Dict:
"""
检查 7宫主是否与其落入星座的主星有 Parivartana(行星交换)
返回: {'has_parivartana': bool, 'planet1': str, 'planet2': str, ...}
"""
# 找到 lord 落入星座的主星
SIGN_LORDS = ['Mars','Venus','Mercury','Moon','Sun','Mercury',
'Venus','Mars','Jupiter','Saturn','Saturn','Jupiter']
host = SIGN_LORDS[lord_sign] # lord 落入星座的主星
# 落在自己掌管的星座不是 Parivartana(行星交换),只是 own-sign 状态。
if host == lord:
return {'has_parivartana': False, 'reason': f'{lord} 位于自己掌管的星座,不构成 Parivartana'}
if host not in d1_planet_lons:
return {'has_parivartana': False, 'reason': f'{host} 位置未知'}
host_sign = sign_of(d1_planet_lons[host])
# Parivartana: lord 在 host 的星座里(host 的定义已保证),host 在 lord 掌管的星座里。
# 旧写法比较 host 与 lord 是否同星座,实际判的是合相而非互换(BUG-1184)。
if SIGN_LORDS[host_sign] == lord:
return {
'has_parivartana': True,
'planet1': lord,
'planet1_sign': lord_sign,
'planet2': host,
'planet2_sign': host_sign,
'note': f'{lord}(在{ SIGN_CN[lord_sign]}) 与 {host}(在{ SIGN_CN[host_sign]}) 交换宫位',
}
return {'has_parivartana': False}
def _interpret_marriage_count(count: int, A: int, B: int) -> str:
"""只描述几何量,不推出婚姻或关系的次数、先后与质量(BUG-1183)。"""
lines = [
f"关系几何指数 {count}:D1 第7宫主所在星座数到它在 D9 所在星座的距离(含首尾),"
"是这个方法里的星座距离,不代表婚姻或关系的次数。"
]
if A == B:
lines.append("D1 与 D9 两个参照点落在同一星座。")
if (B - A) % 12 == 6:
lines.append("D1 与 D9 两个参照点落在相对的星座。")
return "\n".join(lines)
def marriage_counting_full_analysis(
d1_house7_lord: str,
d1_planet_lons: Dict[str, float],
d9_planet_lons: Dict[str, float],
d1_houses: Optional[Dict] = None,
d9_houses: Optional[Dict] = None,
d1_moon_nak_idx: Optional[int] = None,
d9_upapada_lord: Optional[str] = None,
) -> Dict:
"""
完整婚姻计数分析(配合 Dasha + D9 验证)
返回包含婚姻计数 + D9 质量评估 + 综合建议
"""
# 基础计数
base = marriage_counting_method(
d1_house7_lord, d1_planet_lons, d9_planet_lons,
d1_houses, d9_houses
)
if 'error' in base:
return base
# D9 质量评估(简化版)
d9_quality = _assess_d9_marriage_quality(d9_planet_lons, d9_houses)
# 综合建议
recommendations = _marriage_recommendations(base, d9_quality)
return {
**base,
'd9_marriage_quality': d9_quality,
'recommendations': recommendations,
'note': '几何指数只是方法内的星座距离,不是婚姻或关系的计数。',
}
def _assess_d9_marriage_quality(
d9_planet_lons: Dict[str, float],
d9_houses: Optional[Dict]
) -> Dict:
"""D9 婚姻质量评估 v7.0(完整版)"""
quality_points = 0
factors = []
SIGN_LORDS_LIST = ['Mars','Venus','Mercury','Moon','Sun','Mercury',
'Venus','Mars','Jupiter','Saturn','Saturn','Jupiter']
def _sign_of(lon):
return int((lon % 360) / 30)
def _dignity(planet, sign_idx):
"""简化星性判断"""
EXALTATION = {'Sun': 0, 'Moon': 1, 'Mars': 9, 'Mercury': 5,
'Jupiter': 3, 'Venus': 11, 'Saturn': 6}
OWN = {'Sun': [4], 'Moon': [3], 'Mars': [0, 7], 'Mercury': [2, 5],
'Jupiter': [8, 11], 'Venus': [1, 6], 'Saturn': [9, 10]}
DEBILITATION = {'Sun': 6, 'Moon': 7, 'Mars': 3, 'Mercury': 11,
'Jupiter': 9, 'Venus': 5, 'Saturn': 0}
if sign_idx == EXALTATION.get(planet, -1):
return 'exalted'
if sign_idx in OWN.get(planet, []):
return 'own'
if sign_idx == DEBILITATION.get(planet, -1):
return 'debilitated'
return 'neutral'
# 检查 D9 Venus 状态(完整版)
venus_d9 = d9_planet_lons.get('Venus')
if venus_d9 is not None:
venus_sign = _sign_of(venus_d9)
venus_dig = _dignity('Venus', venus_sign)
if venus_dig == 'exalted':
quality_points += 3
factors.append(f"D9 Venus擢升在{SIGN_CN[venus_sign]} → 婚姻质量极高")
elif venus_dig == 'own':
quality_points += 2
factors.append(f"D9 Venus入庙在{SIGN_CN[venus_sign]} → 婚姻关系质量高")
elif venus_dig == 'debilitated':
quality_points -= 2
factors.append(f"D9 Venus落陷在{SIGN_CN[venus_sign]} → 婚姻关系有重大挑战")
elif venus_sign in [1, 6]: # Taurus/Libra own
quality_points += 2
factors.append(f"D9 Venus在本宫{SIGN_CN[venus_sign]} → 关系质量好")
else:
quality_points += 0
factors.append(f"D9 Venus在{SIGN_CN[venus_sign]}({venus_dig}) → 关系质量中等")
# 检查 D9 Mars 状态(婚姻中的冲突指标)
mars_d9 = d9_planet_lons.get('Mars')
if mars_d9 is not None:
mars_sign = _sign_of(mars_d9)
mars_dig = _dignity('Mars', mars_sign)
if mars_dig == 'debilitated':
quality_points -= 1
factors.append(f"D9 Mars落陷 → 婚姻中缺乏行动力/保护")
elif mars_dig in ('exalted', 'own'):
quality_points += 1
factors.append(f"D9 Mars强 → 婚姻中有保护力和行动力")
# 检查 D9 Jupiter 状态(婚姻中的智慧和祝福)
jup_d9 = d9_planet_lons.get('Jupiter')
if jup_d9 is not None:
jup_sign = _sign_of(jup_d9)
jup_dig = _dignity('Jupiter', jup_sign)
if jup_dig in ('exalted', 'own'):
quality_points += 2
factors.append(f"D9 Jupiter强在{SIGN_CN[jup_sign]} → 婚姻有智慧和祝福")
elif jup_dig == 'debilitated':
quality_points -= 1
factors.append(f"D9 Jupiter落陷 → 婚姻缺乏指引")
# 检查 D9 7宫/7宫主(如果有宫位数据)
if d9_houses and '7' in d9_houses:
h7_d9 = d9_houses['7']
if isinstance(h7_d9, dict) and 'lord' in h7_d9:
h7_lord_d9 = h7_d9['lord']
factors.append(f"D9 第7宫主:{h7_lord_d9}")
# D9 7宫主的星性
h7l_d9_lon = d9_planet_lons.get(h7_lord_d9)
if h7l_d9_lon is not None:
h7l_sign = _sign_of(h7l_d9_lon)
h7l_dig = _dignity(h7_lord_d9, h7_sign)
if h7l_dig in ('exalted', 'own'):
quality_points += 2
factors.append(f"D9 7宫主{h7_lord_d9}强 → 伴侣支持有力")
elif h7l_dig == 'debilitated':
quality_points -= 1
factors.append(f"D9 7宫主{h7_lord_d9}落陷 → 伴侣关系挑战")
# 检查 D9 Rahu/Ketu 对7宫的影响
rahu_d9 = d9_planet_lons.get('Rahu')
ketu_d9 = d9_planet_lons.get('Ketu')
if d9_houses:
h7_sign = d9_houses.get('7', {})
if isinstance(h7_sign, dict) and 'sign' in h7_sign:
h7_sign_name = h7_sign['sign']
SIGNS_LIST = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo',
'Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces']
if h7_sign_name in SIGNS_LIST:
h7_idx = SIGNS_LIST.index(h7_sign_name)
if rahu_d9 is not None and _sign_of(rahu_d9) == h7_idx:
quality_points -= 1
factors.append("D9 Rahu在7宫 → 关系中的迷惑/非常规因素")
if ketu_d9 is not None and _sign_of(ketu_d9) == h7_idx:
quality_points -= 1
factors.append("D9 Ketu在7宫 → 关系中的分离倾向")
# 综合评级
if quality_points >= 4:
rating = "高(关系质量好,伴侣支持强,婚姻稳定)"
elif quality_points >= 1:
rating = "中上(关系质量尚可,需经营但基础好)"
elif quality_points >= 0:
rating = "中(关系质量一般,需用心经营)"
elif quality_points >= -2:
rating = "中下(关系有挑战,需提前沟通和调整期望)"
else:
rating = "低(关系质量有重大挑战,强烈建议婚前咨询)"
return {
'quality_rating': rating,
'quality_points': quality_points,
'factors': factors,
}
def _marriage_recommendations(base: Dict, d9_quality: Dict) -> List[str]:
"""几何指数与伴侣数量、事件断语分开(BUG-1183);不随指数大小给不同结论。"""
return [
"几何指数需与 D1 第7宫主、它在 D9 的落点以及已记录的尊贵因素一起读,不单独下结论。",
"这个指数不指认第几位伴侣,也不说明关系的长短、好坏或是否专一。",
"涉及时间的判断须另看已算出的 Dasha 与过运,并以本人的实际经历为准。",
]
# 导出函数
def calc_marriage_count(d1_7lord, d1_lons, d9_lons, d1_h=None, d9_h=None):
"""便捷函数:计算婚姻计数"""
return marriage_counting_method(d1_7lord, d1_lons, d9_lons, d1_h, d9_h)
if __name__ == '__main__':
# 测试:假设 D1 7宫主 = Venus,D1 Venus 在 Gemini(2),D9 Venus 在 Virgo(5)
test = marriage_counting_method(
'Venus',
{'Venus': 2*30 + 15}, # Gemini 15°
{'Venus': 5*30 + 10}, # Virgo 10°
)
print(test)