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:
Jesse_Chen
2026-10-02 23:45:06 +08:00
co-authored by Claude Opus 5.5
parent 67aa1b33d7
commit 7fb165ecf6
4 changed files with 92 additions and 24 deletions
+36 -13
View File
@@ -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
View File
@@ -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: