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:
Jesse_Chen
2026-10-03 11:40:54 +08:00
co-authored by Claude Opus 5.5
parent df21e591e8
commit 6916bbaecc
5 changed files with 195 additions and 15 deletions
+12 -4
View File
@@ -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宫",