fix: resolve Badhaka house by lagna in curse yogas, drop death wording (upstream 6abfe983 follow-up, BUG-1192)
Preta Yoga hard-coded the 11th house as Badhaka; it now uses the single yoga_engine.badhaka_house() definition (movable 11 / fixed 9 / dual 7). Remaining early-death / lineage-ending phrases removed from curse yoga family effects and the Matru-hani yoga effects list. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
67aa1b33d7
commit
7fb165ecf6
@@ -11444,13 +11444,11 @@
|
||||
},
|
||||
"effects": [
|
||||
"母亲健康不佳",
|
||||
"与母亲缘薄",
|
||||
"母亲早逝"
|
||||
"与母亲缘薄"
|
||||
],
|
||||
"effects_en": [
|
||||
"Mother's health is poor",
|
||||
"Weak bond with mother",
|
||||
"Early loss of mother"
|
||||
"Weak bond with mother"
|
||||
],
|
||||
"strength": "弱",
|
||||
"strength_en": "weak",
|
||||
|
||||
@@ -11,6 +11,10 @@ curse_yoga_detector.py — 凶星合相命名与检测引擎
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from yoga_engine import badhaka_house
|
||||
|
||||
# houses_critical 里的占位符:按上升星座换成 Badhaka 宫(变动 11 / 固定 9 / 双体 7)。
|
||||
BADHAKA_HOUSE = "badhaka"
|
||||
|
||||
# 凶星合相定义
|
||||
CURSE_DEFINITIONS = {
|
||||
@@ -25,26 +29,26 @@ CURSE_DEFINITIONS = {
|
||||
"remedy": "需要上师保护,唱诵Guru Vandana,侍奉上师",
|
||||
"deity": "上师/Guru",
|
||||
"body_parts": ["血液", "骨骼", "神经系统"],
|
||||
"family_effect": "家族血脉可能中断,无子女或子女早夭",
|
||||
"family_effect": "家族责任与传承压力主题;不能据此断定子女、婚育或家人安危",
|
||||
},
|
||||
"preta_yoga_saturn_rahu": {
|
||||
"name": "Preta Yoga",
|
||||
"name_cn": "亡灵瑜伽(土星+罗喉)",
|
||||
"planets": ["Saturn", "Rahu"],
|
||||
"houses_critical": [8, 11, 6], # 8宫=死亡,11宫=Badhaka,6宫=疾病
|
||||
"houses_critical": [8, BADHAKA_HOUSE, 6], # 8宫=突变,Badhaka 宫按上升动/定/变取 11/9/7,6宫=疾病
|
||||
"meaning": "长期压力、健康脆弱性或家族责任主题;不能据此断定寿命、死亡或疾病。",
|
||||
"activation": ["Saturn或Rahu的Dasha", "Badhaka宫被激活", "水瓶座相关事件"],
|
||||
"severity": 5,
|
||||
"remedy": "湿婆能量,唱诵Mahamrityunjaya Mantra,进行祖先祭祀",
|
||||
"deity": "湿婆/Shiva",
|
||||
"body_parts": ["慢性疾病部位", "骨骼", "神经系统退化"],
|
||||
"family_effect": "家族血脉停止,女性不婚或无子女,男性只有女儿",
|
||||
"family_effect": "家族传承与责任主题;不能据此断定婚育或子女",
|
||||
},
|
||||
"preta_yoga_saturn_ketu": {
|
||||
"name": "Preta Yoga",
|
||||
"name_cn": "亡灵瑜伽(土星+计都)",
|
||||
"planets": ["Saturn", "Ketu"],
|
||||
"houses_critical": [8, 11, 6],
|
||||
"houses_critical": [8, BADHAKA_HOUSE, 6],
|
||||
"meaning": "长期压力、健康脆弱性或灵性困扰主题;不能据此断定寿命、死亡或疾病。",
|
||||
"activation": ["Saturn或Ketu的Dasha", "灵性敏感期"],
|
||||
"severity": 5,
|
||||
@@ -57,7 +61,7 @@ CURSE_DEFINITIONS = {
|
||||
"name": "Rakshasa Yoga",
|
||||
"name_cn": "恶魔瑜伽",
|
||||
"planets": ["Mars", "Rahu"],
|
||||
"houses_critical": [6, 8], # 6宫=暴力/疾病,8宫=突变/死亡
|
||||
"houses_critical": [6, 8], # 6宫=冲突/疾病,8宫=突变
|
||||
"meaning": "暴力事件,外部攻击,神经系统受损,可能被粗俗或微妙的恶魔力量攻击。",
|
||||
"activation": ["Mars或Rahu的Dasha", "6宫或8宫被Transit激活"],
|
||||
"severity": 4,
|
||||
@@ -70,7 +74,7 @@ CURSE_DEFINITIONS = {
|
||||
"name": "Pisacha Yoga",
|
||||
"name_cn": "鬼魅瑜伽",
|
||||
"planets": ["Mars", "Ketu"],
|
||||
"houses_critical": [8, 1, 6], # 8宫=死亡,1宫=身体,6宫=疾病
|
||||
"houses_critical": [8, 1, 6], # 8宫=突变,1宫=身体,6宫=疾病
|
||||
"meaning": "与有毒的人接触,被充满愤怒仇恨的鬼魂困扰,可能遭遇事故前感到被推动。",
|
||||
"activation": ["Mars或Ketu的Dasha", "天蝎座相关事件", "8宫被激活"],
|
||||
"severity": 4,
|
||||
@@ -90,7 +94,7 @@ HOUSE_PERSONIFICATION = {
|
||||
5: "子女/创造力/投机",
|
||||
6: "疾病/敌人/服务",
|
||||
7: "配偶/合作/公开对手",
|
||||
8: "死亡/突变/遗产/灵性",
|
||||
8: "突变/遗产/隐秘/灵性",
|
||||
9: "父亲/长途旅行/高等教育",
|
||||
10: "事业/社会地位/母亲",
|
||||
11: "收益/愿望/社交网络",
|
||||
@@ -191,14 +195,17 @@ class CurseYogaDetector:
|
||||
analysis = CurseAnalysis()
|
||||
|
||||
# Step 1: 在D1中检测
|
||||
d1_curses = self._detect_in_chart(planets, "D1")
|
||||
d1_curses = self._detect_in_chart(planets, "D1", asc_sign)
|
||||
analysis.curses_detected.extend(d1_curses)
|
||||
|
||||
# Step 2: 在D9中检测(如果有)
|
||||
context = chart_data.get("context", {})
|
||||
d9_planets = context.get("navamsa_planets", {})
|
||||
if d9_planets:
|
||||
d9_curses = self._detect_in_chart(d9_planets, "D9")
|
||||
d9_asc = context.get("navamsa_ascendant") or context.get("navamsa_lagna") or ""
|
||||
if isinstance(d9_asc, dict):
|
||||
d9_asc = d9_asc.get("sign", "")
|
||||
d9_curses = self._detect_in_chart(d9_planets, "D9", str(d9_asc or ""))
|
||||
for c in d9_curses:
|
||||
c.narrative = f"[Navamsa] {c.narrative}"
|
||||
analysis.curses_detected.extend(d9_curses)
|
||||
@@ -218,7 +225,8 @@ class CurseYogaDetector:
|
||||
|
||||
return analysis
|
||||
|
||||
def _detect_in_chart(self, planets: Dict, chart_name: str) -> List[CurseDetection]:
|
||||
def _detect_in_chart(self, planets: Dict, chart_name: str,
|
||||
ascendant_sign: str = "") -> List[CurseDetection]:
|
||||
"""在单个星盘中检测诅咒Yoga"""
|
||||
detected = []
|
||||
|
||||
@@ -250,7 +258,8 @@ class CurseYogaDetector:
|
||||
def_planets = definition["planets"]
|
||||
if set(pair) == set(def_planets):
|
||||
detection = self._create_detection(
|
||||
curse_id, definition, pair, house, sign, chart_name
|
||||
curse_id, definition, pair, house, sign, chart_name,
|
||||
ascendant_sign,
|
||||
)
|
||||
detected.append(detection)
|
||||
|
||||
@@ -258,13 +267,14 @@ class CurseYogaDetector:
|
||||
|
||||
def _create_detection(self, curse_id: str, definition: Dict,
|
||||
planets: Tuple[str, ...], house: int,
|
||||
sign: str, chart_name: str) -> CurseDetection:
|
||||
sign: str, chart_name: str,
|
||||
ascendant_sign: str = "") -> CurseDetection:
|
||||
"""创建单个检测结果"""
|
||||
severity = definition["severity"]
|
||||
severity_label = "critical" if severity >= 5 else "high" if severity >= 4 else "medium"
|
||||
|
||||
# 如果在危险宫位,提高严重程度
|
||||
if house in definition.get("houses_critical", []):
|
||||
if house in self._critical_houses(definition, ascendant_sign):
|
||||
severity = min(severity + 1, 5)
|
||||
severity_label = "critical"
|
||||
|
||||
@@ -298,6 +308,19 @@ class CurseYogaDetector:
|
||||
narrative=f"[{chart_name}] 在{sign}第{house}宫发现 {definition['name']} ({definition['name_cn']})",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _critical_houses(definition: Dict, ascendant_sign: str) -> List[int]:
|
||||
"""展开 Badhaka 占位符;上升未知时 Badhaka 宫不参与加重。"""
|
||||
houses: List[int] = []
|
||||
for item in definition.get("houses_critical", []):
|
||||
if item == BADHAKA_HOUSE:
|
||||
resolved = badhaka_house(ascendant_sign)
|
||||
if resolved is not None:
|
||||
houses.append(resolved)
|
||||
else:
|
||||
houses.append(item)
|
||||
return houses
|
||||
|
||||
def _get_mantras(self, deity: str) -> List[str]:
|
||||
"""获取对应神祇的咒语"""
|
||||
mantras = {
|
||||
|
||||
+13
-7
@@ -65,6 +65,17 @@ PLANET_INDEX = {planet: idx for idx, planet in enumerate(ALL_PLANETS)}
|
||||
RAHU_KETU_OWNED_SIGN_INDEX = {'Rahu': 10, 'Ketu': 7}
|
||||
# PyJHora const.house_strengths_of_planets, copied to make Yoga rule alignment deterministic.
|
||||
# Strength scale: 5 own/lord, 4 exalted, 3 friend, 2 neutral, 1 enemy, 0 debilitated.
|
||||
def badhaka_house(ascendant_sign: str) -> Optional[int]:
|
||||
"""Badhaka 宫的唯一定义:变动 11、固定 9、双体 7;上升未知返回 None。"""
|
||||
if ascendant_sign in MOVABLE_SIGNS:
|
||||
return 11
|
||||
if ascendant_sign in FIXED_SIGNS:
|
||||
return 9
|
||||
if ascendant_sign in DUAL_SIGNS:
|
||||
return 7
|
||||
return None
|
||||
|
||||
|
||||
HOUSE_STRENGTHS = {
|
||||
'Sun': [4, 1, 2, 2, 5, 2, 0, 3, 3, 1, 1, 3],
|
||||
'Moon': [2, 4, 3, 5, 3, 3, 2, 0, 2, 2, 2, 2],
|
||||
@@ -506,13 +517,8 @@ class YogaContext:
|
||||
return house in [2, 7]
|
||||
|
||||
def is_badhaka(self, house: int) -> bool:
|
||||
"""Badhaka 宫:基本盘7宫,固定盘9宫,变动盘11宫"""
|
||||
asc = self.ascendant
|
||||
if asc in ['Aries', 'Cancer', 'Libra', 'Capricorn']:
|
||||
return house == 11
|
||||
if asc in ['Taurus', 'Leo', 'Scorpio', 'Aquarius']:
|
||||
return house == 9
|
||||
return house == 7
|
||||
"""Badhaka 宫:变动上升 11 宫,固定上升 9 宫,双体上升 7 宫。"""
|
||||
return badhaka_house(self.ascendant) == house
|
||||
|
||||
# --- 相对宫位关系 ---
|
||||
def house_offset(self, from_house: int, to_house: int) -> int:
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / 'scripts'))
|
||||
|
||||
from curse_yoga_detector import CURSE_DEFINITIONS, CurseYogaDetector, detect_curse_yogas # noqa: E402
|
||||
|
||||
EXPECTED_BADHAKA = {
|
||||
'Aries': 11, 'Cancer': 11, 'Libra': 11, 'Capricorn': 11,
|
||||
'Taurus': 9, 'Leo': 9, 'Scorpio': 9, 'Aquarius': 9,
|
||||
'Gemini': 7, 'Virgo': 7, 'Sagittarius': 7, 'Pisces': 7,
|
||||
}
|
||||
DEATH_CLAIMS = re.compile(r'过早死亡|死亡风险|早夭|早逝|夭折|血脉(?:可能)?(?:中断|停止)|死亡之神')
|
||||
|
||||
|
||||
def test_preta_critical_houses_use_lagna_badhaka_not_fixed_eleventh() -> None:
|
||||
definition = CURSE_DEFINITIONS['preta_yoga_saturn_rahu']
|
||||
for ascendant, badhaka in EXPECTED_BADHAKA.items():
|
||||
houses = CurseYogaDetector._critical_houses(definition, ascendant)
|
||||
assert houses == [8, badhaka, 6], ascendant
|
||||
assert CurseYogaDetector._critical_houses(definition, '') == [8, 6]
|
||||
|
||||
|
||||
def test_curse_yoga_outputs_and_definitions_carry_no_death_claims() -> None:
|
||||
for name in ('curse_yoga_detector.py', 'rashi_tulya_navamsa.py'):
|
||||
source = (ROOT / 'scripts' / name).read_text(encoding='utf-8')
|
||||
assert not DEATH_CLAIMS.search(source), name
|
||||
chart = {
|
||||
'ascendant': {'sign': 'Taurus'},
|
||||
'planets': {
|
||||
'Mars': {'sign': 'Scorpio', 'house': 7},
|
||||
'Saturn': {'sign': 'Scorpio', 'house': 7},
|
||||
'Rahu': {'sign': 'Capricorn', 'house': 9},
|
||||
'Ketu': {'sign': 'Cancer', 'house': 3},
|
||||
},
|
||||
}
|
||||
result = detect_curse_yogas(chart)
|
||||
assert result['curses_detected']
|
||||
assert not DEATH_CLAIMS.search(str(result))
|
||||
Reference in New Issue
Block a user