Files
Jyotisha/scripts/goel_short_strength.py
T
jesse-ux 9d3eb23ee4 feat: 完整数据版加入六种星座大运
Sthira、Drig、Shoola、Navamsha、Niryaana Shoola 与 K.N. Rao 的 Chara 从本盘算出。Lagna Kendradi 不上表。未推送。
2026-10-09 00:43:00 +08:00

217 lines
7.0 KiB
Python

#!/usr/bin/env python3
"""Goel / BVB short graha and rashi strength for the report rashi dashas.
Dignity, amsha, and kendradi units follow the short method in
Predicting through Jaimini Astrology, printed pages 100-108.
This is not Shadbala and not a house-strength table from another program.
"""
from __future__ import annotations
from typing import Dict, Mapping
try:
from jaimini import FRIENDSHIP, KARAKA_7, SIGNS, calc_chara_karaka_7
from narayana_dasha import SIGN_LORDS, _rasi_aspects
except ImportError: # pragma: no cover - package import
from scripts.jaimini import FRIENDSHIP, KARAKA_7, SIGNS, calc_chara_karaka_7
from scripts.narayana_dasha import SIGN_LORDS, _rasi_aspects
CLASSICAL_PLANETS = ("Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn")
_EXALT_SIGN = {
"Sun": 0,
"Moon": 1,
"Mars": 9,
"Mercury": 5,
"Jupiter": 3,
"Venus": 11,
"Saturn": 6,
}
_DEBIL_SIGN = {
"Sun": 6,
"Moon": 7,
"Mars": 3,
"Mercury": 11,
"Jupiter": 9,
"Venus": 5,
"Saturn": 0,
}
_OWN_SIGNS = {
"Sun": {4},
"Moon": {3},
"Mars": {0, 7},
"Mercury": {2, 5},
"Jupiter": {8, 11},
"Venus": {1, 6},
"Saturn": {9, 10},
}
# Moolatrikona bands are degree-in-sign, end exclusive.
_MOOLATRIKONA = {
"Sun": ((4, 0.0, 20.0),),
"Moon": ((1, 3.0, 30.0),),
"Mars": ((0, 0.0, 12.0),),
"Mercury": ((5, 15.0, 20.0),),
"Jupiter": ((8, 0.0, 10.0),),
"Venus": ((6, 0.0, 15.0),),
"Saturn": ((10, 0.0, 20.0),),
}
_AMSHA_BY_RANK = {1: 70, 2: 60, 3: 50, 4: 40, 5: 30, 6: 20, 7: 10}
_CHARA_RASHI = {0: 20, 1: 40, 2: 60}
def sign_index(longitude: float) -> int:
return int(float(longitude) % 360.0 // 30.0)
def degree_in_sign(longitude: float) -> float:
return float(longitude) % 30.0
def _in_moolatrikona(planet: str, sign_idx: int, degree: float) -> bool:
for sign, start, end in _MOOLATRIKONA.get(planet, ()):
if sign == sign_idx and start <= degree < end:
return True
return False
def dignity_units(planet: str, sign_idx: int, degree: float) -> int:
"""Short-method dignity. Whole exalt and debil signs, except Mercury in Virgo."""
if planet not in CLASSICAL_PLANETS:
raise KeyError(planet)
if sign_idx == _DEBIL_SIGN[planet]:
return 10
if planet == "Mercury" and sign_idx == 5:
if degree < 15.0:
return 70
if degree < 20.0:
return 60
return 50
if sign_idx == _EXALT_SIGN[planet]:
return 70
if _in_moolatrikona(planet, sign_idx, degree):
return 60
if sign_idx in _OWN_SIGNS[planet]:
return 50
lord = SIGN_LORDS[SIGNS[sign_idx]]
relation = FRIENDSHIP.get(planet, {})
if lord in relation.get("friend", []):
return 40
if lord in relation.get("enemy", []):
return 20
return 30
def kendradi_units(sign_idx: int, ak_sign_idx: int) -> int:
offset = (int(sign_idx) - int(ak_sign_idx)) % 12
if offset % 3 == 0:
return 60
if offset % 3 == 1:
return 40
return 20
def karaka_ranks(planet_lons: Mapping[str, float]) -> Dict[str, int]:
degrees = {
planet: degree_in_sign(planet_lons[planet])
for planet in CLASSICAL_PLANETS
if planet in planet_lons
}
table = calc_chara_karaka_7(degrees).get("karaka_table") or {}
ranks = {}
for rank, name in KARAKA_7.items():
row = table.get(name) or {}
planet = row.get("planet")
if planet:
ranks[str(planet)] = int(rank)
return ranks
def graha_strengths(planet_lons: Mapping[str, float]) -> Dict[str, Dict[str, int]]:
ranks = karaka_ranks(planet_lons)
if "Mars" not in ranks and len(ranks) < 7:
raise ValueError("short graha strength needs the seven classical planets")
ak_planet = next(planet for planet, rank in ranks.items() if rank == 1)
ak_sign = sign_index(planet_lons[ak_planet])
rows = {}
for planet in CLASSICAL_PLANETS:
sign_idx = sign_index(planet_lons[planet])
degree = degree_in_sign(planet_lons[planet])
dignity = dignity_units(planet, sign_idx, degree)
amsha = _AMSHA_BY_RANK[ranks[planet]]
kendradi = kendradi_units(sign_idx, ak_sign)
rows[planet] = {
"sign_index": sign_idx,
"dignity": dignity,
"amsha": amsha,
"kendradi": kendradi,
"total": dignity + amsha + kendradi,
"karaka_rank": ranks[planet],
}
return rows
def _aspect_units(sign_idx: int, planet_lons: Mapping[str, float], lord: str) -> int:
contributors = []
for planet in (lord, "Jupiter", "Mercury"):
if planet not in contributors:
contributors.append(planet)
total = 0
for planet in contributors:
if planet not in planet_lons:
continue
source = sign_index(planet_lons[planet])
if source != sign_idx and not _rasi_aspects(source, sign_idx):
continue
if planet == lord and planet in {"Jupiter", "Mercury"}:
total += 120
else:
total += 60
return total
def rashi_strengths(
planet_lons: Mapping[str, float],
graha: Mapping[str, Mapping[str, int]] | None = None,
) -> Dict[int, Dict[str, int]]:
graha_rows = graha if graha is not None else graha_strengths(planet_lons)
occupants: Dict[int, int] = {index: 0 for index in range(12)}
for planet in CLASSICAL_PLANETS:
occupants[sign_index(planet_lons[planet])] += 1
rows = {}
for sign_idx in range(12):
lord = SIGN_LORDS[SIGNS[sign_idx]]
chara = _CHARA_RASHI[sign_idx % 3]
sthira = 10 * occupants[sign_idx]
aspect = _aspect_units(sign_idx, planet_lons, lord)
lord_graha = int(graha_rows[lord]["total"])
rows[sign_idx] = {
"chara": chara,
"sthira": sthira,
"aspect": aspect,
"lord_graha": lord_graha,
"total": chara + sthira + aspect + lord_graha,
}
return rows
def stronger_sign(left: int, right: int, strengths: Mapping[int, Mapping[str, int]], planet_lons: Mapping[str, float]) -> Dict[str, object]:
"""Pick the stronger sign. Equal totals use the higher chara-karaka lord."""
left_total = int(strengths[left]["total"])
right_total = int(strengths[right]["total"])
if left_total != right_total:
selected = left if left_total > right_total else right
return {"status": "selected", "sign_index": selected, "tie_break": "rashi_total"}
ranks = karaka_ranks(planet_lons)
left_lord = SIGN_LORDS[SIGNS[left]]
right_lord = SIGN_LORDS[SIGNS[right]]
left_rank = ranks.get(left_lord)
right_rank = ranks.get(right_lord)
if left_rank is None or right_rank is None or left_rank == right_rank:
return {
"status": "unresolved",
"reason": "equal_rashi_strength_and_karaka",
"candidates": (left, right),
}
selected = left if left_rank < right_rank else right
return {"status": "selected", "sign_index": selected, "tie_break": "lord_karaka_rank"}