fix(vimsopaka): score sign-owner relationships on a 20-point scale (BUG-1261, upstream f32472b5)

This commit is contained in:
jesse-ux
2026-10-07 16:43:34 +08:00
parent db68e90816
commit ab9e30560a
5 changed files with 109 additions and 70 deletions
+15 -10
View File
@@ -651,8 +651,11 @@ def _build_dignity_context(chart_data):
}
return context
def _get_dignity_level(planet, sign, deg_in_sign=None, planets_data=None):
"""判断行星在某星座的尊严等级 (用于 Vimsopaka 映射), 包含五重敌友和落陷取消"""
def _get_dignity_level(planet, sign, deg_in_sign=None, planets_data=None, skip_debilitation=False):
"""判断行星在某星座的尊严等级, 包含五重敌友;默认也含落陷取消。
skip_debilitation 只给 Varga Viswa:落陷和落陷取消不是星主关系。
"""
if EXALTATION.get(planet) == sign:
return 'EXALTED'
@@ -667,7 +670,7 @@ def _get_dignity_level(planet, sign, deg_in_sign=None, planets_data=None):
if SIGN_LORDS.get(sign) == planet:
return 'OWN_SIGN'
if DEBILITATION.get(planet) == sign:
if not skip_debilitation and DEBILITATION.get(planet) == sign:
if _check_neecha_bhanga(planet, sign, planets_data):
return 'NEECHA_BHANGA'
return 'DEBILITATED'
@@ -706,7 +709,7 @@ def _get_dignity_level(planet, sign, deg_in_sign=None, planets_data=None):
return 'NEUTRAL'
def varga_dignity_level(planet, varga_planets):
def varga_dignity_level(planet, varga_planets, skip_debilitation=False):
"""Dignity of a body inside a divisional chart (Vimsopaka and the chart page alike).
The temporary half of the compound relationship is read from that varga's
@@ -717,7 +720,10 @@ def varga_dignity_level(planet, varga_planets):
if not isinstance(pinfo, dict) or pinfo.get('sign') not in SIGN_LORDS:
return None
deg_in_sign = pinfo.get('degree_in_sign', pinfo.get('degree', 0) % 30)
return _get_dignity_level(planet, pinfo['sign'], deg_in_sign, _build_dignity_context(varga_planets))
return _get_dignity_level(
planet, pinfo['sign'], deg_in_sign, _build_dignity_context(varga_planets),
skip_debilitation=skip_debilitation,
)
def _get_planet_status_label(planet, sign, deg_in_sign=None, planets_data=None):
@@ -15756,7 +15762,6 @@ def cmd_full_reading(args):
'OWN_SIGN': DignityLevel.OWN_SIGN, 'GREAT_FRIEND': DignityLevel.GREAT_FRIEND,
'FRIEND': DignityLevel.FRIEND, 'NEUTRAL': DignityLevel.NEUTRAL,
'ENEMY': DignityLevel.ENEMY, 'GREAT_ENEMY': DignityLevel.GREAT_ENEMY,
'DEBILITATED': DignityLevel.DEBILITATED, 'NEECHA_BHANGA': DignityLevel.NEECHA_BHANGA,
}
# 建立 VargaType → varga_result key 的映射(修复:每个分盘独立计算尊贵)
_vt_div = {VimsVargaType.D1:1, VimsVargaType.D2:2, VimsVargaType.D3:3,
@@ -15785,15 +15790,15 @@ def cmd_full_reading(args):
if div == 1:
dl_key = _get_dignity_level(pn, pinfo['sign'],
pinfo.get('degree_in_sign', pinfo.get('degree', 0) % 30),
planets)
planets, skip_debilitation=True)
else:
dl_key = varga_dignity_level(pn, vdata)
dl_key = varga_dignity_level(pn, vdata, skip_debilitation=True)
planet_vargas_input[pn][vt] = dignity_map.get(dl_key, DignityLevel.NEUTRAL)
elif div == 1:
# D1 直接用本命盘数据
p_sign = SIGNS[int(planet_lons.get(pn, 0) / 30) % 12]
d_in_s = planet_lons.get(pn, 0) % 30
dl_key = _get_dignity_level(pn, p_sign, d_in_s, planets)
dl_key = _get_dignity_level(pn, p_sign, d_in_s, planets, skip_debilitation=True)
planet_vargas_input[pn][vt] = dignity_map.get(dl_key, DignityLevel.NEUTRAL)
else:
planet_vargas_input[pn][vt] = DignityLevel.NEUTRAL
@@ -15801,7 +15806,7 @@ def cmd_full_reading(args):
# fallback: D1 从 planet_lons 计算
p_sign = SIGNS[int(planet_lons.get(pn, 0) / 30) % 12]
d_in_s = planet_lons.get(pn, 0) % 30
dl_key = _get_dignity_level(pn, p_sign, d_in_s, planets)
dl_key = _get_dignity_level(pn, p_sign, d_in_s, planets, skip_debilitation=True)
planet_vargas_input[pn][vt] = dignity_map.get(dl_key, DignityLevel.NEUTRAL)
else:
planet_vargas_input[pn][vt] = DignityLevel.NEUTRAL
+39 -52
View File
@@ -60,7 +60,7 @@ class VimsopakaBalaCalculator:
VargaType.D3: 1.0, # Drekkana
VargaType.D4: 0.5, # Chaturthamsa
VargaType.D7: 0.5, # Saptamsa
VargaType.D9: 3.5, # Navamsa
VargaType.D9: 3.0, # Navamsa;十六分盘权重合计 20
VargaType.D10: 0.5, # Dasamsa
VargaType.D12: 0.5, # Dwadasamsa
VargaType.D16: 2.0, # Shodasamsa
@@ -79,7 +79,7 @@ class VimsopakaBalaCalculator:
VargaType.D2: 1.5,
VargaType.D3: 1.5,
VargaType.D7: 1.5,
VargaType.D9: 3.0,
VargaType.D9: 1.5,
VargaType.D10: 1.5,
VargaType.D12: 1.5,
VargaType.D16: 1.5,
@@ -87,31 +87,24 @@ class VimsopakaBalaCalculator:
VargaType.D60: 5.0
}
# 尊严等级对应的Virupas(力量单位)
# 星主关系对应的 Virupas。落陷和落陷取消不是这项的输入。
DIGNITY_VIRUPAS = {
DignityLevel.EXALTED: 20.0,
DignityLevel.MOOLATRIKONA: 18.0,
DignityLevel.OWN_SIGN: 15.0,
DignityLevel.GREAT_FRIEND: 12.5,
DignityLevel.FRIEND: 10.0,
DignityLevel.NEUTRAL: 7.5,
DignityLevel.ENEMY: 5.0,
DignityLevel.GREAT_ENEMY: 3.5,
DignityLevel.DEBILITATED: 2.0,
DignityLevel.NEECHA_BHANGA: 9.0,
DignityLevel.MOOLATRIKONA: 20.0,
DignityLevel.OWN_SIGN: 20.0,
DignityLevel.GREAT_FRIEND: 18.0,
DignityLevel.FRIEND: 15.0,
DignityLevel.NEUTRAL: 10.0,
DignityLevel.ENEMY: 7.0,
DignityLevel.GREAT_ENEMY: 5.0,
}
# 力量等级分类(基于总分)
# 数值分档。不再借用 Vaiseshikamsa 的分盘计数名称。
STRENGTH_CATEGORIES = {
"Parijatamsa": (18.0, 20.0), # 极强(18-20)
"Uttamamsa": (16.0, 18.0), # 优秀(16-18)
"Gopuramsa": (12.0, 16.0), # 良好(12-16)
"Simhasanamsa": (10.0, 12.0), # 中等偏上(10-12)
"Paravatamsa": (8.0, 10.0), # 中等(8-10)
"Devalokamsa": (6.0, 8.0), # 中等偏下(6-8)
"Brahmalokamsa": (4.0, 6.0), # 较弱(4-6)
"Airavata": (2.0, 4.0), # 弱(2-4)
"Vaiseshikamsa": (0.0, 2.0) # 极弱(0-2)
"High": (15.0, 20.000001),
"Moderate": (10.0, 15.0),
"Limited": (5.0, 10.0),
"Low": (0.0, 5.0),
}
def __init__(self, mode: str = "shodasavarga"):
@@ -156,8 +149,10 @@ class VimsopakaBalaCalculator:
# 计算每个分盘的得分
for varga_type, dignity in vargas.items():
if varga_type in self.weights:
# 获取尊严对应的Virupas
virupas = self.DIGNITY_VIRUPAS.get(dignity, 0.0)
# 只接受星主关系。落陷或落陷取消不能当作尊贵分。
if dignity not in self.DIGNITY_VIRUPAS:
raise ValueError('Varga Viswa requires a sign-owner relationship, not debility/cancellation')
virupas = self.DIGNITY_VIRUPAS[dignity]
# 获取分盘权重
weight = self.weights[varga_type]
@@ -175,9 +170,9 @@ class VimsopakaBalaCalculator:
total_weighted_score += weighted_score
total_weight += weight
# 计算最终得分(归一化到20分制)
# 分母固定为 20,不按已提供分盘的权重再归一。
if total_weight > 0:
final_score = total_weighted_score / total_weight
final_score = total_weighted_score / 20.0
else:
final_score = 0.0
@@ -193,7 +188,12 @@ class VimsopakaBalaCalculator:
"category": category,
"varga_scores": varga_scores,
"interpretation": interpretation,
"mode": self.mode
"mode": self.mode,
"calculation_profile": "bphs_7_21_27_varga_viswa_compound_relationships",
"included_weight": total_weight,
"complete": set(vargas).issuperset(self.weights),
"missing_vargas": [v.value for v in self.weights if v not in vargas],
"category_basis": "Numeric score band; not Vaiseshikamsa varga-count titles",
}
def _get_strength_category(self, score: float) -> str:
@@ -201,34 +201,21 @@ class VimsopakaBalaCalculator:
for category, (min_score, max_score) in self.STRENGTH_CATEGORIES.items():
if min_score <= score < max_score:
return category
return "Vaiseshikamsa" # 默认最低等级
return "Unclassified"
def _generate_interpretation(self, planet: str, score: float, category: str, varga_scores: Dict) -> str:
"""生成解读文本"""
interpretation = f"{planet}的Vimsopaka Bala得分为{score:.2f}/20,属于{category}等级。\n\n"
# 根据等级给出解读
if score >= 16:
interpretation += "这是极强的行星力量,表示该行星在生活中能够充分发挥其积极作用,带来显著的成果和成功。"
elif score >= 12:
interpretation += "这是良好的行星力量,表示该行星能够有效发挥作用,在其主管领域带来正面影响。"
elif score >= 8:
interpretation += "这是中等的行星力量,表示该行星的作用较为平衡,既有积极面也有挑战。"
elif score >= 4:
interpretation += "这是较弱的行星力量,表示该行星在发挥作用时会遇到一些困难和阻碍。"
else:
interpretation += "这是极弱的行星力量,表示该行星难以充分发挥其作用,需要通过补救措施来增强。"
# 找出最强和最弱的分盘
"""陈述加权后的星主关系,不把分数说成吉凶或应验。"""
text = (
f"{planet}的 Vimsopaka Bala 为 {score:.2f}/20,数值分档 {category}。"
"这项只计分盘里的星主关系,不是功能吉凶,也不表示必然结果。"
)
if varga_scores:
sorted_vargas = sorted(varga_scores.items(), key=lambda x: x[1]['weighted_score'], reverse=True)
strongest = sorted_vargas[0]
weakest = sorted_vargas[-1]
interpretation += f"\n\n最强分盘:{strongest[0]}({strongest[1]['dignity']},得分{strongest[1]['weighted_score']})"
interpretation += f"\n最弱分盘:{weakest[0]}({weakest[1]['dignity']},得分{weakest[1]['weighted_score']})"
return interpretation
ranked = sorted(varga_scores.items(), key=lambda item: item[1]['virupas'], reverse=True)
text += f" 关系分最高:{ranked[0][0]}({ranked[0][1]['virupas']:g}/20)。"
text += f" 关系分最低:{ranked[-1][0]}({ranked[-1][1]['virupas']:g}/20)。"
if len(varga_scores) < len(self.weights):
text += " 输入不完整,这里只是已计入分盘的合计,不是完整分数。"
return text
def compare_planets(self, results: Dict[str, Dict]) -> List[Tuple[str, float]]:
"""