fix(yoga): Kemadruma has one main detector; lunar/solar neighbour yogas count five planets (T2, BUG-1206)
Astrologer ruling 甲7: yoga_expansion.detect_kemadruma is the main detector (2nd/12th from the Moon count only Mars..Saturn, no kendra cancellation, nodes in kendras do not cancel). kemadruma_yoga (ky2 kendra cancellation) and kemadruma_variant (nodes as neighbours) are reference_only; YogaEngine.detect appends the main row. BUG-1187 omission: the algorithmic _detect_solar_lunar_yogas block still counted the Sun, Moon and nodes, so Ketu next to the Moon gave 'Anapha Yoga' beside Kemadruma (Taylor) and Sun/nodes gave Durudhura beside Sunaphaa. Both blocks now count the five true planets. divisional_yoga compared a 'house' key vargas never carry (always 'isolated') and counted the Sun; it now uses sign adjacency and the same five. 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
df21e591e8
commit
6916bbaecc
@@ -236,15 +236,20 @@ def _detect_moon_varga_yogas(varga_planets: Dict) -> List[Dict]:
|
||||
|
||||
moon = varga_planets['Moon']
|
||||
moon_sign = moon.get('sign', '')
|
||||
if moon_sign not in SIGNS:
|
||||
return yogas
|
||||
|
||||
# Kemadruma检查
|
||||
has_neighbor = False
|
||||
for pn in ['Sun', 'Mercury', 'Venus', 'Mars', 'Jupiter', 'Saturn']:
|
||||
if pn in varga_planets:
|
||||
ps = varga_planets[pn]
|
||||
if ps.get('house', 0) in (moon.get('house', 0) - 1, moon.get('house', 0) + 1):
|
||||
has_neighbor = True
|
||||
break
|
||||
# Kemadruma:与 D1 主检测器同一口径(yoga_expansion.detect_kemadruma;占星师
|
||||
# 定稿 2026-10-03 甲7,BUG-1206)——月亮所在星座的前后一宫只数火水木金土,太阳、
|
||||
# 罗计不计,不启用角宫解除。旧写法按分盘里并不存在的 `house` 字段比相邻,
|
||||
# 永远判「无邻星」,且把太阳算作邻星。
|
||||
moon_idx = SIGNS.index(moon_sign)
|
||||
neighbours = {(moon_idx + 1) % 12, (moon_idx - 1) % 12}
|
||||
has_neighbor = any(
|
||||
varga_planets.get(pn, {}).get('sign') in SIGNS
|
||||
and SIGNS.index(varga_planets[pn]['sign']) in neighbours
|
||||
for pn in ('Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn')
|
||||
)
|
||||
if not has_neighbor:
|
||||
yogas.append({
|
||||
'name': 'Kemadruma (分盘)',
|
||||
|
||||
@@ -13,6 +13,10 @@ English reader edition).
|
||||
|
||||
- Neecha Bhanga (BUG-1205, 乙14): `yogas_doshas.calc_nicha_bhanga_raj_yoga`;
|
||||
only the cancellation conditions that hold are listed, never called Raja Yoga.
|
||||
- Kemadruma (BUG-1206, 甲7): `yoga_expansion.detect_kemadruma`; the 2nd / 12th
|
||||
from the Moon count only Mars, Mercury, Jupiter, Venus, Saturn, and there is no
|
||||
kendra cancellation (planets in kendras, including only Rahu / Ketu there, do
|
||||
not cancel it).
|
||||
|
||||
Inputs are D1 rows `{sign, house, lon?, retrograde?, navamsa_sign?}`. A missing
|
||||
`navamsa_sign` is derived from the sidereal longitude (Parashari navamsa: each
|
||||
@@ -25,8 +29,10 @@ from __future__ import annotations
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
try: # scripts/ on sys.path
|
||||
from yoga_expansion import detect_kemadruma
|
||||
from yogas_doshas import calc_nicha_bhanga_raj_yoga
|
||||
except ImportError: # package import (scripts.main_yoga_detectors)
|
||||
from scripts.yoga_expansion import detect_kemadruma
|
||||
from scripts.yogas_doshas import calc_nicha_bhanga_raj_yoga
|
||||
|
||||
SIGNS = ['Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo',
|
||||
@@ -138,6 +144,30 @@ def neecha_bhanga_rows(planets: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
return rows
|
||||
|
||||
|
||||
def kemadruma_rows(planets: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""Kemadruma from `yoga_expansion.detect_kemadruma` (no kendra cancellation)."""
|
||||
result = detect_kemadruma(planets or {})
|
||||
if not result.get('present'):
|
||||
return []
|
||||
moon_sign = (planets.get('Moon') or {}).get('sign')
|
||||
return [{
|
||||
'name': 'Kemadruma Yoga',
|
||||
'name_cn': '空劫瑜伽',
|
||||
'category': 'durbhaga',
|
||||
'planets': ['Moon'],
|
||||
'combination': f'月亮在{_sign_zh(moon_sign)},第2、12宫无火水木金土(太阳与罗计不计;不启用角宫解除)',
|
||||
'combination_en': (f'Moon in {moon_sign}; none of Mars to Saturn in the 2nd or 12th from it '
|
||||
'(the Sun and the nodes are not counted; no kendra cancellation)'),
|
||||
'effects': ['人生艰辛', '需自力更生', '精神挑战'],
|
||||
'effects_en': ['Hard life', 'Must rely on oneself', 'Mental and spiritual challenges'],
|
||||
'strength': '弱',
|
||||
'strength_en': 'weak',
|
||||
'source': 'BPHS',
|
||||
'rule_id': 'main_detector:kemadruma',
|
||||
'detector': 'yoga_expansion.detect_kemadruma',
|
||||
}]
|
||||
|
||||
|
||||
def main_detector_rows(planets: Dict[str, Any], ascendant: Optional[str] = None) -> List[Dict[str, Any]]:
|
||||
"""Every main-detector row for one D1 chart, in a fixed order."""
|
||||
return neecha_bhanga_rows(planets)
|
||||
return kemadruma_rows(planets) + neecha_bhanga_rows(planets)
|
||||
|
||||
+12
-4
@@ -68,6 +68,8 @@ DUAL_SIGNS = ['Gemini', 'Virgo', 'Sagittarius', 'Pisces']
|
||||
ALL_PLANETS = ['Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn', 'Rahu', 'Ketu']
|
||||
PLANET_INDEX = {planet: idx for idx, planet in enumerate(ALL_PLANETS)}
|
||||
RAHU_KETU_OWNED_SIGN_INDEX = {'Rahu': 10, 'Ketu': 7}
|
||||
# Planets that form the solar / lunar neighbour yogas (BUG-1187, BUG-1206).
|
||||
FIVE_TRUE_PLANETS = ('Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn')
|
||||
# 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]:
|
||||
@@ -797,6 +799,12 @@ class YogaEngine:
|
||||
|
||||
来源:从 dangling commit f19369c 恢复的失散代码(原在 jyotish_engine.py cmd_yoga 中)。
|
||||
算法:基于行星相对于太阳/月亮的位置,非规则 JSON 可表达的。
|
||||
|
||||
只计火、水、木、金、土五星(BPHS 第 37 章第 7–10 颂、第 38 章第 1 颂;
|
||||
Raman 300 Combinations 第 2–5 条):太阳、月亮与罗睺、计都不形成这些格局。
|
||||
BUG-1187 把同名 JSON 规则改成只计五星,但漏了这一段算法版,罗计或太阳
|
||||
单独在月亮邻宫时仍出 Anapha / Sunapha / Durudhura,与 Kemadruma 同盘
|
||||
矛盾(BUG-1206)。Kemadruma 本身由 main_yoga_detectors 出。
|
||||
"""
|
||||
results = []
|
||||
p = ctx.planets
|
||||
@@ -818,8 +826,8 @@ class YogaEngine:
|
||||
if sun_house is not None:
|
||||
h2 = ((sun_house - 1 + 1) % 12) + 1 # 太阳第2宫
|
||||
h12 = ((sun_house - 1 + 11) % 12) + 1 # 太阳第12宫
|
||||
p_in_h2 = [n for n in planets_in_house.get(h2, []) if n != "Sun"]
|
||||
p_in_h12 = [n for n in planets_in_house.get(h12, []) if n != "Sun"]
|
||||
p_in_h2 = [n for n in planets_in_house.get(h2, []) if n in FIVE_TRUE_PLANETS]
|
||||
p_in_h12 = [n for n in planets_in_house.get(h12, []) if n in FIVE_TRUE_PLANETS]
|
||||
if p_in_h12 and not p_in_h2:
|
||||
results.append({"name": "Voshi Yoga", "name_cn": "太阳前瑜伽",
|
||||
"combination": f"{'+'.join(p_in_h12)}在太阳第12宫",
|
||||
@@ -849,8 +857,8 @@ class YogaEngine:
|
||||
if moon_house is not None:
|
||||
h2 = ((moon_house - 1 + 1) % 12) + 1 # 月亮第2宫
|
||||
h12 = ((moon_house - 1 + 11) % 12) + 1 # 月亮第12宫
|
||||
p_in_h2 = [n for n in planets_in_house.get(h2, []) if n != "Moon"]
|
||||
p_in_h12 = [n for n in planets_in_house.get(h12, []) if n != "Moon"]
|
||||
p_in_h2 = [n for n in planets_in_house.get(h2, []) if n in FIVE_TRUE_PLANETS]
|
||||
p_in_h12 = [n for n in planets_in_house.get(h12, []) if n in FIVE_TRUE_PLANETS]
|
||||
if p_in_h12 and not p_in_h2:
|
||||
results.append({"name": "Anapha Yoga", "name_cn": "月后瑜伽",
|
||||
"combination": f"{'+'.join(p_in_h12)}在月亮第12宫",
|
||||
|
||||
Reference in New Issue
Block a user